Compare commits
36 Commits
eval-optim
...
eval-cache
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea792bcdc8 | ||
|
|
71650c83c6 | ||
|
|
d51deeac1c | ||
|
|
1f8541258c | ||
|
|
3a9753132e | ||
|
|
2324ee4891 | ||
|
|
28c1f8800b | ||
|
|
af775bdcf9 | ||
|
|
8d2be51d19 | ||
|
|
9102508f33 | ||
|
|
a6aaf81103 | ||
|
|
c7a232e200 | ||
|
|
30d14b772f | ||
|
|
fbaee9b8fb | ||
|
|
753730c410 | ||
|
|
c116e6e837 | ||
|
|
3e261410bc | ||
|
|
ffec547ebc | ||
|
|
512afd8b7a | ||
|
|
69505c84e1 | ||
|
|
af5c323e93 | ||
|
|
9053ac0693 | ||
|
|
b39ab10749 | ||
|
|
8787218c7c | ||
|
|
89951cf7fb | ||
|
|
45a28ed36f | ||
|
|
6ec852e7f0 | ||
|
|
7c718646cb | ||
|
|
8d95e1f299 | ||
|
|
6396416dfa | ||
|
|
44f390ed48 | ||
|
|
4ca1a0b864 | ||
|
|
2021b1d8d1 | ||
|
|
b012852cb2 | ||
|
|
074e0678bd | ||
|
|
891390d76f |
25
.github/workflows/test.yml
vendored
25
.github/workflows/test.yml
vendored
@@ -1,32 +1,27 @@
|
||||
name: "Test"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
|
||||
tests:
|
||||
needs: [check_cachix]
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.5
|
||||
- uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v14.1
|
||||
- uses: cachix/install-nix-action@v13
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/cachix-action@v10
|
||||
if: needs.check_cachix.outputs.secret == 'true'
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- run: nix-build -A checks.$(nix-instantiate --eval -E '(builtins.currentSystem)')
|
||||
|
||||
#- run: nix flake check
|
||||
- run: nix-build -A checks.$(if [[ `uname` = Linux ]]; then echo x86_64-linux; else echo x86_64-darwin; fi)
|
||||
check_cachix:
|
||||
name: Cachix secret present for installer tests
|
||||
runs-on: ubuntu-latest
|
||||
@@ -38,7 +33,6 @@ jobs:
|
||||
env:
|
||||
_CACHIX_SECRETS: ${{ secrets.CACHIX_SIGNING_KEY }}${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
run: echo "::set-output name=secret::${{ env._CACHIX_SECRETS != '' }}"
|
||||
|
||||
installer:
|
||||
needs: [tests, check_cachix]
|
||||
if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true'
|
||||
@@ -46,11 +40,11 @@ jobs:
|
||||
outputs:
|
||||
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.5
|
||||
- uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/install-nix-action@v14.1
|
||||
- uses: cachix/install-nix-action@v13
|
||||
- uses: cachix/cachix-action@v10
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
@@ -58,7 +52,6 @@ jobs:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- id: prepare-installer
|
||||
run: scripts/prepare-installer-for-github-actions
|
||||
|
||||
installer_test:
|
||||
needs: [installer, check_cachix]
|
||||
if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true'
|
||||
@@ -67,9 +60,9 @@ jobs:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.5
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/install-nix-action@v14.1
|
||||
- uses: cachix/install-nix-action@v13
|
||||
with:
|
||||
install_url: '${{needs.installer.outputs.installerURL}}'
|
||||
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -15,7 +15,6 @@ perl/Makefile.config
|
||||
/doc/manual/*.1
|
||||
/doc/manual/*.5
|
||||
/doc/manual/*.8
|
||||
/doc/manual/generated/*
|
||||
/doc/manual/nix.json
|
||||
/doc/manual/conf-file.json
|
||||
/doc/manual/builtins.json
|
||||
@@ -40,7 +39,6 @@ perl/Makefile.config
|
||||
|
||||
# /src/libstore/
|
||||
*.gen.*
|
||||
/src/libstore/tests/libstore-tests
|
||||
|
||||
# /src/libutil/
|
||||
/src/libutil/tests/libutil-tests
|
||||
@@ -58,6 +56,9 @@ perl/Makefile.config
|
||||
|
||||
/src/nix-prefetch-url/nix-prefetch-url
|
||||
|
||||
# /src/nix-daemon/
|
||||
/src/nix-daemon/nix-daemon
|
||||
|
||||
/src/nix-collect-garbage/nix-collect-garbage
|
||||
|
||||
# /src/nix-channel/
|
||||
@@ -75,6 +76,7 @@ perl/Makefile.config
|
||||
# /tests/
|
||||
/tests/test-tmp
|
||||
/tests/common.sh
|
||||
/tests/dummy
|
||||
/tests/result*
|
||||
/tests/restricted-innocent
|
||||
/tests/shell
|
||||
|
||||
4
Makefile
4
Makefile
@@ -4,7 +4,6 @@ makefiles = \
|
||||
src/libutil/local.mk \
|
||||
src/libutil/tests/local.mk \
|
||||
src/libstore/local.mk \
|
||||
src/libstore/tests/local.mk \
|
||||
src/libfetchers/local.mk \
|
||||
src/libmain/local.mk \
|
||||
src/libexpr/local.mk \
|
||||
@@ -13,7 +12,6 @@ makefiles = \
|
||||
src/resolve-system-dependencies/local.mk \
|
||||
scripts/local.mk \
|
||||
misc/bash/local.mk \
|
||||
misc/fish/local.mk \
|
||||
misc/zsh/local.mk \
|
||||
misc/systemd/local.mk \
|
||||
misc/launchd/local.mk \
|
||||
@@ -34,4 +32,4 @@ endif
|
||||
|
||||
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++17
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
HOST_OS = @host_os@
|
||||
AR = @AR@
|
||||
BDW_GC_LIBS = @BDW_GC_LIBS@
|
||||
BOOST_LDFLAGS = @BOOST_LDFLAGS@
|
||||
|
||||
53
benchmark.sh
Executable file
53
benchmark.sh
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -euo pipefail
|
||||
set -x
|
||||
|
||||
callNix () {
|
||||
nix \
|
||||
--experimental-features "nix-command flakes" \
|
||||
--store /tmp/nix \
|
||||
"$@"
|
||||
}
|
||||
|
||||
callBuild () {
|
||||
callNix \
|
||||
eval --impure --file "$THINGTOBENCH" drvPath \
|
||||
"$@"
|
||||
}
|
||||
getCompletions () {
|
||||
NIX_GET_COMPLETIONS=6 callNix build "github:NixOS/nixpkgs?rev=ad0d20345219790533ebe06571f82ed6b034db31#firef" "$@"
|
||||
}
|
||||
runSearch () {
|
||||
callNix search "github:NixOS/nixpkgs?rev=ad0d20345219790533ebe06571f82ed6b034db31" firefox "$@"
|
||||
}
|
||||
|
||||
noCache () {
|
||||
"$@" --option eval-cache false
|
||||
}
|
||||
coldCache () {
|
||||
if [[ -e ~/.cache/nix/eval-cache-v2 || -e ~/.cache/nix/eval-cache-v3 ]]; then
|
||||
echo "Error: The cache should be clean"
|
||||
exit 1
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
run_all () {
|
||||
|
||||
mkdir -p "$out"
|
||||
|
||||
NIX_SHOW_STATS=1 NIX_SHOW_STATS_PATH=$out/eval-stats.json bash $0 noCache callBuild
|
||||
|
||||
hyperfine \
|
||||
--warmup 2 \
|
||||
--export-csv "$out/result.csv" \
|
||||
--export-json "$out/result.json" \
|
||||
--export-markdown "$out/result.md" \
|
||||
--style basic \
|
||||
--prepare '' "bash $0 noCache callBuild" \
|
||||
--prepare 'rm -rf ~/.cache/nix/' "bash $0 coldCache callBuild" \
|
||||
--prepare '' "bash $0 callBuild"
|
||||
}
|
||||
|
||||
"$@"
|
||||
@@ -1,45 +0,0 @@
|
||||
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
|
||||
index 4b2c429..1fb4c52 100644
|
||||
--- a/pthread_stop_world.c
|
||||
+++ b/pthread_stop_world.c
|
||||
@@ -673,6 +673,8 @@ GC_INNER void GC_push_all_stacks(void)
|
||||
struct GC_traced_stack_sect_s *traced_stack_sect;
|
||||
pthread_t self = pthread_self();
|
||||
word total_size = 0;
|
||||
+ size_t stack_limit;
|
||||
+ pthread_attr_t pattr;
|
||||
|
||||
if (!EXPECT(GC_thr_initialized, TRUE))
|
||||
GC_thr_init();
|
||||
@@ -722,6 +724,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
|
||||
59
configure.ac
59
configure.ac
@@ -32,6 +32,14 @@ AC_ARG_WITH(system, AS_HELP_STRING([--with-system=SYSTEM],[Platform identifier (
|
||||
system="$machine_name-`echo $host_os | "$SED" -e's/@<:@0-9.@:>@*$//g'`";;
|
||||
esac])
|
||||
|
||||
sys_name=$(uname -s | tr 'A-Z ' 'a-z_')
|
||||
|
||||
case $sys_name in
|
||||
cygwin*)
|
||||
sys_name=cygwin
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_MSG_RESULT($system)
|
||||
AC_SUBST(system)
|
||||
AC_DEFINE_UNQUOTED(SYSTEM, ["$system"], [platform identifier ('cpu-os')])
|
||||
@@ -55,12 +63,10 @@ AC_SYS_LARGEFILE
|
||||
|
||||
# Solaris-specific stuff.
|
||||
AC_STRUCT_DIRENT_D_TYPE
|
||||
case "$host_os" in
|
||||
solaris*)
|
||||
if test "$sys_name" = sunos; then
|
||||
# Solaris requires -lsocket -lnsl for network functions
|
||||
LDFLAGS="-lsocket -lnsl $LDFLAGS"
|
||||
;;
|
||||
esac
|
||||
LIBS="-lsocket -lnsl $LIBS"
|
||||
fi
|
||||
|
||||
|
||||
# Check for pubsetbuf.
|
||||
@@ -144,7 +150,7 @@ int main() {
|
||||
}]])], GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC=no, GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC=yes)
|
||||
AC_MSG_RESULT($GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC)
|
||||
if test "x$GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC" = xyes; then
|
||||
LDFLAGS="-latomic $LDFLAGS"
|
||||
LIBS="-latomic $LIBS"
|
||||
fi
|
||||
|
||||
PKG_PROG_PKG_CONFIG
|
||||
@@ -204,31 +210,28 @@ AC_SUBST(HAVE_LIBCPUID, [$have_libcpuid])
|
||||
|
||||
|
||||
# Look for libseccomp, required for Linux sandboxing.
|
||||
case "$host_os" in
|
||||
linux*)
|
||||
AC_ARG_ENABLE([seccomp-sandboxing],
|
||||
AS_HELP_STRING([--disable-seccomp-sandboxing],[Don't build support for seccomp sandboxing (only recommended if your arch doesn't support libseccomp yet!)
|
||||
]))
|
||||
if test "x$enable_seccomp_sandboxing" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp],
|
||||
[CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"])
|
||||
have_seccomp=1
|
||||
AC_DEFINE([HAVE_SECCOMP], [1], [Whether seccomp is available and should be used for sandboxing.])
|
||||
else
|
||||
have_seccomp=
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if test "$sys_name" = linux; then
|
||||
AC_ARG_ENABLE([seccomp-sandboxing],
|
||||
AS_HELP_STRING([--disable-seccomp-sandboxing],[Don't build support for seccomp sandboxing (only recommended if your arch doesn't support libseccomp yet!)
|
||||
]))
|
||||
if test "x$enable_seccomp_sandboxing" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp],
|
||||
[CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"])
|
||||
have_seccomp=1
|
||||
AC_DEFINE([HAVE_SECCOMP], [1], [Whether seccomp is available and should be used for sandboxing.])
|
||||
else
|
||||
have_seccomp=
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
have_seccomp=
|
||||
fi
|
||||
AC_SUBST(HAVE_SECCOMP, [$have_seccomp])
|
||||
|
||||
|
||||
# Look for aws-cpp-sdk-s3.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([aws/s3/S3Client.h],
|
||||
[AC_DEFINE([ENABLE_S3], [1], [Whether to enable S3 support via aws-sdk-cpp.]) enable_s3=1],
|
||||
[AC_DEFINE([ENABLE_S3], [1], [Whether to enable S3 support via aws-sdk-cpp.]) enable_s3=1],
|
||||
[AC_DEFINE([ENABLE_S3], [0], [Whether to enable S3 support via aws-sdk-cpp.]) enable_s3=])
|
||||
AC_SUBST(ENABLE_S3, [$enable_s3])
|
||||
AC_LANG_POP(C++)
|
||||
@@ -260,8 +263,6 @@ AC_ARG_ENABLE(doc-gen, AS_HELP_STRING([--disable-doc-gen],[disable documentation
|
||||
doc_generate=$enableval, doc_generate=yes)
|
||||
AC_SUBST(doc_generate)
|
||||
|
||||
# Look for lowdown library.
|
||||
PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.8.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
|
||||
|
||||
# Setuid installations.
|
||||
AC_CHECK_FUNCS([setresuid setreuid lchown])
|
||||
@@ -273,11 +274,9 @@ AC_CHECK_FUNCS([strsignal posix_fallocate sysconf])
|
||||
|
||||
# This is needed if bzip2 is a static library, and the Nix libraries
|
||||
# are dynamic.
|
||||
case "${host_os}" in
|
||||
darwin*)
|
||||
if test "$(uname)" = "Darwin"; then
|
||||
LDFLAGS="-all_load $LDFLAGS"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
|
||||
AC_ARG_WITH(sandbox-shell, AS_HELP_STRING([--with-sandbox-shell=PATH],[path of a statically-linked shell to use as /bin/sh in sandboxes]),
|
||||
|
||||
@@ -6,11 +6,9 @@ builtins:
|
||||
concatStrings (map
|
||||
(name:
|
||||
let builtin = builtins.${name}; in
|
||||
"<dt><code>${name} "
|
||||
+ concatStringsSep " " (map (s: "<var>${s}</var>") builtin.args)
|
||||
+ "</code></dt>"
|
||||
+ "<dd>\n\n"
|
||||
+ builtin.doc
|
||||
+ "\n\n</dd>"
|
||||
" - `builtins.${name}` " + concatStringsSep " " (map (s: "*${s}*") builtin.args)
|
||||
+ " \n\n"
|
||||
+ concatStrings (map (s: " ${s}\n") (splitLines builtin.doc)) + "\n\n"
|
||||
)
|
||||
(attrNames builtins))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{ command, renderLinks ? false }:
|
||||
command:
|
||||
|
||||
with builtins;
|
||||
with import ./utils.nix;
|
||||
@@ -20,11 +20,7 @@ let
|
||||
categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues def.commands)));
|
||||
listCommands = cmds:
|
||||
concatStrings (map (name:
|
||||
"* "
|
||||
+ (if renderLinks
|
||||
then "[`${command} ${name}`](./${appendName filename name}.md)"
|
||||
else "`${command} ${name}`")
|
||||
+ " - ${cmds.${name}.description}\n")
|
||||
"* [`${command} ${name}`](./${appendName filename name}.md) - ${cmds.${name}.description}\n")
|
||||
(attrNames cmds));
|
||||
in
|
||||
"where *subcommand* is one of the following:\n\n"
|
||||
@@ -93,7 +89,7 @@ let
|
||||
in
|
||||
|
||||
let
|
||||
manpages = processCommand { filename = "nix"; command = "nix"; def = builtins.fromJSON command; };
|
||||
manpages = processCommand { filename = "nix"; command = "nix"; def = command; };
|
||||
summary = concatStrings (map (manpage: " - [${manpage.command}](command-ref/new-cli/${manpage.name})\n") manpages);
|
||||
in
|
||||
(listToAttrs manpages) // { "SUMMARY.md" = summary; }
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
ifeq ($(doc_generate),yes)
|
||||
|
||||
MANUAL_SRCS := $(call rwildcard, $(d)/src, *.md)
|
||||
|
||||
# Generate man pages.
|
||||
man-pages := $(foreach n, \
|
||||
nix-env.1 nix-build.1 nix-shell.1 nix-store.1 nix-instantiate.1 \
|
||||
@@ -44,7 +46,7 @@ $(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
|
||||
@rm -rf $@
|
||||
$(trace-gen) $(nix-eval) --write-to $@ --expr 'import doc/manual/generate-manpage.nix { command = builtins.readFile $<; renderLinks = true; }'
|
||||
$(trace-gen) $(nix-eval) --write-to $@ --expr 'import doc/manual/generate-manpage.nix (builtins.fromJSON (builtins.readFile $<))'
|
||||
|
||||
$(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
|
||||
@@ -62,7 +64,6 @@ $(d)/conf-file.json: $(bindir)/nix
|
||||
$(d)/src/expressions/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/expressions/builtins-prefix.md $(bindir)/nix
|
||||
@cat doc/manual/src/expressions/builtins-prefix.md > $@.tmp
|
||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp
|
||||
@cat doc/manual/src/expressions/builtins-suffix.md >> $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/builtins.json: $(bindir)/nix
|
||||
@@ -73,28 +74,17 @@ $(d)/builtins.json: $(bindir)/nix
|
||||
install: $(docdir)/manual/index.html
|
||||
|
||||
# Generate 'nix' manpages.
|
||||
install: $(mandir)/man1/nix3-manpages
|
||||
man: doc/manual/generated/man1/nix3-manpages
|
||||
all: doc/manual/generated/man1/nix3-manpages
|
||||
|
||||
$(mandir)/man1/nix3-manpages: doc/manual/generated/man1/nix3-manpages
|
||||
@mkdir -p $(DESTDIR)$$(dirname $@)
|
||||
$(trace-install) install -m 0644 $$(dirname $<)/* $(DESTDIR)$$(dirname $@)
|
||||
|
||||
doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli
|
||||
@mkdir -p $(DESTDIR)$$(dirname $@)
|
||||
install: $(d)/src/command-ref/new-cli
|
||||
$(trace-gen) for i in doc/manual/src/command-ref/new-cli/*.md; do \
|
||||
name=$$(basename $$i .md); \
|
||||
tmpFile=$$(mktemp); \
|
||||
if [[ $$name = SUMMARY ]]; then continue; fi; \
|
||||
printf "Title: %s\n\n" "$$name" > $$tmpFile; \
|
||||
cat $$i >> $$tmpFile; \
|
||||
lowdown -sT man -M section=1 $$tmpFile -o $(DESTDIR)$$(dirname $@)/$$name.1; \
|
||||
rm $$tmpFile; \
|
||||
printf "Title: %s\n\n" "$$name" > $$i.tmp; \
|
||||
cat $$i >> $$i.tmp; \
|
||||
lowdown -sT man -M section=1 $$i.tmp -o $(mandir)/man1/$$name.1; \
|
||||
done
|
||||
@touch $@
|
||||
|
||||
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/command-ref/conf-file.md $(d)/src/expressions/builtins.md $(call rwildcard, $(d)/src, *.md)
|
||||
$(trace-gen) RUST_LOG=warn mdbook build doc/manual -d $(DESTDIR)$(docdir)/manual
|
||||
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/command-ref/conf-file.md $(d)/src/expressions/builtins.md
|
||||
$(trace-gen) RUST_LOG=warn mdbook build doc/manual -d $(docdir)/manual
|
||||
@cp doc/manual/highlight.pack.js $(docdir)/manual/highlight.js
|
||||
|
||||
endif
|
||||
|
||||
@@ -70,8 +70,6 @@
|
||||
- [Hacking](contributing/hacking.md)
|
||||
- [CLI guideline](contributing/cli-guideline.md)
|
||||
- [Release Notes](release-notes/release-notes.md)
|
||||
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
||||
- [Release 2.4 (2021-11-01)](release-notes/rl-2.4.md)
|
||||
- [Release 2.3 (2019-09-04)](release-notes/rl-2.3.md)
|
||||
- [Release 2.2 (2019-01-11)](release-notes/rl-2.2.md)
|
||||
- [Release 2.1 (2018-09-02)](release-notes/rl-2.1.md)
|
||||
|
||||
@@ -17,7 +17,7 @@ By default Nix reads settings from the following places:
|
||||
|
||||
Otherwise it will look for `nix/nix.conf` files in `XDG_CONFIG_DIRS`
|
||||
and `XDG_CONFIG_HOME`. If these are unset, it will look in
|
||||
`$HOME/.config/nix/nix.conf`.
|
||||
`$HOME/.config/nix.conf`.
|
||||
|
||||
- If `NIX_CONFIG` is set, its contents is treated as the contents of
|
||||
a configuration file.
|
||||
|
||||
@@ -10,39 +10,35 @@ Most Nix commands interpret the following environment variables:
|
||||
A colon-separated list of directories used to look up Nix
|
||||
expressions enclosed in angle brackets (i.e., `<path>`). For
|
||||
instance, the value
|
||||
|
||||
|
||||
/home/eelco/Dev:/etc/nixos
|
||||
|
||||
|
||||
will cause Nix to look for paths relative to `/home/eelco/Dev` and
|
||||
`/etc/nixos`, in this order. It is also possible to match paths
|
||||
against a prefix. For example, the value
|
||||
|
||||
|
||||
nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos
|
||||
|
||||
|
||||
will cause Nix to search for `<nixpkgs/path>` in
|
||||
`/home/eelco/Dev/nixpkgs-branch/path` and `/etc/nixos/nixpkgs/path`.
|
||||
|
||||
|
||||
If a path in the Nix search path starts with `http://` or
|
||||
`https://`, it is interpreted as the URL of a tarball that will be
|
||||
downloaded and unpacked to a temporary location. The tarball must
|
||||
consist of a single top-level directory. For example, setting
|
||||
`NIX_PATH` to
|
||||
|
||||
nixpkgs=https://github.com/NixOS/nixpkgs/archive/master.tar.gz
|
||||
|
||||
tells Nix to download and use the current contents of the
|
||||
`master` branch in the `nixpkgs` repository.
|
||||
|
||||
The URLs of the tarballs from the official nixos.org channels (see
|
||||
[the manual for `nix-channel`](nix-channel.md)) can be abbreviated
|
||||
as `channel:<channel-name>`. For instance, the following two
|
||||
values of `NIX_PATH` are equivalent:
|
||||
|
||||
nixpkgs=channel:nixos-21.05
|
||||
nixpkgs=https://nixos.org/channels/nixos-21.05/nixexprs.tar.xz
|
||||
|
||||
The Nix search path can also be extended using the `-I` option to
|
||||
many Nix commands, which takes precedence over `NIX_PATH`.
|
||||
|
||||
nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-15.09.tar.gz
|
||||
|
||||
tells Nix to download the latest revision in the Nixpkgs/NixOS 15.09
|
||||
channel.
|
||||
|
||||
A following shorthand can be used to refer to the official channels:
|
||||
|
||||
nixpkgs=channel:nixos-15.09
|
||||
|
||||
The search path can be extended using the `-I` option, which takes
|
||||
precedence over `NIX_PATH`.
|
||||
|
||||
- `NIX_IGNORE_SYMLINK_STORE`\
|
||||
Normally, the Nix store directory (typically `/nix/store`) is not
|
||||
@@ -54,7 +50,7 @@ Most Nix commands interpret the following environment variables:
|
||||
builds are deployed to machines where `/nix/store` resolves
|
||||
differently. If you are sure that you’re not going to do that, you
|
||||
can set `NIX_IGNORE_SYMLINK_STORE` to `1`.
|
||||
|
||||
|
||||
Note that if you’re symlinking the Nix store so that you can put it
|
||||
on another file system than the root file system, on Linux you’re
|
||||
better off using `bind` mount points, e.g.,
|
||||
@@ -63,7 +59,7 @@ Most Nix commands interpret the following environment variables:
|
||||
$ mkdir /nix
|
||||
$ mount -o bind /mnt/otherdisk/nix /nix
|
||||
```
|
||||
|
||||
|
||||
Consult the mount 8 manual page for details.
|
||||
|
||||
- `NIX_STORE_DIR`\
|
||||
|
||||
@@ -401,7 +401,7 @@ of a derivation `x` by looking at their respective `name` attributes.
|
||||
The names (e.g., `gcc-3.3.1` are split into two parts: the package name
|
||||
(`gcc`), and the version (`3.3.1`). The version part starts after the
|
||||
first dash not followed by a letter. `x` is considered an upgrade of `y`
|
||||
if their package names match, and the version of `y` is higher than that
|
||||
if their package names match, and the version of `y` is higher that that
|
||||
of `x`.
|
||||
|
||||
The versions are compared by splitting them into contiguous components
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
[`--command` *cmd*]
|
||||
[`--run` *cmd*]
|
||||
[`--exclude` *regexp*]
|
||||
[`--pure`]
|
||||
[`--keep` *name*]
|
||||
[--pure]
|
||||
[--keep *name*]
|
||||
{{`--packages` | `-p`} {*packages* | *expressions*} … | [*path*]}
|
||||
|
||||
# Description
|
||||
@@ -110,19 +110,13 @@ shell in which to build it:
|
||||
|
||||
```console
|
||||
$ nix-shell '<nixpkgs>' -A pan
|
||||
[nix-shell]$ eval ${unpackPhase:-unpackPhase}
|
||||
[nix-shell]$ unpackPhase
|
||||
[nix-shell]$ cd pan-*
|
||||
[nix-shell]$ eval ${configurePhase:-configurePhase}
|
||||
[nix-shell]$ eval ${buildPhase:-buildPhase}
|
||||
[nix-shell]$ configurePhase
|
||||
[nix-shell]$ buildPhase
|
||||
[nix-shell]$ ./pan/gui/pan
|
||||
```
|
||||
|
||||
The reason we use form `eval ${configurePhase:-configurePhase}` here is because
|
||||
those packages that override these phases do so by exporting the overridden
|
||||
values in the environment variable of the same name.
|
||||
Here bash is being told to either evaluate the contents of 'configurePhase',
|
||||
if it exists as a variable, otherwise evaluate the configurePhase function.
|
||||
|
||||
To clear the environment first, and do some additional automatic
|
||||
initialisation of the interactive shell:
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ Special exit codes:
|
||||
|
||||
- `104`\
|
||||
Not deterministic, the build succeeded in check mode but the
|
||||
resulting output is not binary reproducible.
|
||||
resulting output is not binary reproducable.
|
||||
|
||||
With the `--keep-going` flag it's possible for multiple failures to
|
||||
occur, in this case the 1xx status codes are or combined using binary
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Goals
|
||||
|
||||
Purpose of this document is to provide a clear direction to **help design
|
||||
delightful command line** experience. This document contains guidelines to
|
||||
delightful command line** experience. This document contain guidelines to
|
||||
follow to ensure a consistent and approachable user experience.
|
||||
|
||||
## Overview
|
||||
@@ -115,7 +115,7 @@ The rules are:
|
||||
|
||||
- Help is shown by using `--help` or `help` command (eg `nix` `--``help` or
|
||||
`nix help`).
|
||||
- For non-COMMANDs (eg. `nix` `--``help` and `nix store` `--``help`) we **show
|
||||
- For non-COMMANDs (eg. `nix` `--``help` and `nix store` `--``help`) we **show
|
||||
a summary** of most common use cases. Summary is presented on the STDOUT
|
||||
without any use of PAGER.
|
||||
- For COMMANDs (eg. `nix init` `--``help` or `nix help init`) we display the
|
||||
@@ -230,8 +230,8 @@ Now **Learn** part of the output is where you educate users. You should only
|
||||
show it when you know that a build will take some time and not annoy users of
|
||||
the builds that take only few seconds.
|
||||
|
||||
Every feature like this should go through an intensive review and testing to
|
||||
collect as much feedback as possible and to fine tune every little detail. If
|
||||
Every feature like this should go though a intensive review and testing to
|
||||
collect as much a feedback as possible and to fine tune every little detail. If
|
||||
done right this can be an awesome features beginners and advance users will
|
||||
love, but if not done perfectly it will annoy users and leave bad impression.
|
||||
|
||||
@@ -272,11 +272,11 @@ not know which `ARGUMENTS` and `OPTIONS` are required or which values are
|
||||
possible for those options.
|
||||
|
||||
In cases, the user might not provide the input or they provide wrong input,
|
||||
rather than show the error, prompt a user with an option to find and select
|
||||
rather then show the error, prompt a user with an option to find and select
|
||||
correct input (see examples).
|
||||
|
||||
Prompting is of course not required when TTY is not attached to STDIN. This
|
||||
would mean that scripts won't need to handle prompt, but rather handle errors.
|
||||
would mean that scripts wont need to handle prompt, but rather handle errors.
|
||||
|
||||
A place to use prompt and provide user with interactive select
|
||||
|
||||
@@ -300,7 +300,7 @@ going to happen.
|
||||
```shell
|
||||
$ nix build --option substitutors https://cache.example.org
|
||||
------------------------------------------------------------------------
|
||||
Warning! A security related question needs to be answered.
|
||||
Warning! A security related question need to be answered.
|
||||
------------------------------------------------------------------------
|
||||
The following substitutors will be used to in `my-project`:
|
||||
- https://cache.example.org
|
||||
@@ -311,14 +311,14 @@ $ nix build --option substitutors https://cache.example.org
|
||||
|
||||
# Output
|
||||
|
||||
Terminal output can be quite limiting in many ways. Which should force us to
|
||||
Terminal output can be quite limiting in many ways. Which should forces us to
|
||||
think about the experience even more. As with every design the output is a
|
||||
compromise between being terse and being verbose, between showing help to
|
||||
beginners and annoying advance users. For this it is important that we know
|
||||
what are the priorities.
|
||||
|
||||
Nix command line should be first and foremost written with beginners in mind.
|
||||
But users won't stay beginners for long and what was once useful might quickly
|
||||
But users wont stay beginners for long and what was once useful might quickly
|
||||
become annoying. There is no golden rule that we can give in this guideline
|
||||
that would make it easier how to draw a line and find best compromise.
|
||||
|
||||
@@ -508,7 +508,7 @@ can, with a few key strokes, be changed into and advance introspection tool.
|
||||
|
||||
### Progress
|
||||
|
||||
For longer running commands we should provide and overview the progress.
|
||||
For longer running commands we should provide and overview of the progress.
|
||||
This is shown best in `nix build` example:
|
||||
|
||||
```shell
|
||||
@@ -553,7 +553,7 @@ going to happen.
|
||||
```shell
|
||||
$ nix build --option substitutors https://cache.example.org
|
||||
------------------------------------------------------------------------
|
||||
Warning! A security related question needs to be answered.
|
||||
Warning! A security related question need to be answered.
|
||||
------------------------------------------------------------------------
|
||||
The following substitutors will be used to in `my-project`:
|
||||
- https://cache.example.org
|
||||
|
||||
@@ -237,7 +237,7 @@ Derivations can declare some infrequently used optional attributes.
|
||||
- `preferLocalBuild`\
|
||||
If this attribute is set to `true` and [distributed building is
|
||||
enabled](../advanced-topics/distributed-builds.md), then, if
|
||||
possible, the derivation will be built locally instead of forwarded
|
||||
possible, the derivaton will be built locally instead of forwarded
|
||||
to a remote machine. This is appropriate for trivial builders
|
||||
where the cost of doing a download or remote build would exceed
|
||||
the cost of building locally.
|
||||
|
||||
@@ -9,8 +9,7 @@ scope. Instead, you can access them through the `builtins` built-in
|
||||
value, which is a set that contains all built-in functions and values.
|
||||
For instance, `derivation` is also available as `builtins.derivation`.
|
||||
|
||||
<dl>
|
||||
<dt><code>derivation <var>attrs</var></code>;
|
||||
<code>builtins.derivation <var>attrs</var></code></dt>
|
||||
<dd><p><var>derivation</var> in described in
|
||||
<a href="derivations.md">its own section</a>.</p></dd>
|
||||
- `derivation` *attrs*; `builtins.derivation` *attrs*\
|
||||
|
||||
`derivation` is described in [its own section](derivations.md).
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
</dl>
|
||||
@@ -26,7 +26,7 @@ elements (referenced from the figure by number):
|
||||
called with three arguments: `stdenv`, `fetchurl`, and `perl`. They
|
||||
are needed to build Hello, but we don't know how to build them here;
|
||||
that's why they are function arguments. `stdenv` is a package that
|
||||
is used by almost all Nix Packages; it provides a
|
||||
is used by almost all Nix Packages packages; it provides a
|
||||
“standard” environment consisting of the things you would expect
|
||||
in a basic Unix environment: a C/C++ compiler (GCC, to be precise),
|
||||
the Bash shell, fundamental Unix tools such as `cp`, `grep`, `tar`,
|
||||
|
||||
@@ -64,7 +64,7 @@ Nix has the following basic data types:
|
||||
the start of each line. To be precise, it strips from each line a
|
||||
number of spaces equal to the minimal indentation of the string as a
|
||||
whole (disregarding the indentation of empty lines). For instance,
|
||||
the first and second line are indented two spaces, while the third
|
||||
the first and second line are indented two space, while the third
|
||||
line is indented four spaces. Thus, two spaces are stripped from
|
||||
each line, so the resulting string is
|
||||
|
||||
@@ -139,13 +139,6 @@ Nix has the following basic data types:
|
||||
environment variable `NIX_PATH` will be searched for the given file
|
||||
or directory name.
|
||||
|
||||
Antiquotation is supported in any paths except those in angle brackets.
|
||||
`./${foo}-${bar}.nix` is a more convenient way of writing
|
||||
`./. + "/" + foo + "-" + bar + ".nix"` or `./. + "/${foo}-${bar}.nix"`. At
|
||||
least one slash must appear *before* any antiquotations for this to be
|
||||
recognized as a path. `a.${foo}/b.${bar}` is a syntactically valid division
|
||||
operation. `./a.${foo}/b.${bar}` is a path.
|
||||
|
||||
- *Booleans* with values `true` and `false`.
|
||||
|
||||
- The null value, denoted as `null`.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Building Nix from Source
|
||||
|
||||
After cloning Nix's Git repository, issue the following commands:
|
||||
After unpacking or checking out the Nix sources, issue the following
|
||||
commands:
|
||||
|
||||
```console
|
||||
$ ./bootstrap.sh
|
||||
$ ./configure options...
|
||||
$ make
|
||||
$ make install
|
||||
@@ -11,6 +11,13 @@ $ make install
|
||||
|
||||
Nix requires GNU Make so you may need to invoke `gmake` instead.
|
||||
|
||||
When building from the Git repository, these should be preceded by the
|
||||
command:
|
||||
|
||||
```console
|
||||
$ ./bootstrap.sh
|
||||
```
|
||||
|
||||
The installation path can be specified by passing the `--prefix=prefix`
|
||||
to `configure`. The default installation directory is `/usr/local`. You
|
||||
can change this to any location you like. You must have write permission
|
||||
|
||||
@@ -40,7 +40,7 @@ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
|
||||
> **Note**
|
||||
>
|
||||
> You must not add the export and then do the install, as the Nix
|
||||
> installer will detect the presence of Nix configuration, and abort.
|
||||
> installer will detect the presense of Nix configuration, and abort.
|
||||
|
||||
## `NIX_SSL_CERT_FILE` with macOS and the Nix daemon
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Installing Nix from Source
|
||||
|
||||
If no binary package is available or if you want to hack on Nix, you
|
||||
can build Nix from its Git repository.
|
||||
If no binary package is available, you can download and compile a source
|
||||
distribution.
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
# Obtaining the Source
|
||||
# Obtaining a Source Distribution
|
||||
|
||||
The most recent sources of Nix can be obtained from its [Git
|
||||
repository](https://github.com/NixOS/nix). For example, the following
|
||||
command will check out the latest revision into a directory called
|
||||
`nix`:
|
||||
The source tarball of the most recent stable release can be downloaded
|
||||
from the [Nix homepage](http://nixos.org/nix/download.html). You can
|
||||
also grab the [most recent development
|
||||
release](http://hydra.nixos.org/job/nix/master/release/latest-finished#tabs-constituents).
|
||||
|
||||
Alternatively, the most recent sources of Nix can be obtained from its
|
||||
[Git repository](https://github.com/NixOS/nix). For example, the
|
||||
following command will check out the latest revision into a directory
|
||||
called `nix`:
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/NixOS/nix
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
- GNU Autoconf (<https://www.gnu.org/software/autoconf/>) and the
|
||||
autoconf-archive macro collection
|
||||
(<https://www.gnu.org/software/autoconf-archive/>). These are
|
||||
needed to run the bootstrap script.
|
||||
(<https://www.gnu.org/software/autoconf-archive/>). These are only
|
||||
needed to run the bootstrap script, and are not necessary if your
|
||||
source distribution came with a pre-built `./configure` script.
|
||||
|
||||
- GNU Make.
|
||||
|
||||
@@ -25,6 +26,15 @@
|
||||
available for download from the official repository
|
||||
<https://github.com/google/brotli>.
|
||||
|
||||
- The bzip2 compressor program and the `libbz2` library. Thus you must
|
||||
have bzip2 installed, including development headers and libraries.
|
||||
If your distribution does not provide these, you can obtain bzip2
|
||||
from
|
||||
<https://sourceware.org/bzip2/>.
|
||||
|
||||
- `liblzma`, which is provided by XZ Utils. If your distribution does
|
||||
not provide this, you can get it from <https://tukaani.org/xz/>.
|
||||
|
||||
- cURL and its library. If your distribution does not provide it, you
|
||||
can get it from <https://curl.haxx.se/>.
|
||||
|
||||
@@ -51,7 +61,8 @@
|
||||
you need version 2.5.35, which is available on
|
||||
[SourceForge](http://lex.sourceforge.net/). Slightly older versions
|
||||
may also work, but ancient versions like the ubiquitous 2.5.4a
|
||||
won't.
|
||||
won't. Note that these are only required if you modify the parser or
|
||||
when you are building from the Git repository.
|
||||
|
||||
- The `libseccomp` is used to provide syscall filtering on Linux. This
|
||||
is an optional dependency and can be disabled passing a
|
||||
|
||||
@@ -127,7 +127,7 @@ $ nix-env --install firefox
|
||||
|
||||
_could_ cause quite a bit of build activity, as not only Firefox but
|
||||
also all its dependencies (all the way up to the C library and the
|
||||
compiler) would have to be built, at least if they are not already in the
|
||||
compiler) would have to built, at least if they are not already in the
|
||||
Nix store. This is a _source deployment model_. For most users,
|
||||
building from source is not very pleasant as it takes far too long.
|
||||
However, Nix can automatically skip building from source and instead
|
||||
|
||||
@@ -1,539 +1,8 @@
|
||||
# Release 2.4 (2021-11-01)
|
||||
|
||||
This is the first release in more than two years and is the result of
|
||||
more than 2800 commits from 195 contributors since release 2.3.
|
||||
|
||||
## Highlights
|
||||
|
||||
* Nix's **error messages** have been improved a lot. For instance,
|
||||
evaluation errors now point out the location of the error:
|
||||
|
||||
```
|
||||
$ nix build
|
||||
error: undefined variable 'bzip3'
|
||||
|
||||
at /nix/store/449lv242z0zsgwv95a8124xi11sp419f-source/flake.nix:88:13:
|
||||
|
||||
87| [ curl
|
||||
88| bzip3 xz brotli editline
|
||||
| ^
|
||||
89| openssl sqlite
|
||||
```
|
||||
|
||||
* The **`nix` command** has seen a lot of work and is now almost at
|
||||
feature parity with the old command-line interface (the `nix-*`
|
||||
commands). It aims to be [more modern, consistent and pleasant to
|
||||
use](../contributing/cli-guideline.md) than the old CLI. It is still
|
||||
marked as experimental but its interface should not change much
|
||||
anymore in future releases.
|
||||
|
||||
* **Flakes** are a new format to package Nix-based projects in a more
|
||||
discoverable, composable, consistent and reproducible way. A flake
|
||||
is just a repository or tarball containing a file named `flake.nix`
|
||||
that specifies dependencies on other flakes and returns any Nix
|
||||
assets such as packages, Nixpkgs overlays, NixOS modules or CI
|
||||
tests. The new `nix` CLI is primarily based around flakes; for
|
||||
example, a command like `nix run nixpkgs#hello` runs the `hello`
|
||||
application from the `nixpkgs` flake.
|
||||
|
||||
Flakes are currently marked as experimental. For an introduction,
|
||||
see [this blog
|
||||
post](https://www.tweag.io/blog/2020-05-25-flakes/). For detailed
|
||||
information about flake syntax and semantics, see the [`nix flake`
|
||||
manual page](../command-ref/new-cli/nix3-flake.md).
|
||||
|
||||
* Nix's store can now be **content-addressed**, meaning that the hash
|
||||
component of a store path is the hash of the path's
|
||||
contents. Previously Nix could only build **input-addressed** store
|
||||
paths, where the hash is computed from the derivation dependency
|
||||
graph. Content-addressing allows deduplication, early cutoff in
|
||||
build systems, and unprivileged closure copying. This is still [an
|
||||
experimental
|
||||
feature](https://discourse.nixos.org/t/content-addressed-nix-call-for-testers/12881).
|
||||
|
||||
* The Nix manual has been converted into Markdown, making it easier to
|
||||
contribute. In addition, every `nix` subcommand now has a manual
|
||||
page, documenting every option.
|
||||
|
||||
* A new setting that allows **experimental features** to be enabled
|
||||
selectively. This allows us to merge unstable features into Nix more
|
||||
quickly and do more frequent releases.
|
||||
|
||||
## Other features
|
||||
|
||||
* There are many new `nix` subcommands:
|
||||
|
||||
- `nix develop` is intended to replace `nix-shell`. It has a number
|
||||
of new features:
|
||||
|
||||
* It automatically sets the output environment variables (such as
|
||||
`$out`) to writable locations (such as `./outputs/out`).
|
||||
|
||||
* It can store the environment in a profile. This is useful for
|
||||
offline work.
|
||||
|
||||
* It can run specific phases directly. For instance, `nix develop
|
||||
--build` runs `buildPhase`.
|
||||
|
||||
- It allows dependencies in the Nix store to be "redirected" to
|
||||
arbitrary directories using the `--redirect` flag. This is
|
||||
useful if you want to hack on a package *and* some of its
|
||||
dependencies at the same time.
|
||||
|
||||
- `nix print-dev-env` prints the environment variables and bash
|
||||
functions defined by a derivation. This is useful for users of
|
||||
other shells than bash (especially with `--json`).
|
||||
|
||||
- `nix shell` was previously named `nix run` and is intended to
|
||||
replace `nix-shell -p`, but without the `stdenv` overhead. It
|
||||
simply starts a shell where some packages have been added to
|
||||
`$PATH`.
|
||||
|
||||
- `nix run` (not to be confused with the old subcommand that has
|
||||
been renamed to `nix shell`) runs an "app", a flake output that
|
||||
specifies a command to run, or an eponymous program from a
|
||||
package. For example, `nix run nixpkgs#hello` runs the `hello`
|
||||
program from the `hello` package in `nixpkgs`.
|
||||
|
||||
- `nix flake` is the container for flake-related operations, such as
|
||||
creating a new flake, querying the contents of a flake or updating
|
||||
flake lock files.
|
||||
|
||||
- `nix registry` allows you to query and update the flake registry,
|
||||
which maps identifiers such as `nixpkgs` to concrete flake URLs.
|
||||
|
||||
- `nix profile` is intended to replace `nix-env`. Its main advantage
|
||||
is that it keeps track of the provenance of installed packages
|
||||
(e.g. exactly which flake version a package came from). It also
|
||||
has some helpful subcommands:
|
||||
|
||||
* `nix profile history` shows what packages were added, upgraded
|
||||
or removed between each version of a profile.
|
||||
|
||||
* `nix profile diff-closures` shows the changes between the
|
||||
closures of each version of a profile. This allows you to
|
||||
discover the addition or removal of dependencies or size
|
||||
changes.
|
||||
|
||||
**Warning**: after a profile has been updated using `nix profile`,
|
||||
it is no longer usable with `nix-env`.
|
||||
|
||||
- `nix store diff-closures` shows the differences between the
|
||||
closures of two store paths in terms of the versions and sizes of
|
||||
dependencies in the closures.
|
||||
|
||||
- `nix store make-content-addressable` rewrites an arbitrary closure
|
||||
to make it content-addressed. Such paths can be copied into other
|
||||
stores without requiring signatures.
|
||||
|
||||
- `nix bundle` uses the [`nix-bundle`
|
||||
program](https://github.com/matthewbauer/nix-bundle) to convert a
|
||||
closure into a self-extracting executable.
|
||||
|
||||
- Various other replacements for the old CLI, e.g. `nix store gc`,
|
||||
`nix store delete`, `nix store repair`, `nix nar dump-path`, `nix
|
||||
store prefetch-file`, `nix store prefetch-tarball`, `nix key` and
|
||||
`nix daemon`.
|
||||
|
||||
* Nix now has an **evaluation cache** for flake outputs. For example,
|
||||
a second invocation of the command `nix run nixpkgs#firefox` will
|
||||
not need to evaluate the `firefox` attribute because it's already in
|
||||
the evaluation cache. This is made possible by the hermetic
|
||||
evaluation model of flakes.
|
||||
|
||||
* The new `--offline` flag disables substituters and causes all
|
||||
locally cached tarballs and repositories to be considered
|
||||
up-to-date.
|
||||
|
||||
* The new `--refresh` flag causes all locally cached tarballs and
|
||||
repositories to be considered out-of-date.
|
||||
|
||||
* Many `nix` subcommands now have a `--json` option to produce
|
||||
machine-readable output.
|
||||
|
||||
* `nix repl` has a new `:doc` command to show documentation about
|
||||
builtin functions (e.g. `:doc builtins.map`).
|
||||
|
||||
* Binary cache stores now have an option `index-debug-info` to create
|
||||
an index of DWARF debuginfo files for use by
|
||||
[`dwarffs`](https://github.com/edolstra/dwarffs).
|
||||
|
||||
* To support flakes, Nix now has an extensible mechanism for fetching
|
||||
source trees. Currently it has the following backends:
|
||||
|
||||
* Git repositories
|
||||
|
||||
* Mercurial repositories
|
||||
|
||||
* GitHub and GitLab repositories (an optimisation for faster
|
||||
fetching than Git)
|
||||
|
||||
* Tarballs
|
||||
|
||||
* Arbitrary directories
|
||||
|
||||
The fetcher infrastructure is exposed via flake input specifications
|
||||
and via the `fetchTree` built-in.
|
||||
|
||||
* **Languages changes**: the only new language feature is that you can
|
||||
now have antiquotations in paths, e.g. `./${foo}` instead of `./. +
|
||||
foo`.
|
||||
|
||||
* **New built-in functions**:
|
||||
|
||||
- `builtins.fetchTree` allows fetching a source tree using any
|
||||
backends supported by the fetcher infrastructure. It subsumes the
|
||||
functionality of existing built-ins like `fetchGit`,
|
||||
`fetchMercurial` and `fetchTarball`.
|
||||
|
||||
- `builtins.getFlake` fetches a flake and returns its output
|
||||
attributes. This function should not be used inside flakes! Use
|
||||
flake inputs instead.
|
||||
|
||||
- `builtins.floor` and `builtins.ceil` round a floating-point number
|
||||
down and up, respectively.
|
||||
|
||||
* Experimental support for recursive Nix. This means that Nix
|
||||
derivations can now call Nix to build other derivations. This is not
|
||||
in a stable state yet and not well
|
||||
[documented](https://github.com/NixOS/nix/commit/c4d7c76b641d82b2696fef73ce0ac160043c18da).
|
||||
|
||||
* The new experimental feature `no-url-literals` disables URL
|
||||
literals. This helps to implement [RFC
|
||||
45](https://github.com/NixOS/rfcs/pull/45).
|
||||
|
||||
* Nix now uses `libarchive` to decompress and unpack tarballs and zip
|
||||
files, so `tar` is no longer required.
|
||||
|
||||
* The priority of substituters can now be overridden using the
|
||||
`priority` substituter setting (e.g. `--substituters
|
||||
'http://cache.nixos.org?priority=100 daemon?priority=10'`).
|
||||
|
||||
* `nix edit` now supports non-derivation attributes, e.g. `nix edit
|
||||
.#nixosConfigurations.bla`.
|
||||
|
||||
* The `nix` command now provides command line completion for `bash`,
|
||||
`zsh` and `fish`. Since the support for getting completions is built
|
||||
into `nix`, it's easy to add support for other shells.
|
||||
|
||||
* The new `--log-format` flag selects what Nix's output looks like. It
|
||||
defaults to a terse progress indicator. There is a new
|
||||
`internal-json` output format for use by other programs.
|
||||
|
||||
* `nix eval` has a new `--apply` flag that applies a function to the
|
||||
evaluation result.
|
||||
|
||||
* `nix eval` has a new `--write-to` flag that allows it to write a
|
||||
nested attribute set of string leaves to a corresponding directory
|
||||
tree.
|
||||
|
||||
* Memory improvements: many operations that add paths to the store or
|
||||
copy paths between stores now run in constant memory.
|
||||
|
||||
* Many `nix` commands now support the flag `--derivation` to operate
|
||||
on a `.drv` file itself instead of its outputs.
|
||||
|
||||
* There is a new store called `dummy://` that does not support
|
||||
building or adding paths. This is useful if you want to use the Nix
|
||||
evaluator but don't have a Nix store.
|
||||
|
||||
* The `ssh-ng://` store now allows substituting paths on the remote,
|
||||
as `ssh://` already did.
|
||||
|
||||
* When auto-calling a function with an ellipsis, all arguments are now
|
||||
passed.
|
||||
|
||||
* New `nix-shell` features:
|
||||
|
||||
- It preserves the `PS1` environment variable if
|
||||
`NIX_SHELL_PRESERVE_PROMPT` is set.
|
||||
|
||||
- With `-p`, it passes any `--arg`s as Nixpkgs arguments.
|
||||
|
||||
- Support for structured attributes.
|
||||
|
||||
* `nix-prefetch-url` has a new `--executable` flag.
|
||||
|
||||
* On `x86_64` systems, [`x86_64` microarchitecture
|
||||
levels](https://lwn.net/Articles/844831/) are mapped to additional
|
||||
system types (e.g. `x86_64-v1-linux`).
|
||||
|
||||
* The new `--eval-store` flag allows you to use a different store for
|
||||
evaluation than for building or storing the build result. This is
|
||||
primarily useful when you want to query whether something exists in
|
||||
a read-only store, such as a binary cache:
|
||||
|
||||
```
|
||||
# nix path-info --json --store https://cache.nixos.org \
|
||||
--eval-store auto nixpkgs#hello
|
||||
```
|
||||
|
||||
(Here `auto` indicates the local store.)
|
||||
|
||||
* The Nix daemon has a new low-latency mechanism for copying
|
||||
closures. This is useful when building on remote stores such as
|
||||
`ssh-ng://`.
|
||||
|
||||
* Plugins can now register `nix` subcommands.
|
||||
|
||||
## Incompatible changes
|
||||
|
||||
* The `nix` command is now marked as an experimental feature. This
|
||||
means that you need to add
|
||||
|
||||
```
|
||||
experimental-features = nix-command
|
||||
```
|
||||
|
||||
to your `nix.conf` if you want to use it, or pass
|
||||
`--extra-experimental-features nix-command` on the command line.
|
||||
|
||||
* The `nix` command no longer has a syntax for referring to packages
|
||||
in a channel. This means that the following no longer works:
|
||||
|
||||
```console
|
||||
nix build nixpkgs.hello # Nix 2.3
|
||||
```
|
||||
|
||||
Instead, you can either use the `#` syntax to select a package from
|
||||
a flake, e.g.
|
||||
|
||||
```console
|
||||
nix build nixpkgs#hello
|
||||
```
|
||||
|
||||
Or, if you want to use the `nixpkgs` channel in the `NIX_PATH`
|
||||
environment variable:
|
||||
|
||||
```console
|
||||
nix build -f '<nixpkgs>' hello
|
||||
```
|
||||
|
||||
* The old `nix run` has been renamed to `nix shell`, while there is a
|
||||
new `nix run` that runs a default command. So instead of
|
||||
|
||||
```console
|
||||
nix run nixpkgs.hello -c hello # Nix 2.3
|
||||
```
|
||||
|
||||
you should use
|
||||
|
||||
```console
|
||||
nix shell nixpkgs#hello -c hello
|
||||
```
|
||||
|
||||
or just
|
||||
|
||||
```console
|
||||
nix run nixpkgs#hello
|
||||
```
|
||||
|
||||
if the command you want to run has the same name as the package.
|
||||
|
||||
* It is now an error to modify the `plugin-files` setting via a
|
||||
command-line flag that appears after the first non-flag argument to
|
||||
any command, including a subcommand to `nix`. For example,
|
||||
`nix-instantiate default.nix --plugin-files ""` must now become
|
||||
`nix-instantiate --plugin-files "" default.nix`.
|
||||
|
||||
* We no longer release source tarballs. If you want to build from
|
||||
source, please build from the tags in the Git repository.
|
||||
|
||||
## Contributors
|
||||
|
||||
This release has contributions from
|
||||
Adam Höse,
|
||||
Albert Safin,
|
||||
Alex Kovar,
|
||||
Alex Zero,
|
||||
Alexander Bantyev,
|
||||
Alexandre Esteves,
|
||||
Alyssa Ross,
|
||||
Anatole Lucet,
|
||||
Anders Kaseorg,
|
||||
Andreas Rammhold,
|
||||
Antoine Eiche,
|
||||
Antoine Martin,
|
||||
Arnout Engelen,
|
||||
Arthur Gautier,
|
||||
aszlig,
|
||||
Ben Burdette,
|
||||
Benjamin Hipple,
|
||||
Bernardo Meurer,
|
||||
Björn Gohla,
|
||||
Bjørn Forsman,
|
||||
Bob van der Linden,
|
||||
Brian Leung,
|
||||
Brian McKenna,
|
||||
Brian Wignall,
|
||||
Bruce Toll,
|
||||
Bryan Richter,
|
||||
Calle Rosenquist,
|
||||
Calvin Loncaric,
|
||||
Carlo Nucera,
|
||||
Carlos D'Agostino,
|
||||
Chaz Schlarp,
|
||||
Christian Höppner,
|
||||
Christian Kampka,
|
||||
Chua Hou,
|
||||
Chuck,
|
||||
Cole Helbling,
|
||||
Daiderd Jordan,
|
||||
Dan Callahan,
|
||||
Dani,
|
||||
Daniel Fitzpatrick,
|
||||
Danila Fedorin,
|
||||
Daniël de Kok,
|
||||
Danny Bautista,
|
||||
DavHau,
|
||||
David McFarland,
|
||||
Dima,
|
||||
Domen Kožar,
|
||||
Dominik Schrempf,
|
||||
Dominique Martinet,
|
||||
dramforever,
|
||||
Dustin DeWeese,
|
||||
edef,
|
||||
Eelco Dolstra,
|
||||
Emilio Karakey,
|
||||
Emily,
|
||||
Eric Culp,
|
||||
Ersin Akinci,
|
||||
Fabian Möller,
|
||||
Farid Zakaria,
|
||||
Federico Pellegrin,
|
||||
Finn Behrens,
|
||||
Florian Franzen,
|
||||
Félix Baylac-Jacqué,
|
||||
Gabriel Gonzalez,
|
||||
Geoff Reedy,
|
||||
Georges Dubus,
|
||||
Graham Christensen,
|
||||
Greg Hale,
|
||||
Greg Price,
|
||||
Gregor Kleen,
|
||||
Gregory Hale,
|
||||
Griffin Smith,
|
||||
Guillaume Bouchard,
|
||||
Harald van Dijk,
|
||||
illustris,
|
||||
Ivan Zvonimir Horvat,
|
||||
Jade,
|
||||
Jake Waksbaum,
|
||||
jakobrs,
|
||||
James Ottaway,
|
||||
Jan Tojnar,
|
||||
Janne Heß,
|
||||
Jaroslavas Pocepko,
|
||||
Jarrett Keifer,
|
||||
Jeremy Schlatter,
|
||||
Joachim Breitner,
|
||||
Joe Hermaszewski,
|
||||
Joe Pea,
|
||||
John Ericson,
|
||||
Jonathan Ringer,
|
||||
Josef Kemetmüller,
|
||||
Joseph Lucas,
|
||||
Jude Taylor,
|
||||
Julian Stecklina,
|
||||
Julien Tanguy,
|
||||
Jörg Thalheim,
|
||||
Kai Wohlfahrt,
|
||||
keke,
|
||||
Keshav Kini,
|
||||
Kevin Quick,
|
||||
Kevin Stock,
|
||||
Kjetil Orbekk,
|
||||
Krzysztof Gogolewski,
|
||||
kvtb,
|
||||
Lars Mühmel,
|
||||
Leonhard Markert,
|
||||
Lily Ballard,
|
||||
Linus Heckemann,
|
||||
Lorenzo Manacorda,
|
||||
Lucas Desgouilles,
|
||||
Lucas Franceschino,
|
||||
Lucas Hoffmann,
|
||||
Luke Granger-Brown,
|
||||
Madeline Haraj,
|
||||
Marwan Aljubeh,
|
||||
Mat Marini,
|
||||
Mateusz Piotrowski,
|
||||
Matthew Bauer,
|
||||
Matthew Kenigsberg,
|
||||
Mauricio Scheffer,
|
||||
Maximilian Bosch,
|
||||
Michael Adler,
|
||||
Michael Bishop,
|
||||
Michael Fellinger,
|
||||
Michael Forney,
|
||||
Michael Reilly,
|
||||
mlatus,
|
||||
Mykola Orliuk,
|
||||
Nathan van Doorn,
|
||||
Naïm Favier,
|
||||
ng0,
|
||||
Nick Van den Broeck,
|
||||
Nicolas Stig124 Formichella,
|
||||
Niels Egberts,
|
||||
Niklas Hambüchen,
|
||||
Nikola Knezevic,
|
||||
oxalica,
|
||||
p01arst0rm,
|
||||
Pamplemousse,
|
||||
Patrick Hilhorst,
|
||||
Paul Opiyo,
|
||||
Pavol Rusnak,
|
||||
Peter Kolloch,
|
||||
Philipp Bartsch,
|
||||
Philipp Middendorf,
|
||||
Piotr Szubiakowski,
|
||||
Profpatsch,
|
||||
Puck Meerburg,
|
||||
Ricardo M. Correia,
|
||||
Rickard Nilsson,
|
||||
Robert Hensing,
|
||||
Robin Gloster,
|
||||
Rodrigo,
|
||||
Rok Garbas,
|
||||
Ronnie Ebrin,
|
||||
Rovanion Luckey,
|
||||
Ryan Burns,
|
||||
Ryan Mulligan,
|
||||
Ryne Everett,
|
||||
Sam Doshi,
|
||||
Sam Lidder,
|
||||
Samir Talwar,
|
||||
Samuel Dionne-Riel,
|
||||
Sebastian Ullrich,
|
||||
Sergei Trofimovich,
|
||||
Sevan Janiyan,
|
||||
Shao Cheng,
|
||||
Shea Levy,
|
||||
Silvan Mosberger,
|
||||
Stefan Frijters,
|
||||
Stefan Jaax,
|
||||
sternenseemann,
|
||||
Steven Shaw,
|
||||
Stéphan Kochen,
|
||||
SuperSandro2000,
|
||||
Suraj Barkale,
|
||||
Taeer Bar-Yam,
|
||||
Thomas Churchman,
|
||||
Théophane Hufschmitt,
|
||||
Timothy DeHerrera,
|
||||
Timothy Klim,
|
||||
Tobias Möst,
|
||||
Tobias Pflug,
|
||||
Tom Bereknyei,
|
||||
Travis A. Everett,
|
||||
Ujjwal Jain,
|
||||
Vladimír Čunát,
|
||||
Wil Taylor,
|
||||
Will Dietz,
|
||||
Yaroslav Bolyukin,
|
||||
Yestin L. Harrison,
|
||||
YI,
|
||||
Yorick van Pelt,
|
||||
Yuriy Taraday and
|
||||
zimbatm.
|
||||
# Release 2.4 (202X-XX-XX)
|
||||
|
||||
- It is now an error to modify the `plugin-files` setting via a
|
||||
command-line flag that appears after the first non-flag argument
|
||||
to any command, including a subcommand to `nix`. For example,
|
||||
`nix-instantiate default.nix --plugin-files ""` must now become
|
||||
`nix-instantiate --plugin-files "" default.nix`.
|
||||
- Plugins that add new `nix` subcommands are now actually respected.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Release 2.5 (2021-XX-XX)
|
||||
|
||||
* Binary cache stores now have a setting `compression-level`.
|
||||
|
||||
* `nix develop` now has a flag `--unpack` to run `unpackPhase`.
|
||||
13
flake.lock
generated
13
flake.lock
generated
@@ -3,26 +3,27 @@
|
||||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1633514407,
|
||||
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
|
||||
"lastModified": 1617481909,
|
||||
"narHash": "sha256-SqnfOFuLuVRRNeVJr1yeEPJue/qWoCp5N6o5Kr///p4=",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
|
||||
"rev": "148f9b2f586c41b7e36e73009db43ea68c7a1a4d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "kristapsdz",
|
||||
"ref": "VERSION_0_8_4",
|
||||
"repo": "lowdown",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1632864508,
|
||||
"narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=",
|
||||
"lastModified": 1622593737,
|
||||
"narHash": "sha256-9loxFJg85AbzJrSkU4pE/divZ1+zOxDy2FSjlrufCB8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "82891b5e2c2359d7e58d08849e4c89511ab94234",
|
||||
"rev": "bb8a5e54845012ed1375ffd5f317d2fdf434b20e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
319
flake.nix
319
flake.nix
@@ -2,7 +2,7 @@
|
||||
description = "The purely functional package manager";
|
||||
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-21.05-small";
|
||||
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
|
||||
inputs.lowdown-src = { url = "github:kristapsdz/lowdown/VERSION_0_8_4"; flake = false; };
|
||||
|
||||
outputs = { self, nixpkgs, lowdown-src }:
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
linuxSystems = linux64BitSystems ++ [ "i686-linux" ];
|
||||
systems = linuxSystems ++ [ "x86_64-darwin" "aarch64-darwin" ];
|
||||
|
||||
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
||||
|
||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
||||
|
||||
# Memoize nixpkgs for different platforms for efficiency.
|
||||
@@ -61,7 +59,6 @@
|
||||
|
||||
configureFlags =
|
||||
lib.optionals stdenv.isLinux [
|
||||
"--with-boost=${boost}/lib"
|
||||
"--with-sandbox-shell=${sh}/bin/busybox"
|
||||
"LDFLAGS=-fuse-ld=gold"
|
||||
];
|
||||
@@ -71,7 +68,7 @@
|
||||
[
|
||||
buildPackages.bison
|
||||
buildPackages.flex
|
||||
(lib.getBin buildPackages.lowdown-nix)
|
||||
(lib.getBin buildPackages.lowdown)
|
||||
buildPackages.mdbook
|
||||
buildPackages.autoconf-archive
|
||||
buildPackages.autoreconfHook
|
||||
@@ -79,10 +76,10 @@
|
||||
|
||||
# Tests
|
||||
buildPackages.git
|
||||
buildPackages.mercurial # FIXME: remove? only needed for tests
|
||||
buildPackages.mercurial
|
||||
buildPackages.jq
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
|
||||
++ lib.optionals stdenv.isLinux [(pkgs.util-linuxMinimal or pkgs.utillinuxMinimal)];
|
||||
|
||||
buildDeps =
|
||||
[ curl
|
||||
@@ -90,12 +87,13 @@
|
||||
openssl sqlite
|
||||
libarchive
|
||||
boost
|
||||
lowdown-nix
|
||||
nlohmann_json
|
||||
lowdown
|
||||
gmock
|
||||
]
|
||||
++ lib.optionals stdenv.isLinux [libseccomp]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
|
||||
++ lib.optional stdenv.isx86_64 libcpuid;
|
||||
|
||||
awsDeps = lib.optional (stdenv.isLinux || stdenv.isDarwin)
|
||||
(aws-sdk-cpp.override {
|
||||
@@ -104,13 +102,7 @@
|
||||
});
|
||||
|
||||
propagatedDeps =
|
||||
[ ((boehmgc.override {
|
||||
enableLargeConfig = true;
|
||||
}).overrideAttrs(o: {
|
||||
patches = (o.patches or []) ++ [
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
];
|
||||
}))
|
||||
[ (boehmgc.override { enableLargeConfig = true; })
|
||||
];
|
||||
|
||||
perlDeps =
|
||||
@@ -127,7 +119,8 @@
|
||||
''
|
||||
mkdir -p $out/nix-support
|
||||
|
||||
# Converts /nix/store/50p3qk8k...-nix-2.4pre20201102_550e11f/bin/nix to 50p3qk8k.../bin/nix.
|
||||
# Converts /nix/store/50p3qk8kka9dl6wyq40vydq945k0j3kv-nix-2.4pre20201102_550e11f/bin/nix
|
||||
# To 50p3qk8kka9dl6wyq40vydq945k0j3kv/bin/nix
|
||||
tarballPath() {
|
||||
# Remove the store prefix
|
||||
local path=''${1#${builtins.storeDir}/}
|
||||
@@ -140,11 +133,10 @@
|
||||
|
||||
substitute ${./scripts/install.in} $out/install \
|
||||
${pkgs.lib.concatMapStrings
|
||||
(system: let
|
||||
tarball = if builtins.elem system crossSystems then self.hydraJobs.binaryTarballCross.x86_64-linux.${system} else self.hydraJobs.binaryTarball.${system};
|
||||
in '' \
|
||||
--replace '@tarballHash_${system}@' $(nix --experimental-features nix-command hash-file --base16 --type sha256 ${tarball}/*.tar.xz) \
|
||||
--replace '@tarballPath_${system}@' $(tarballPath ${tarball}/*.tar.xz) \
|
||||
(system:
|
||||
'' \
|
||||
--replace '@tarballHash_${system}@' $(nix --experimental-features nix-command hash-file --base16 --type sha256 ${self.hydraJobs.binaryTarball.${system}}/*.tar.xz) \
|
||||
--replace '@tarballPath_${system}@' $(tarballPath ${self.hydraJobs.binaryTarball.${system}}/*.tar.xz) \
|
||||
''
|
||||
)
|
||||
systems
|
||||
@@ -153,15 +145,13 @@
|
||||
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
|
||||
testNixVersions = pkgs: client: daemon: with commonDeps pkgs; with pkgs.lib; pkgs.stdenv.mkDerivation {
|
||||
testNixVersions = pkgs: client: daemon: with commonDeps pkgs; pkgs.stdenv.mkDerivation {
|
||||
NIX_DAEMON_PACKAGE = daemon;
|
||||
NIX_CLIENT_PACKAGE = client;
|
||||
name =
|
||||
"nix-tests"
|
||||
+ optionalString
|
||||
(versionAtLeast daemon.version "2.4pre20211005" &&
|
||||
versionAtLeast client.version "2.4pre20211005")
|
||||
"-${client.version}-against-${daemon.version}";
|
||||
# Must keep this name short as OSX has a rather strict limit on the
|
||||
# socket path length, and this name appears in the path of the
|
||||
# nix-daemon socket used in the tests
|
||||
name = "nix-tests";
|
||||
inherit version;
|
||||
|
||||
src = self;
|
||||
@@ -180,92 +170,21 @@
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
'';
|
||||
installCheckPhase = "make installcheck";
|
||||
|
||||
installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES";
|
||||
};
|
||||
|
||||
binaryTarball = buildPackages: nix: pkgs: let
|
||||
inherit (pkgs) cacert;
|
||||
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
|
||||
in
|
||||
|
||||
buildPackages.runCommand "nix-binary-tarball-${version}"
|
||||
{ #nativeBuildInputs = lib.optional (system != "aarch64-linux") shellcheck;
|
||||
meta.description = "Distribution-independent Nix bootstrap binaries for ${pkgs.system}";
|
||||
}
|
||||
''
|
||||
cp ${installerClosureInfo}/registration $TMPDIR/reginfo
|
||||
cp ${./scripts/create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
|
||||
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
|
||||
substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
|
||||
if type -p shellcheck; then
|
||||
# SC1090: Don't worry about not being able to find
|
||||
# $nix/etc/profile.d/nix.sh
|
||||
shellcheck --exclude SC1090 $TMPDIR/install
|
||||
shellcheck $TMPDIR/create-darwin-volume.sh
|
||||
shellcheck $TMPDIR/install-darwin-multi-user.sh
|
||||
shellcheck $TMPDIR/install-systemd-multi-user.sh
|
||||
|
||||
# SC1091: Don't panic about not being able to source
|
||||
# /etc/profile
|
||||
# SC2002: Ignore "useless cat" "error", when loading
|
||||
# .reginfo, as the cat is a much cleaner
|
||||
# implementation, even though it is "useless"
|
||||
# SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
|
||||
# root's home directory
|
||||
shellcheck --external-sources \
|
||||
--exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
|
||||
fi
|
||||
|
||||
chmod +x $TMPDIR/install
|
||||
chmod +x $TMPDIR/create-darwin-volume.sh
|
||||
chmod +x $TMPDIR/install-darwin-multi-user.sh
|
||||
chmod +x $TMPDIR/install-systemd-multi-user.sh
|
||||
chmod +x $TMPDIR/install-multi-user
|
||||
dir=nix-${version}-${pkgs.system}
|
||||
fn=$out/$dir.tar.xz
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
|
||||
tar cvfJ $fn \
|
||||
--owner=0 --group=0 --mode=u+rw,uga+r \
|
||||
--absolute-names \
|
||||
--hard-dereference \
|
||||
--transform "s,$TMPDIR/install,$dir/install," \
|
||||
--transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
|
||||
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
|
||||
--transform "s,$NIX_STORE,$dir/store,S" \
|
||||
$TMPDIR/install \
|
||||
$TMPDIR/create-darwin-volume.sh \
|
||||
$TMPDIR/install-darwin-multi-user.sh \
|
||||
$TMPDIR/install-systemd-multi-user.sh \
|
||||
$TMPDIR/install-multi-user \
|
||||
$TMPDIR/reginfo \
|
||||
$(cat ${installerClosureInfo}/store-paths)
|
||||
'';
|
||||
|
||||
in {
|
||||
|
||||
# A Nixpkgs overlay that overrides the 'nix' and
|
||||
# 'nix.perl-bindings' packages.
|
||||
overlay = final: prev: {
|
||||
|
||||
# An older version of Nix to test against when using the daemon.
|
||||
# Currently using `nixUnstable` as the stable one doesn't respect
|
||||
# `NIX_DAEMON_SOCKET_PATH` which is needed for the tests.
|
||||
nixStable = prev.nix;
|
||||
|
||||
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
||||
nixUnstable = prev.nixUnstable;
|
||||
|
||||
nix = with final; with commonDeps pkgs; stdenv.mkDerivation {
|
||||
name = "nix-${version}";
|
||||
inherit version;
|
||||
@@ -310,7 +229,7 @@
|
||||
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
||||
'';
|
||||
|
||||
doInstallCheck = true;
|
||||
doInstallCheck = false;
|
||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
separateDebugInfo = true;
|
||||
@@ -335,9 +254,9 @@
|
||||
xz
|
||||
pkgs.perl
|
||||
boost
|
||||
nlohmann_json
|
||||
]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security;
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium;
|
||||
|
||||
configureFlags = ''
|
||||
--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
|
||||
@@ -351,14 +270,21 @@
|
||||
|
||||
};
|
||||
|
||||
lowdown-nix = with final; stdenv.mkDerivation rec {
|
||||
name = "lowdown-0.9.0";
|
||||
lowdown = with final; stdenv.mkDerivation rec {
|
||||
name = "lowdown-0.8.4";
|
||||
|
||||
/*
|
||||
src = fetchurl {
|
||||
url = "https://kristaps.bsd.lv/lowdown/snapshots/${name}.tar.gz";
|
||||
hash = "sha512-U9WeGoInT9vrawwa57t6u9dEdRge4/P+0wLxmQyOL9nhzOEUU2FRz2Be9H0dCjYE7p2v3vCXIYk40M+jjULATw==";
|
||||
};
|
||||
*/
|
||||
|
||||
src = lowdown-src;
|
||||
|
||||
outputs = [ "out" "bin" "dev" ];
|
||||
|
||||
nativeBuildInputs = [ buildPackages.which ];
|
||||
nativeBuildInputs = [ which ];
|
||||
|
||||
configurePhase = ''
|
||||
${if (stdenv.isDarwin && stdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
|
||||
@@ -368,6 +294,13 @@
|
||||
'';
|
||||
};
|
||||
|
||||
nix-benchmarks = prev.runCommandNoCC "nix-benchmarks" {
|
||||
buildInputs = [ final.nix prev.hyperfine ];
|
||||
THINGTOBENCH = ./thingToBench.nix;
|
||||
NIX_PATH="nixpkgs=${prev.path}";
|
||||
} ''
|
||||
bash ${./benchmark.sh} run_all
|
||||
'';
|
||||
};
|
||||
|
||||
hydraJobs = {
|
||||
@@ -377,33 +310,92 @@
|
||||
|
||||
buildStatic = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static);
|
||||
|
||||
buildCross = nixpkgs.lib.genAttrs crossSystems (crossSystem:
|
||||
nixpkgs.lib.genAttrs ["x86_64-linux"] (system: self.packages.${system}."nix-${crossSystem}"));
|
||||
|
||||
# Perl bindings for various platforms.
|
||||
perlBindings = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix.perl-bindings);
|
||||
|
||||
# Binary tarball for various platforms, containing a Nix store
|
||||
# with the closure of 'nix' package, and the second half of
|
||||
# the installation script.
|
||||
binaryTarball = nixpkgs.lib.genAttrs systems (system: binaryTarball nixpkgsFor.${system} nixpkgsFor.${system}.nix nixpkgsFor.${system});
|
||||
binaryTarball = nixpkgs.lib.genAttrs systems (system:
|
||||
|
||||
binaryTarballCross = nixpkgs.lib.genAttrs ["x86_64-linux"] (system: builtins.listToAttrs (map (crossSystem: {
|
||||
name = crossSystem;
|
||||
value = let
|
||||
nixpkgsCross = import nixpkgs {
|
||||
inherit system crossSystem;
|
||||
overlays = [ self.overlay ];
|
||||
};
|
||||
in binaryTarball nixpkgsFor.${system} self.packages.${system}."nix-${crossSystem}" nixpkgsCross;
|
||||
}) crossSystems));
|
||||
with nixpkgsFor.${system};
|
||||
|
||||
let
|
||||
installerClosureInfo = closureInfo { rootPaths = [ nix cacert ]; };
|
||||
in
|
||||
|
||||
runCommand "nix-binary-tarball-${version}"
|
||||
{ #nativeBuildInputs = lib.optional (system != "aarch64-linux") shellcheck;
|
||||
meta.description = "Distribution-independent Nix bootstrap binaries for ${system}";
|
||||
}
|
||||
''
|
||||
cp ${installerClosureInfo}/registration $TMPDIR/reginfo
|
||||
cp ${./scripts/create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
|
||||
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
|
||||
substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \
|
||||
--subst-var-by nix ${nix} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
|
||||
if type -p shellcheck; then
|
||||
# SC1090: Don't worry about not being able to find
|
||||
# $nix/etc/profile.d/nix.sh
|
||||
shellcheck --exclude SC1090 $TMPDIR/install
|
||||
shellcheck $TMPDIR/create-darwin-volume.sh
|
||||
shellcheck $TMPDIR/install-darwin-multi-user.sh
|
||||
shellcheck $TMPDIR/install-systemd-multi-user.sh
|
||||
|
||||
# SC1091: Don't panic about not being able to source
|
||||
# /etc/profile
|
||||
# SC2002: Ignore "useless cat" "error", when loading
|
||||
# .reginfo, as the cat is a much cleaner
|
||||
# implementation, even though it is "useless"
|
||||
# SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
|
||||
# root's home directory
|
||||
shellcheck --external-sources \
|
||||
--exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
|
||||
fi
|
||||
|
||||
chmod +x $TMPDIR/install
|
||||
chmod +x $TMPDIR/create-darwin-volume.sh
|
||||
chmod +x $TMPDIR/install-darwin-multi-user.sh
|
||||
chmod +x $TMPDIR/install-systemd-multi-user.sh
|
||||
chmod +x $TMPDIR/install-multi-user
|
||||
dir=nix-${version}-${system}
|
||||
fn=$out/$dir.tar.xz
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
|
||||
tar cvfJ $fn \
|
||||
--owner=0 --group=0 --mode=u+rw,uga+r \
|
||||
--absolute-names \
|
||||
--hard-dereference \
|
||||
--transform "s,$TMPDIR/install,$dir/install," \
|
||||
--transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
|
||||
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
|
||||
--transform "s,$NIX_STORE,$dir/store,S" \
|
||||
$TMPDIR/install \
|
||||
$TMPDIR/create-darwin-volume.sh \
|
||||
$TMPDIR/install-darwin-multi-user.sh \
|
||||
$TMPDIR/install-systemd-multi-user.sh \
|
||||
$TMPDIR/install-multi-user \
|
||||
$TMPDIR/reginfo \
|
||||
$(cat ${installerClosureInfo}/store-paths)
|
||||
'');
|
||||
|
||||
# The first half of the installation script. This is uploaded
|
||||
# to https://nixos.org/nix/install. It downloads the binary
|
||||
# tarball for the user's system and calls the second half of the
|
||||
# installation script.
|
||||
installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" "armv6l-linux" "armv7l-linux" ];
|
||||
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
|
||||
installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
||||
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" ];
|
||||
|
||||
# Line coverage analysis.
|
||||
coverage =
|
||||
@@ -445,12 +437,6 @@
|
||||
inherit (self) overlay;
|
||||
};
|
||||
|
||||
tests.nssPreload = (import ./tests/nss-preload.nix rec {
|
||||
system = "x86_64-linux";
|
||||
inherit nixpkgs;
|
||||
inherit (self) overlay;
|
||||
});
|
||||
|
||||
tests.githubFlakes = (import ./tests/github-flakes.nix rec {
|
||||
system = "x86_64-linux";
|
||||
inherit nixpkgs;
|
||||
@@ -489,31 +475,25 @@
|
||||
'';
|
||||
*/
|
||||
|
||||
installTests = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in
|
||||
pkgs.runCommand "install-tests" {
|
||||
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
|
||||
againstCurrentUnstable =
|
||||
# FIXME: temporarily disable this on macOS because of #3605.
|
||||
if system == "x86_64-linux"
|
||||
then testNixVersions pkgs pkgs.nix pkgs.nixUnstable
|
||||
else null;
|
||||
# Disabled because the latest stable version doesn't handle
|
||||
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
|
||||
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
|
||||
} "touch $out");
|
||||
|
||||
};
|
||||
|
||||
checks = forAllSystems (system: {
|
||||
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||
installTests = self.hydraJobs.installTests.${system};
|
||||
installTests =
|
||||
let pkgs = nixpkgsFor.${system}; in
|
||||
pkgs.runCommand "install-tests" {
|
||||
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
|
||||
againstCurrentUnstable = testNixVersions pkgs pkgs.nix pkgs.nixUnstable;
|
||||
# Disabled because the latest stable version doesn't handle
|
||||
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
|
||||
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
|
||||
} "touch $out";
|
||||
});
|
||||
|
||||
packages = forAllSystems (system: {
|
||||
inherit (nixpkgsFor.${system}) nix;
|
||||
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems) {
|
||||
inherit (nixpkgsFor.${system}) nix nix-benchmarks;
|
||||
} // nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems) {
|
||||
nix-static = let
|
||||
nixpkgs = nixpkgsFor.${system}.pkgsStatic;
|
||||
in with commonDeps nixpkgs; nixpkgs.stdenv.mkDerivation {
|
||||
@@ -551,49 +531,8 @@
|
||||
stripAllList = ["bin"];
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
hardeningDisable = [ "pie" ];
|
||||
};
|
||||
} // builtins.listToAttrs (map (crossSystem: {
|
||||
name = "nix-${crossSystem}";
|
||||
value = let
|
||||
nixpkgsCross = import nixpkgs {
|
||||
inherit system crossSystem;
|
||||
overlays = [ self.overlay ];
|
||||
};
|
||||
in with commonDeps nixpkgsCross; nixpkgsCross.stdenv.mkDerivation {
|
||||
name = "nix-${version}";
|
||||
|
||||
src = self;
|
||||
|
||||
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";
|
||||
};
|
||||
}) crossSystems)));
|
||||
});
|
||||
|
||||
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@ my $nixpkgsDir = "/home/eelco/Dev/nixpkgs-pristine";
|
||||
|
||||
my $TMPDIR = $ENV{'TMPDIR'} // "/tmp";
|
||||
|
||||
my $isLatest = ($ENV{'IS_LATEST'} // "") eq "1";
|
||||
|
||||
# FIXME: cut&paste from nixos-channel-scripts.
|
||||
sub fetch {
|
||||
my ($url, $type) = @_;
|
||||
@@ -37,18 +35,16 @@ sub fetch {
|
||||
my $evalUrl = "https://hydra.nixos.org/eval/$evalId";
|
||||
my $evalInfo = decode_json(fetch($evalUrl, 'application/json'));
|
||||
#print Dumper($evalInfo);
|
||||
my $flakeUrl = $evalInfo->{flake} or die;
|
||||
my $flakeInfo = decode_json(`nix flake metadata --json "$flakeUrl"` or die);
|
||||
my $nixRev = $flakeInfo->{revision} or die;
|
||||
|
||||
my $buildInfo = decode_json(fetch("$evalUrl/job/build.x86_64-linux", 'application/json'));
|
||||
#print Dumper($buildInfo);
|
||||
my $nixRev = $evalInfo->{jobsetevalinputs}->{nix}->{revision} or die;
|
||||
|
||||
my $releaseName = $buildInfo->{nixname};
|
||||
my $tarballInfo = decode_json(fetch("$evalUrl/job/tarball", 'application/json'));
|
||||
|
||||
my $releaseName = $tarballInfo->{releasename};
|
||||
$releaseName =~ /nix-(.*)$/ or die;
|
||||
my $version = $1;
|
||||
|
||||
print STDERR "Flake URL is $flakeUrl, Nix revision is $nixRev, version is $version\n";
|
||||
print STDERR "Nix revision is $nixRev, version is $version\n";
|
||||
|
||||
my $releaseDir = "nix/$releaseName";
|
||||
|
||||
@@ -87,12 +83,12 @@ sub downloadFile {
|
||||
|
||||
if (!-e $tmpFile) {
|
||||
print STDERR "downloading $srcFile to $tmpFile...\n";
|
||||
system("NIX_REMOTE=https://cache.nixos.org/ nix store cat '$srcFile' > '$tmpFile'") == 0
|
||||
system("NIX_REMOTE=https://cache.nixos.org/ nix cat-store '$srcFile' > '$tmpFile'") == 0
|
||||
or die "unable to fetch $srcFile\n";
|
||||
}
|
||||
|
||||
my $sha256_expected = $buildInfo->{buildproducts}->{$productNr}->{sha256hash} or die;
|
||||
my $sha256_actual = `nix hash file --base16 --type sha256 '$tmpFile'`;
|
||||
my $sha256_actual = `nix hash-file --base16 --type sha256 '$tmpFile'`;
|
||||
chomp $sha256_actual;
|
||||
if ($sha256_expected ne $sha256_actual) {
|
||||
print STDERR "file $tmpFile is corrupt, got $sha256_actual, expected $sha256_expected\n";
|
||||
@@ -108,13 +104,12 @@ sub downloadFile {
|
||||
return $sha256_expected;
|
||||
}
|
||||
|
||||
downloadFile("tarball", "2"); # .tar.bz2
|
||||
my $tarballHash = downloadFile("tarball", "3"); # .tar.xz
|
||||
downloadFile("binaryTarball.i686-linux", "1");
|
||||
downloadFile("binaryTarball.x86_64-linux", "1");
|
||||
downloadFile("binaryTarball.aarch64-linux", "1");
|
||||
downloadFile("binaryTarball.x86_64-darwin", "1");
|
||||
downloadFile("binaryTarball.aarch64-darwin", "1");
|
||||
downloadFile("binaryTarballCross.x86_64-linux.armv6l-linux", "1");
|
||||
downloadFile("binaryTarballCross.x86_64-linux.armv7l-linux", "1");
|
||||
downloadFile("installerScript", "1");
|
||||
|
||||
for my $fn (glob "$tmpDir/*") {
|
||||
@@ -136,38 +131,41 @@ for my $fn (glob "$tmpDir/*") {
|
||||
}
|
||||
}
|
||||
|
||||
exit if $version =~ /pre/;
|
||||
|
||||
# Update nix-fallback-paths.nix.
|
||||
if ($isLatest) {
|
||||
system("cd $nixpkgsDir && git pull") == 0 or die;
|
||||
system("cd $nixpkgsDir && git pull") == 0 or die;
|
||||
|
||||
sub getStorePath {
|
||||
my ($jobName) = @_;
|
||||
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
|
||||
return $buildInfo->{buildoutputs}->{out}->{path} or die "cannot get store path for '$jobName'";
|
||||
sub getStorePath {
|
||||
my ($jobName) = @_;
|
||||
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
|
||||
for my $product (values %{$buildInfo->{buildproducts}}) {
|
||||
next unless $product->{type} eq "nix-build";
|
||||
next if $product->{path} =~ /[a-z]+$/;
|
||||
return $product->{path};
|
||||
}
|
||||
|
||||
write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
||||
"{\n" .
|
||||
" x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
|
||||
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
|
||||
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
|
||||
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
|
||||
" aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" .
|
||||
"}\n");
|
||||
|
||||
system("cd $nixpkgsDir && git commit -a -m 'nix-fallback-paths.nix: Update to $version'") == 0 or die;
|
||||
die;
|
||||
}
|
||||
|
||||
write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
||||
"{\n" .
|
||||
" x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
|
||||
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
|
||||
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
|
||||
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
|
||||
"}\n");
|
||||
|
||||
system("cd $nixpkgsDir && git commit -a -m 'nix-fallback-paths.nix: Update to $version'") == 0 or die;
|
||||
|
||||
# Update the "latest" symlink.
|
||||
$channelsBucket->add_key(
|
||||
"nix-latest/install", "",
|
||||
{ "x-amz-website-redirect-location" => "https://releases.nixos.org/$releaseDir/install" })
|
||||
or die $channelsBucket->err . ": " . $channelsBucket->errstr
|
||||
if $isLatest;
|
||||
or die $channelsBucket->err . ": " . $channelsBucket->errstr;
|
||||
|
||||
# Tag the release in Git.
|
||||
chdir("/home/eelco/Dev/nix-pristine") or die;
|
||||
system("git remote update origin") == 0 or die;
|
||||
system("git tag --force --sign $version $nixRev -m 'Tagging release $version'") == 0 or die;
|
||||
system("git push --tags") == 0 or die;
|
||||
system("git push --force-with-lease origin $nixRev:refs/heads/latest-release") == 0 or die if $isLatest;
|
||||
system("git push --force-with-lease origin $nixRev:refs/heads/latest-release") == 0 or die;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
function _nix_complete
|
||||
# Get the current command up to a cursor.
|
||||
# - Behaves correctly even with pipes and nested in commands like env.
|
||||
# - TODO: Returns the command verbatim (does not interpolate variables).
|
||||
# That might not be optimal for arguments like -f.
|
||||
set -l nix_args (commandline --current-process --tokenize --cut-at-cursor)
|
||||
# --cut-at-cursor with --tokenize removes the current token so we need to add it separately.
|
||||
# https://github.com/fish-shell/fish-shell/issues/7375
|
||||
# Can be an empty string.
|
||||
set -l current_token (commandline --current-token --cut-at-cursor)
|
||||
|
||||
# Nix wants the index of the argv item to complete but the $nix_args variable
|
||||
# also contains the program name (argv[0]) so we would need to subtract 1.
|
||||
# But the variable also misses the current token so it cancels out.
|
||||
set -l nix_arg_to_complete (count $nix_args)
|
||||
|
||||
env NIX_GET_COMPLETIONS=$nix_arg_to_complete $nix_args $current_token
|
||||
end
|
||||
|
||||
function _nix_accepts_files
|
||||
set -l response (_nix_complete)
|
||||
# First line is either filenames or no-filenames.
|
||||
test $response[1] = 'filenames'
|
||||
end
|
||||
|
||||
function _nix
|
||||
set -l response (_nix_complete)
|
||||
# Skip the first line since it handled by _nix_accepts_files.
|
||||
# Tail lines each contain a command followed by a tab character and, optionally, a description.
|
||||
# This is also the format fish expects.
|
||||
string collect -- $response[2..-1]
|
||||
end
|
||||
|
||||
# Disable file path completion if paths do not belong in the current context.
|
||||
complete --command nix --condition 'not _nix_accepts_files' --no-files
|
||||
|
||||
complete --command nix --arguments '(_nix)'
|
||||
@@ -1 +0,0 @@
|
||||
$(eval $(call install-file-as, $(d)/completion.fish, $(datarootdir)/fish/vendor_completions.d/nix.fish, 0644))
|
||||
@@ -1,4 +1,4 @@
|
||||
ifdef HOST_DARWIN
|
||||
ifeq ($(OS), Darwin)
|
||||
|
||||
$(eval $(call install-data-in, $(d)/org.nixos.nix-daemon.plist, $(prefix)/Library/LaunchDaemons))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ifdef HOST_LINUX
|
||||
ifeq ($(OS), Linux)
|
||||
|
||||
$(foreach n, nix-daemon.socket nix-daemon.service, $(eval $(call install-file-in, $(d)/$(n), $(prefix)/lib/systemd/system, 0644)))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ifdef HOST_LINUX
|
||||
ifeq ($(OS), Linux)
|
||||
|
||||
$(foreach n, nix-daemon.conf, $(eval $(call install-file-in, $(d)/$(n), $(sysconfdir)/init, 0644)))
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#compdef nix
|
||||
|
||||
function _nix() {
|
||||
local ifs_bk="$IFS"
|
||||
local input=("${(Q)words[@]}")
|
||||
@@ -20,4 +18,4 @@ function _nix() {
|
||||
_describe 'nix' suggestions
|
||||
}
|
||||
|
||||
_nix "$@"
|
||||
compdef _nix nix
|
||||
|
||||
27
mk/lib.mk
27
mk/lib.mk
@@ -10,25 +10,8 @@ bin-scripts :=
|
||||
noinst-scripts :=
|
||||
man-pages :=
|
||||
install-tests :=
|
||||
OS = $(shell uname -s)
|
||||
|
||||
ifdef HOST_OS
|
||||
HOST_KERNEL = $(firstword $(subst -, ,$(HOST_OS)))
|
||||
ifeq ($(HOST_KERNEL), cygwin)
|
||||
HOST_CYGWIN = 1
|
||||
endif
|
||||
ifeq ($(patsubst darwin%,,$(HOST_KERNEL)),)
|
||||
HOST_DARWIN = 1
|
||||
endif
|
||||
ifeq ($(patsubst freebsd%,,$(HOST_KERNEL)),)
|
||||
HOST_FREEBSD = 1
|
||||
endif
|
||||
ifeq ($(HOST_KERNEL), linux)
|
||||
HOST_LINUX = 1
|
||||
endif
|
||||
ifeq ($(patsubst solaris%,,$(HOST_KERNEL)),)
|
||||
HOST_SOLARIS = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
# Hack to define a literal space.
|
||||
space :=
|
||||
@@ -67,16 +50,16 @@ endif
|
||||
BUILD_SHARED_LIBS ?= 1
|
||||
|
||||
ifeq ($(BUILD_SHARED_LIBS), 1)
|
||||
ifdef HOST_CYGWIN
|
||||
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
||||
GLOBAL_CFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE
|
||||
GLOBAL_CXXFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE
|
||||
else
|
||||
GLOBAL_CFLAGS += -fPIC
|
||||
GLOBAL_CXXFLAGS += -fPIC
|
||||
endif
|
||||
ifndef HOST_DARWIN
|
||||
ifndef HOST_SOLARIS
|
||||
ifndef HOST_FREEBSD
|
||||
ifneq ($(OS), Darwin)
|
||||
ifneq ($(OS), SunOS)
|
||||
ifneq ($(OS), FreeBSD)
|
||||
GLOBAL_LDFLAGS += -Wl,--no-copy-dt-needed-entries
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
libs-list :=
|
||||
|
||||
ifdef HOST_DARWIN
|
||||
ifeq ($(OS), Darwin)
|
||||
SO_EXT = dylib
|
||||
else
|
||||
ifdef HOST_CYGWIN
|
||||
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
||||
SO_EXT = dll
|
||||
else
|
||||
SO_EXT = so
|
||||
@@ -59,7 +59,7 @@ define build-library
|
||||
$(1)_OBJS := $$(addprefix $(buildprefix), $$(addsuffix .o, $$(basename $$(_srcs))))
|
||||
_libs := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_PATH))
|
||||
|
||||
ifdef HOST_CYGWIN
|
||||
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
||||
$(1)_INSTALL_DIR ?= $$(bindir)
|
||||
else
|
||||
$(1)_INSTALL_DIR ?= $$(libdir)
|
||||
@@ -73,18 +73,18 @@ define build-library
|
||||
ifeq ($(BUILD_SHARED_LIBS), 1)
|
||||
|
||||
ifdef $(1)_ALLOW_UNDEFINED
|
||||
ifdef HOST_DARWIN
|
||||
ifeq ($(OS), Darwin)
|
||||
$(1)_LDFLAGS += -undefined suppress -flat_namespace
|
||||
endif
|
||||
else
|
||||
ifndef HOST_DARWIN
|
||||
ifndef HOST_CYGWIN
|
||||
ifneq ($(OS), Darwin)
|
||||
ifneq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
||||
$(1)_LDFLAGS += -Wl,-z,defs
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef HOST_DARWIN
|
||||
ifneq ($(OS), Darwin)
|
||||
$(1)_LDFLAGS += -Wl,-soname=$$($(1)_NAME).$(SO_EXT)
|
||||
endif
|
||||
|
||||
@@ -93,7 +93,7 @@ define build-library
|
||||
$$($(1)_PATH): $$($(1)_OBJS) $$(_libs) | $$(_d)/
|
||||
$$(trace-ld) $(CXX) -o $$(abspath $$@) -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE)) $$($(1)_LDFLAGS_UNINSTALLED)
|
||||
|
||||
ifndef HOST_DARWIN
|
||||
ifneq ($(OS), Darwin)
|
||||
$(1)_LDFLAGS_USE += -Wl,-rpath,$$(abspath $$(_d))
|
||||
endif
|
||||
$(1)_LDFLAGS_USE += -L$$(_d) -l$$(patsubst lib%,%,$$(strip $$($(1)_NAME)))
|
||||
@@ -108,7 +108,7 @@ define build-library
|
||||
$$(trace-ld) $(CXX) -o $$@ -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED))
|
||||
|
||||
$(1)_LDFLAGS_USE_INSTALLED += -L$$(DESTDIR)$$($(1)_INSTALL_DIR) -l$$(patsubst lib%,%,$$(strip $$($(1)_NAME)))
|
||||
ifndef HOST_DARWIN
|
||||
ifneq ($(OS), Darwin)
|
||||
ifeq ($(SET_RPATH_TO_LIBS), 1)
|
||||
$(1)_LDFLAGS_USE_INSTALLED += -Wl,-rpath,$$($(1)_INSTALL_DIR)
|
||||
else
|
||||
@@ -125,8 +125,8 @@ define build-library
|
||||
$(1)_PATH := $$(_d)/$$($(1)_NAME).a
|
||||
|
||||
$$($(1)_PATH): $$($(1)_OBJS) | $$(_d)/
|
||||
$$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$?
|
||||
$$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
|
||||
$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$?
|
||||
$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
|
||||
|
||||
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS)
|
||||
|
||||
|
||||
@@ -13,7 +13,3 @@ define run-install-test
|
||||
endef
|
||||
|
||||
.PHONY: check installcheck
|
||||
|
||||
print-top-help += \
|
||||
echo " check: Run unit tests"; \
|
||||
echo " installcheck: Run functional tests";
|
||||
|
||||
@@ -11,12 +11,12 @@ libnixrust_INSTALL_PATH := $(libdir)/libnixrust.$(SO_EXT)
|
||||
libnixrust_LDFLAGS_USE := -L$(d)/target/$(RUST_DIR) -lnixrust
|
||||
libnixrust_LDFLAGS_USE_INSTALLED := -L$(libdir) -lnixrust
|
||||
|
||||
ifdef HOST_LINUX
|
||||
ifeq ($(OS), Linux)
|
||||
libnixrust_LDFLAGS_USE += -ldl
|
||||
libnixrust_LDFLAGS_USE_INSTALLED += -ldl
|
||||
endif
|
||||
|
||||
ifdef HOST_DARWIN
|
||||
ifeq ($(OS), Darwin)
|
||||
libnixrust_BUILD_FLAGS = NIX_LDFLAGS="-undefined dynamic_lookup"
|
||||
else
|
||||
libnixrust_LDFLAGS_USE += -Wl,-rpath,$(abspath $(d)/target/$(RUST_DIR))
|
||||
@@ -31,7 +31,7 @@ $(libnixrust_PATH): $(call rwildcard, $(d)/src, *.rs) $(d)/Cargo.toml
|
||||
|
||||
$(libnixrust_INSTALL_PATH): $(libnixrust_PATH)
|
||||
$(target-gen) cp $^ $@
|
||||
ifdef HOST_DARWIN
|
||||
ifeq ($(OS), Darwin)
|
||||
install_name_tool -id $@ $@
|
||||
endif
|
||||
|
||||
@@ -40,7 +40,7 @@ clean: clean-rust
|
||||
clean-rust:
|
||||
$(suppress) rm -rfv nix-rust/target
|
||||
|
||||
ifndef HOST_DARWIN
|
||||
ifneq ($(OS), Darwin)
|
||||
check: rust-tests
|
||||
|
||||
rust-tests:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
makefiles = local.mk
|
||||
|
||||
GLOBAL_CXXFLAGS += -g -Wall -std=c++17 -I ../src
|
||||
GLOBAL_CXXFLAGS += -g -Wall -std=c++17
|
||||
|
||||
-include Makefile.config
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
HOST_OS = @host_os@
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CXX = @CXX@
|
||||
|
||||
@@ -7,8 +7,6 @@ CXXFLAGS=
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
# Use 64-bit file system calls so that we can support files > 2 GiB.
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ our @EXPORT = qw(
|
||||
derivationFromPath
|
||||
addTempRoot
|
||||
getBinDir getStoreDir
|
||||
queryRawRealisation
|
||||
);
|
||||
|
||||
our $VERSION = '0.15';
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "crypto.hh"
|
||||
|
||||
#include <sodium.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
||||
using namespace nix;
|
||||
@@ -121,18 +120,6 @@ SV * queryPathInfo(char * path, int base32)
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
SV * queryRawRealisation(char * outputId)
|
||||
PPCODE:
|
||||
try {
|
||||
auto realisation = store()->queryRealisation(DrvOutput::parse(outputId));
|
||||
if (realisation)
|
||||
XPUSHs(sv_2mortal(newSVpv(realisation->toJSON().dump().c_str(), 0)));
|
||||
else
|
||||
XPUSHs(sv_2mortal(newSVpv("", 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV * queryPathFromHashPart(char * hashPart)
|
||||
PPCODE:
|
||||
|
||||
@@ -28,7 +28,7 @@ Store_CXXFLAGS = \
|
||||
|
||||
Store_LDFLAGS := $(SODIUM_LIBS) $(NIX_LIBS)
|
||||
|
||||
ifdef HOST_CYGWIN
|
||||
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
||||
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
|
||||
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
|
||||
Store_LDFLAGS += $(shell find ${archlib} -name ${libperl})
|
||||
|
||||
@@ -715,8 +715,7 @@ create_volume() {
|
||||
# 6) getting special w/ awk may be fragile, but doing it to:
|
||||
# - save time over running slow diskutil commands
|
||||
# - skirt risk we grab wrong volume if multiple match
|
||||
_sudo "to create a new APFS volume '$NIX_VOLUME_LABEL' on $NIX_VOLUME_USE_DISK" \
|
||||
/usr/sbin/diskutil apfs addVolume "$NIX_VOLUME_USE_DISK" "$NIX_VOLUME_FS" "$NIX_VOLUME_LABEL" -nomount | /usr/bin/awk '/Created new APFS Volume/ {print $5}'
|
||||
/usr/sbin/diskutil apfs addVolume "$NIX_VOLUME_USE_DISK" "$NIX_VOLUME_FS" "$NIX_VOLUME_LABEL" -nomount | /usr/bin/awk '/Created new APFS Volume/ {print $5}'
|
||||
}
|
||||
|
||||
volume_uuid_from_special() {
|
||||
@@ -739,6 +738,7 @@ await_volume() {
|
||||
setup_volume() {
|
||||
local use_special use_uuid profile_packages
|
||||
task "Creating a Nix volume" >&2
|
||||
# DOING: I'm tempted to wrap this call in a grep to get the new disk special without doing anything too complex, but this sudo wrapper *is* a little complex, so it'll be a PITA unless maybe we can skip sudo on this. Let's just try it without.
|
||||
|
||||
use_special="${NIX_VOLUME_USE_SPECIAL:-$(create_volume)}"
|
||||
|
||||
@@ -759,11 +759,6 @@ setup_volume() {
|
||||
|
||||
await_volume
|
||||
|
||||
if [ "$(/usr/sbin/diskutil info -plist "$NIX_ROOT" | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
|
||||
_sudo "to set enableOwnership (enabling users to own files)" \
|
||||
/usr/sbin/diskutil enableOwnership "$NIX_ROOT"
|
||||
fi
|
||||
|
||||
# TODO: below is a vague kludge for now; I just don't know
|
||||
# what if any safe action there is to take here. Also, the
|
||||
# reminder isn't very helpful.
|
||||
|
||||
@@ -13,22 +13,11 @@ NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
|
||||
read_only_root() {
|
||||
# this touch command ~should~ always produce an error
|
||||
# as of this change I confirmed /usr/bin/touch emits:
|
||||
# "touch: /: Operation not permitted" Monterey
|
||||
# "touch: /: Read-only file system" Catalina+ and Big Sur
|
||||
# "touch: /: Permission denied" Mojave
|
||||
# (not matching prefix for compat w/ coreutils touch in case using
|
||||
# an explicit path causes problems; its prefix differs)
|
||||
case "$(/usr/bin/touch / 2>&1)" in
|
||||
*"Read-only file system") # Catalina, Big Sur
|
||||
return 0
|
||||
;;
|
||||
*"Operation not permitted") # Monterey
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
[[ "$(/usr/bin/touch / 2>&1)" = *"Read-only file system" ]]
|
||||
|
||||
# Avoiding the slow semantic way to get this information (~330ms vs ~8ms)
|
||||
# unless using touch causes problems. Just in case, that approach is:
|
||||
@@ -78,7 +67,7 @@ poly_service_installed_check() {
|
||||
poly_service_uninstall_directions() {
|
||||
echo "$1. Remove macOS-specific components:"
|
||||
if should_create_volume && test_nix_volume_mountd_installed; then
|
||||
nix_volume_mountd_uninstall_directions
|
||||
darwin_volume_uninstall_directions
|
||||
fi
|
||||
if test_nix_daemon_installed; then
|
||||
nix_daemon_uninstall_directions
|
||||
@@ -217,8 +206,4 @@ poly_prepare_to_install() {
|
||||
EOF
|
||||
setup_darwin_volume
|
||||
fi
|
||||
|
||||
if [ "$(diskutil info -plist /nix | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
|
||||
failure "This script needs a /nix volume with global permissions! This may require running sudo diskutil enableOwnership /nix."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
|
||||
readonly NIX_ROOT="/nix"
|
||||
readonly NIX_EXTRA_CONF=${NIX_EXTRA_CONF:-}
|
||||
|
||||
readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshrc" "/etc/bash.bashrc" "/etc/zsh/zshrc")
|
||||
readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshenv" "/etc/bash.bashrc" "/etc/zsh/zshenv")
|
||||
readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
|
||||
readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
|
||||
|
||||
@@ -599,7 +599,7 @@ manager. This will happen in a few stages:
|
||||
1. Make sure your computer doesn't already have Nix. If it does, I
|
||||
will show you instructions on how to clean up your old install.
|
||||
|
||||
2. Show you what I am going to install and where. Then I will ask
|
||||
2. Show you what we are going to install and where. Then we will ask
|
||||
if you are ready to continue.
|
||||
|
||||
3. Create the system users and groups that the Nix daemon uses to run
|
||||
@@ -614,14 +614,14 @@ manager. This will happen in a few stages:
|
||||
|
||||
EOF
|
||||
|
||||
if ui_confirm "Would you like to see a more detailed list of what I will do?"; then
|
||||
if ui_confirm "Would you like to see a more detailed list of what we will do?"; then
|
||||
cat <<EOF
|
||||
|
||||
I will:
|
||||
We will:
|
||||
|
||||
- make sure your computer doesn't already have Nix files
|
||||
(if it does, I will tell you how to clean them up.)
|
||||
- create local users (see the list above for the users I'll make)
|
||||
- create local users (see the list above for the users we'll make)
|
||||
- create a local group ($NIX_BUILD_GROUP_NAME)
|
||||
- install Nix in to $NIX_ROOT
|
||||
- create a configuration file in /etc/nix
|
||||
@@ -656,7 +656,7 @@ run in a headless fashion, like this:
|
||||
|
||||
$ curl -L https://nixos.org/nix/install | sh
|
||||
|
||||
or maybe in a CI pipeline. Because of that, I'm going to skip the
|
||||
or maybe in a CI pipeline. Because of that, we're going to skip the
|
||||
verbose output in the interest of brevity.
|
||||
|
||||
If you would like to
|
||||
@@ -670,7 +670,7 @@ EOF
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
This script is going to call sudo a lot. Every time I do, it'll
|
||||
This script is going to call sudo a lot. Every time we do, it'll
|
||||
output exactly what it'll do, and why.
|
||||
|
||||
Just like this:
|
||||
@@ -682,15 +682,15 @@ EOF
|
||||
cat <<EOF
|
||||
|
||||
This might look scary, but everything can be undone by running just a
|
||||
few commands. I used to ask you to confirm each time sudo ran, but it
|
||||
few commands. We used to ask you to confirm each time sudo ran, but it
|
||||
was too many times. Instead, I'll just ask you this one time:
|
||||
|
||||
EOF
|
||||
if ui_confirm "Can I use sudo?"; then
|
||||
if ui_confirm "Can we use sudo?"; then
|
||||
ok "Yay! Thanks! Let's get going!"
|
||||
else
|
||||
failure <<EOF
|
||||
That is okay, but I can't install.
|
||||
That is okay, but we can't install.
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
@@ -701,10 +701,7 @@ install_from_extracted_nix() {
|
||||
cd "$EXTRACTED_NIX_PATH"
|
||||
|
||||
_sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \
|
||||
cp -RLp ./store/* "$NIX_ROOT/store/"
|
||||
|
||||
_sudo "to make the new store non-writable at $NIX_ROOT/store" \
|
||||
chmod -R ugo-w "$NIX_ROOT/store/"
|
||||
rsync -rlpt --chmod=-w ./store/* "$NIX_ROOT/store/"
|
||||
|
||||
if [ -d "$NIX_INSTALLED_NIX" ]; then
|
||||
echo " Alright! We have our first nix at $NIX_INSTALLED_NIX"
|
||||
@@ -811,8 +808,8 @@ main() {
|
||||
# pre-Catalina macOS if run as root user.
|
||||
if [ $EUID -eq 0 ]; then
|
||||
failure <<EOF
|
||||
Please do not run this script with root privileges. I will call sudo
|
||||
when I need to.
|
||||
Please do not run this script with root privileges. We will call sudo
|
||||
when we need to.
|
||||
EOF
|
||||
fi
|
||||
|
||||
|
||||
@@ -106,11 +106,12 @@ while [ $# -gt 0 ]; do
|
||||
echo ""
|
||||
echo " --no-channel-add: Don't add any channels. nixpkgs-unstable is installed by default."
|
||||
echo ""
|
||||
echo " --no-modify-profile: Don't modify the user profile to automatically load nix."
|
||||
echo " --no-modify-profile: Skip channel installation. When not provided nixpkgs-unstable"
|
||||
echo " is installed by default."
|
||||
echo ""
|
||||
echo " --daemon-user-count: Number of build users to create. Defaults to 32."
|
||||
echo ""
|
||||
echo " --nix-extra-conf-file: Path to nix.conf to prepend when installing /etc/nix/nix.conf"
|
||||
echo " --nix-extra-conf-file: Path to nix.conf to prepend when installing /etc/nix.conf"
|
||||
echo ""
|
||||
if [ -n "${INVOKED_FROM_INSTALL_IN:-}" ]; then
|
||||
echo " --tarball-url-prefix URL: Base URL to download the Nix tarball from."
|
||||
@@ -215,7 +216,7 @@ if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
|
||||
if [ -w "$fn" ]; then
|
||||
if ! grep -q "$p" "$fn"; then
|
||||
echo "modifying $fn..." >&2
|
||||
printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p" "$p" >> "$fn"
|
||||
echo -e "\nif [ -e $p ]; then . $p; fi # added by Nix installer" >> "$fn"
|
||||
fi
|
||||
added=1
|
||||
break
|
||||
@@ -226,7 +227,7 @@ if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
|
||||
if [ -w "$fn" ]; then
|
||||
if ! grep -q "$p" "$fn"; then
|
||||
echo "modifying $fn..." >&2
|
||||
printf '\nif [ -e %s ]; then . %s; fi # added by Nix installer\n' "$p" "$p" >> "$fn"
|
||||
echo -e "\nif [ -e $p ]; then . $p; fi # added by Nix installer" >> "$fn"
|
||||
fi
|
||||
added=1
|
||||
break
|
||||
|
||||
@@ -40,23 +40,13 @@ case "$(uname -s).$(uname -m)" in
|
||||
path=@tarballPath_aarch64-linux@
|
||||
system=aarch64-linux
|
||||
;;
|
||||
Linux.armv6l_linux)
|
||||
hash=@tarballHash_armv6l-linux@
|
||||
path=@tarballPath_armv6l-linux@
|
||||
system=armv6l-linux
|
||||
;;
|
||||
Linux.armv7l_linux)
|
||||
hash=@tarballHash_armv7l-linux@
|
||||
path=@tarballPath_armv7l-linux@
|
||||
system=armv7l-linux
|
||||
;;
|
||||
Darwin.x86_64)
|
||||
hash=@tarballHash_x86_64-darwin@
|
||||
path=@tarballPath_x86_64-darwin@
|
||||
system=x86_64-darwin
|
||||
;;
|
||||
Darwin.arm64|Darwin.aarch64)
|
||||
hash=@tarballHash_aarch64-darwin@
|
||||
hash=@binaryTarball_aarch64-darwin@
|
||||
path=@tarballPath_aarch64-darwin@
|
||||
system=aarch64-darwin
|
||||
;;
|
||||
@@ -76,21 +66,14 @@ fi
|
||||
|
||||
tarball=$tmpDir/nix-@nixVersion@-$system.tar.xz
|
||||
|
||||
require_util curl "download the binary tarball"
|
||||
require_util tar "unpack the binary tarball"
|
||||
if [ "$(uname -s)" != "Darwin" ]; then
|
||||
require_util xz "unpack the binary tarball"
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null 2>&1; then
|
||||
fetch() { wget "$1" -O "$2"; }
|
||||
elif command -v curl > /dev/null 2>&1; then
|
||||
fetch() { curl -L "$1" -o "$2"; }
|
||||
else
|
||||
oops "you don't have wget or curl installed, which I need to download the binary tarball"
|
||||
fi
|
||||
|
||||
echo "downloading Nix @nixVersion@ binary tarball for $system from '$url' to '$tmpDir'..."
|
||||
fetch "$url" "$tarball" || oops "failed to download '$url'"
|
||||
curl -L "$url" -o "$tarball" || oops "failed to download '$url'"
|
||||
|
||||
if command -v sha256sum > /dev/null 2>&1; then
|
||||
hash2="$(sha256sum -b "$tarball" | cut -c1-64)"
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "derivations.hh"
|
||||
#include "local-store.hh"
|
||||
#include "legacy.hh"
|
||||
#include "experimental-features.hh"
|
||||
|
||||
using namespace nix;
|
||||
using std::cin;
|
||||
@@ -131,14 +130,11 @@ static int main_build_remote(int argc, char * * argv)
|
||||
for (auto & m : machines) {
|
||||
debug("considering building on remote machine '%s'", m.storeUri);
|
||||
|
||||
if (m.enabled
|
||||
&& (neededSystem == "builtin"
|
||||
|| std::find(m.systemTypes.begin(),
|
||||
m.systemTypes.end(),
|
||||
neededSystem) != m.systemTypes.end()) &&
|
||||
if (m.enabled && std::find(m.systemTypes.begin(),
|
||||
m.systemTypes.end(),
|
||||
neededSystem) != m.systemTypes.end() &&
|
||||
m.allSupported(requiredFeatures) &&
|
||||
m.mandatoryMet(requiredFeatures))
|
||||
{
|
||||
m.mandatoryMet(requiredFeatures)) {
|
||||
rightType = true;
|
||||
AutoCloseFD free;
|
||||
uint64_t load = 0;
|
||||
@@ -274,23 +270,14 @@ connected:
|
||||
|
||||
{
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri));
|
||||
copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
|
||||
copyPaths(store, ref<Store>(sshStore), store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
|
||||
}
|
||||
|
||||
uploadLock = -1;
|
||||
|
||||
auto drv = store->readDerivation(*drvPath);
|
||||
auto outputHashes = staticOutputHashes(*store, drv);
|
||||
|
||||
// Hijack the inputs paths of the derivation to include all the paths
|
||||
// that come from the `inputDrvs` set.
|
||||
// We don’t do that for the derivations whose `inputDrvs` is empty
|
||||
// because
|
||||
// 1. It’s not needed
|
||||
// 2. Changing the `inputSrcs` set changes the associated output ids,
|
||||
// which break CA derivations
|
||||
if (!drv.inputDrvs.empty())
|
||||
drv.inputSrcs = store->parseStorePathSet(inputs);
|
||||
drv.inputSrcs = store->parseStorePathSet(inputs);
|
||||
|
||||
auto result = sshStore->buildDerivation(*drvPath, drv);
|
||||
|
||||
@@ -299,7 +286,7 @@ connected:
|
||||
|
||||
std::set<Realisation> missingRealisations;
|
||||
StorePathSet missingPaths;
|
||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) {
|
||||
if (settings.isExperimentalFeatureEnabled("ca-derivations") && !derivationHasKnownOutputPaths(drv.type())) {
|
||||
for (auto & outputName : wantedOutputs) {
|
||||
auto thisOutputHash = outputHashes.at(outputName);
|
||||
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
|
||||
@@ -325,13 +312,13 @@ connected:
|
||||
if (auto localStore = store.dynamic_pointer_cast<LocalStore>())
|
||||
for (auto & path : missingPaths)
|
||||
localStore->locksHeld.insert(store->printStorePath(path)); /* FIXME: ugly */
|
||||
copyPaths(*sshStore, *store, missingPaths, NoRepair, NoCheckSigs, NoSubstitute);
|
||||
copyPaths(ref<Store>(sshStore), store, missingPaths, NoRepair, NoCheckSigs, NoSubstitute);
|
||||
}
|
||||
// XXX: Should be done as part of `copyPaths`
|
||||
for (auto & realisation : missingRealisations) {
|
||||
// Should hold, because if the feature isn't enabled the set
|
||||
// of missing realisations should be empty
|
||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
||||
settings.requireExperimentalFeature("ca-derivations");
|
||||
store->registerDrvOutput(realisation);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,30 +54,6 @@ void StoreCommand::run()
|
||||
run(getStore());
|
||||
}
|
||||
|
||||
EvalCommand::EvalCommand()
|
||||
{
|
||||
}
|
||||
|
||||
EvalCommand::~EvalCommand()
|
||||
{
|
||||
if (evalState)
|
||||
evalState->printStats();
|
||||
}
|
||||
|
||||
ref<Store> EvalCommand::getEvalStore()
|
||||
{
|
||||
if (!evalStore)
|
||||
evalStore = evalStoreUrl ? openStore(*evalStoreUrl) : getStore();
|
||||
return ref<Store>(evalStore);
|
||||
}
|
||||
|
||||
ref<EvalState> EvalCommand::getEvalState()
|
||||
{
|
||||
if (!evalState)
|
||||
evalState = std::make_shared<EvalState>(searchPath, getEvalStore(), getStore());
|
||||
return ref<EvalState>(evalState);
|
||||
}
|
||||
|
||||
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
||||
: recursive(recursive)
|
||||
{
|
||||
@@ -115,12 +91,12 @@ void BuiltPathsCommand::run(ref<Store> store)
|
||||
for (auto & p : store->queryAllValidPaths())
|
||||
paths.push_back(BuiltPath::Opaque{p});
|
||||
} else {
|
||||
paths = toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
|
||||
paths = toBuiltPaths(store, realiseMode, operateOn, installables);
|
||||
if (recursive) {
|
||||
// XXX: This only computes the store path closure, ignoring
|
||||
// intermediate realisations
|
||||
StorePathSet pathsRoots, pathsClosure;
|
||||
for (auto & root : paths) {
|
||||
for (auto & root: paths) {
|
||||
auto rootFromThis = root.outPaths();
|
||||
pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
|
||||
}
|
||||
@@ -138,20 +114,17 @@ StorePathsCommand::StorePathsCommand(bool recursive)
|
||||
{
|
||||
}
|
||||
|
||||
void StorePathsCommand::run(ref<Store> store, BuiltPaths && paths)
|
||||
void StorePathsCommand::run(ref<Store> store, BuiltPaths paths)
|
||||
{
|
||||
StorePathSet storePaths;
|
||||
for (auto & builtPath : paths)
|
||||
for (auto & p : builtPath.outPaths())
|
||||
storePaths.insert(p);
|
||||
StorePaths storePaths;
|
||||
for (auto& builtPath : paths)
|
||||
for (auto& p : builtPath.outPaths())
|
||||
storePaths.push_back(p);
|
||||
|
||||
auto sorted = store->topoSortPaths(storePaths);
|
||||
std::reverse(sorted.begin(), sorted.end());
|
||||
|
||||
run(store, std::move(sorted));
|
||||
run(store, std::move(storePaths));
|
||||
}
|
||||
|
||||
void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePaths)
|
||||
void StorePathCommand::run(ref<Store> store, std::vector<StorePath> storePaths)
|
||||
{
|
||||
if (storePaths.size() != 1)
|
||||
throw UsageError("this command requires exactly one store path");
|
||||
@@ -203,10 +176,10 @@ void MixProfile::updateProfile(const BuiltPaths & buildables)
|
||||
|
||||
for (auto & buildable : buildables) {
|
||||
std::visit(overloaded {
|
||||
[&](const BuiltPath::Opaque & bo) {
|
||||
[&](BuiltPath::Opaque bo) {
|
||||
result.push_back(bo.path);
|
||||
},
|
||||
[&](const BuiltPath::Built & bfd) {
|
||||
[&](BuiltPath::Built bfd) {
|
||||
for (auto & output : bfd.outputs) {
|
||||
result.push_back(output.second);
|
||||
}
|
||||
@@ -215,7 +188,7 @@ void MixProfile::updateProfile(const BuiltPaths & buildables)
|
||||
}
|
||||
|
||||
if (result.size() != 1)
|
||||
throw UsageError("'--profile' requires that the arguments produce a single store path, but there are %d", result.size());
|
||||
throw Error("'--profile' requires that the arguments produce a single store path, but there are %d", result.size());
|
||||
|
||||
updateProfile(result[0]);
|
||||
}
|
||||
|
||||
@@ -45,18 +45,11 @@ private:
|
||||
|
||||
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||
{
|
||||
EvalCommand();
|
||||
|
||||
~EvalCommand();
|
||||
|
||||
ref<Store> getEvalStore();
|
||||
|
||||
ref<EvalState> getEvalState();
|
||||
|
||||
private:
|
||||
std::shared_ptr<Store> evalStore;
|
||||
|
||||
std::shared_ptr<EvalState> evalState;
|
||||
|
||||
~EvalCommand();
|
||||
};
|
||||
|
||||
struct MixFlakeOptions : virtual Args, EvalCommand
|
||||
@@ -108,8 +101,6 @@ enum class Realise {
|
||||
exists. */
|
||||
Derivation,
|
||||
/* Evaluate in dry-run mode. Postcondition: nothing. */
|
||||
// FIXME: currently unused, but could be revived if we can
|
||||
// evaluate derivations in-memory.
|
||||
Nothing
|
||||
};
|
||||
|
||||
@@ -169,7 +160,7 @@ public:
|
||||
|
||||
using StoreCommand::run;
|
||||
|
||||
virtual void run(ref<Store> store, BuiltPaths && paths) = 0;
|
||||
virtual void run(ref<Store> store, BuiltPaths paths) = 0;
|
||||
|
||||
void run(ref<Store> store) override;
|
||||
|
||||
@@ -182,9 +173,9 @@ struct StorePathsCommand : public BuiltPathsCommand
|
||||
|
||||
using BuiltPathsCommand::run;
|
||||
|
||||
virtual void run(ref<Store> store, std::vector<StorePath> && storePaths) = 0;
|
||||
virtual void run(ref<Store> store, std::vector<StorePath> storePaths) = 0;
|
||||
|
||||
void run(ref<Store> store, BuiltPaths && paths) override;
|
||||
void run(ref<Store> store, BuiltPaths paths) override;
|
||||
};
|
||||
|
||||
/* A command that operates on exactly one store path. */
|
||||
@@ -194,7 +185,7 @@ struct StorePathCommand : public StorePathsCommand
|
||||
|
||||
virtual void run(ref<Store> store, const StorePath & storePath) = 0;
|
||||
|
||||
void run(ref<Store> store, std::vector<StorePath> && storePaths) override;
|
||||
void run(ref<Store> store, std::vector<StorePath> storePaths) override;
|
||||
};
|
||||
|
||||
/* A helper class for registering commands globally. */
|
||||
@@ -225,37 +216,26 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
|
||||
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
|
||||
}
|
||||
|
||||
BuiltPaths build(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store, Realise mode,
|
||||
const std::vector<std::shared_ptr<Installable>> & installables,
|
||||
BuildMode bMode = bmNormal);
|
||||
BuiltPaths build(ref<Store> store, Realise mode,
|
||||
std::vector<std::shared_ptr<Installable>> installables, BuildMode bMode = bmNormal);
|
||||
|
||||
std::set<StorePath> toStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
const std::vector<std::shared_ptr<Installable>> & installables);
|
||||
std::set<StorePath> toStorePaths(ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
std::vector<std::shared_ptr<Installable>> installables);
|
||||
|
||||
StorePath toStorePath(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
StorePath toStorePath(ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
std::shared_ptr<Installable> installable);
|
||||
|
||||
std::set<StorePath> toDerivations(
|
||||
ref<Store> store,
|
||||
const std::vector<std::shared_ptr<Installable>> & installables,
|
||||
std::set<StorePath> toDerivations(ref<Store> store,
|
||||
std::vector<std::shared_ptr<Installable>> installables,
|
||||
bool useDeriver = false);
|
||||
|
||||
BuiltPaths toBuiltPaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
const std::vector<std::shared_ptr<Installable>> & installables);
|
||||
std::vector<std::shared_ptr<Installable>> installables);
|
||||
|
||||
/* Helper function to generate args that invoke $EDITOR on
|
||||
filename:lineno. */
|
||||
|
||||
@@ -58,13 +58,9 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
|
||||
addFlag({
|
||||
.longName = "no-registries",
|
||||
.description =
|
||||
"Don't allow lookups in the flake registries. This option is deprecated; use `--no-use-registries`.",
|
||||
.description = "Don't allow lookups in the flake registries.",
|
||||
.category = category,
|
||||
.handler = {[&]() {
|
||||
lockFlags.useRegistries = false;
|
||||
warn("'--no-registries' is deprecated; use '--no-use-registries'");
|
||||
}}
|
||||
.handler = {&lockFlags.useRegistries, false}
|
||||
});
|
||||
|
||||
addFlag({
|
||||
@@ -175,50 +171,14 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
|
||||
|
||||
void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
{
|
||||
if (file) {
|
||||
evalSettings.pureEval = false;
|
||||
auto state = getEvalState();
|
||||
Expr *e = state->parseExprFromFile(
|
||||
resolveExprPath(state->checkSourcePath(lookupFileArg(*state, *file)))
|
||||
);
|
||||
if (file) return; // FIXME
|
||||
|
||||
Value root;
|
||||
state->eval(e, root);
|
||||
|
||||
auto autoArgs = getAutoArgs(*state);
|
||||
|
||||
std::string prefix_ = std::string(prefix);
|
||||
auto sep = prefix_.rfind('.');
|
||||
std::string searchWord;
|
||||
if (sep != std::string::npos) {
|
||||
searchWord = prefix_.substr(sep, std::string::npos);
|
||||
prefix_ = prefix_.substr(0, sep);
|
||||
} else {
|
||||
searchWord = prefix_;
|
||||
prefix_ = "";
|
||||
}
|
||||
|
||||
Value &v1(*findAlongAttrPath(*state, prefix_, *autoArgs, root).first);
|
||||
state->forceValue(v1);
|
||||
Value v2;
|
||||
state->autoCallFunction(*autoArgs, v1, v2);
|
||||
|
||||
if (v2.type() == nAttrs) {
|
||||
for (auto & i : *v2.attrs) {
|
||||
std::string name = i.name;
|
||||
if (name.find(searchWord) == 0) {
|
||||
completions->add(i.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completeFlakeRefWithFragment(
|
||||
getEvalState(),
|
||||
lockFlags,
|
||||
getDefaultFlakeAttrPathPrefixes(),
|
||||
getDefaultFlakeAttrPaths(),
|
||||
prefix);
|
||||
}
|
||||
completeFlakeRefWithFragment(
|
||||
getEvalState(),
|
||||
lockFlags,
|
||||
getDefaultFlakeAttrPathPrefixes(),
|
||||
getDefaultFlakeAttrPaths(),
|
||||
prefix);
|
||||
}
|
||||
|
||||
void completeFlakeRefWithFragment(
|
||||
@@ -289,6 +249,19 @@ void completeFlakeRefWithFragment(
|
||||
completeFlakeRef(evalState->store, prefix);
|
||||
}
|
||||
|
||||
ref<EvalState> EvalCommand::getEvalState()
|
||||
{
|
||||
if (!evalState)
|
||||
evalState = std::make_shared<EvalState>(searchPath, getStore());
|
||||
return ref<EvalState>(evalState);
|
||||
}
|
||||
|
||||
EvalCommand::~EvalCommand()
|
||||
{
|
||||
if (evalState)
|
||||
evalState->printStats();
|
||||
}
|
||||
|
||||
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
||||
{
|
||||
if (prefix == "")
|
||||
@@ -329,6 +302,14 @@ Installable::getCursors(EvalState & state)
|
||||
return {{evalCache->getRoot(), ""}};
|
||||
}
|
||||
|
||||
std::pair<Value*, Pos> Installable::toValue(EvalState & state)
|
||||
{
|
||||
auto values = toValues(state);
|
||||
if (values.empty())
|
||||
throw Error("cannot find flake attribute '%s'", what());
|
||||
return {std::get<0>(values[0]), std::get<1>(values[0])};
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
|
||||
Installable::getCursor(EvalState & state)
|
||||
{
|
||||
@@ -378,7 +359,6 @@ DerivedPaths InstallableValue::toDerivedPaths()
|
||||
DerivedPaths res;
|
||||
|
||||
std::map<StorePath, std::set<std::string>> drvsToOutputs;
|
||||
RealisedPath::Set drvsToCopy;
|
||||
|
||||
// Group by derivation, helps with .all in particular
|
||||
for (auto & drv : toDerivations()) {
|
||||
@@ -386,7 +366,6 @@ DerivedPaths InstallableValue::toDerivedPaths()
|
||||
if (outputName == "")
|
||||
throw Error("derivation '%s' lacks an 'outputName' attribute", state->store->printStorePath(drv.drvPath));
|
||||
drvsToOutputs[drv.drvPath].insert(outputName);
|
||||
drvsToCopy.insert(drv.drvPath);
|
||||
}
|
||||
|
||||
for (auto & i : drvsToOutputs)
|
||||
@@ -407,11 +386,11 @@ struct InstallableAttrPath : InstallableValue
|
||||
|
||||
std::string what() override { return attrPath; }
|
||||
|
||||
std::pair<Value *, Pos> toValue(EvalState & state) override
|
||||
std::vector<std::tuple<Value *, Pos, std::string>> toValues(EvalState & state) override
|
||||
{
|
||||
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
||||
state.forceValue(*vRes);
|
||||
return {vRes, pos};
|
||||
return {{vRes, pos, attrPath}};
|
||||
}
|
||||
|
||||
virtual std::vector<InstallableValue::DerivationInfo> toDerivations() override;
|
||||
@@ -457,12 +436,10 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked
|
||||
|
||||
callFlake(state, lockedFlake, *vFlake);
|
||||
|
||||
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
||||
assert(aOutputs);
|
||||
|
||||
state.forceValue(*aOutputs->value);
|
||||
|
||||
return aOutputs->value;
|
||||
auto vRes = state.allocValue();
|
||||
auto gotField = state.lazyGetAttrField(*vFlake, {state.symbols.create("outputs")}, noPos, *vRes);
|
||||
assert(gotField != EvalState::LazyValueType::Missing);
|
||||
return vRes;
|
||||
}
|
||||
|
||||
ref<eval_cache::EvalCache> openEvalCache(
|
||||
@@ -529,25 +506,34 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
|
||||
auto root = cache->getRoot();
|
||||
|
||||
for (auto & attrPath : getActualAttrPaths()) {
|
||||
auto attr = root->findAlongAttrPath(
|
||||
parseAttrPath(*state, attrPath),
|
||||
true
|
||||
);
|
||||
auto emptyArgs = state->allocBindings(0);
|
||||
try {
|
||||
auto [drvValue, pos] = findAlongAttrPath(
|
||||
*state,
|
||||
attrPath,
|
||||
*emptyArgs,
|
||||
*getFlakeOutputs(*state, *lockedFlake)
|
||||
);
|
||||
Value * v = state->allocValue();
|
||||
if (!state->getAttrField(*drvValue, {state->sDrvPath}, pos, *v))
|
||||
break;
|
||||
auto drvPath = state->forceString(*v);
|
||||
if (!state->getAttrField(*drvValue, {state->sOutPath}, pos, *v))
|
||||
break;
|
||||
auto outPath = state->forceString(*v);
|
||||
if (!state->getAttrField(*drvValue, {state->sOutputName}, pos, *v))
|
||||
break;
|
||||
auto outputName = state->forceString(*v);
|
||||
|
||||
if (!attr) continue;
|
||||
auto drvInfo = DerivationInfo{
|
||||
state->store->parseStorePath(drvPath),
|
||||
state->store->maybeParseStorePath(outPath),
|
||||
outputName
|
||||
};
|
||||
|
||||
if (!attr->isDerivation())
|
||||
throw Error("flake output attribute '%s' is not a derivation", attrPath);
|
||||
|
||||
auto drvPath = attr->forceDerivation();
|
||||
|
||||
auto drvInfo = DerivationInfo{
|
||||
std::move(drvPath),
|
||||
state->store->maybeParseStorePath(attr->getAttr(state->sOutPath)->getString()),
|
||||
attr->getAttr(state->sOutputName)->getString()
|
||||
};
|
||||
|
||||
return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
|
||||
return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
|
||||
} catch (AttrPathNotFound & e) {
|
||||
}
|
||||
}
|
||||
|
||||
throw Error("flake '%s' does not provide attribute %s",
|
||||
@@ -561,25 +547,22 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||
return res;
|
||||
}
|
||||
|
||||
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
||||
std::vector<std::tuple<Value *, Pos, std::string>>
|
||||
InstallableFlake::toValues(EvalState & state)
|
||||
{
|
||||
auto lockedFlake = getLockedFlake();
|
||||
|
||||
auto vOutputs = getFlakeOutputs(state, *lockedFlake);
|
||||
|
||||
auto vOutputs = getFlakeOutputs(state, *getLockedFlake());
|
||||
auto emptyArgs = state.allocBindings(0);
|
||||
|
||||
std::vector<std::tuple<Value *, Pos, std::string>> res;
|
||||
|
||||
for (auto & attrPath : getActualAttrPaths()) {
|
||||
try {
|
||||
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
|
||||
state.forceValue(*v);
|
||||
return {v, pos};
|
||||
} catch (AttrPathNotFound & e) {
|
||||
}
|
||||
res.push_back({v, pos, attrPath});
|
||||
} catch (Error &) {}
|
||||
}
|
||||
|
||||
throw Error("flake '%s' does not provide attribute %s",
|
||||
flakeRef, showAttrPaths(getActualAttrPaths()));
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||
@@ -602,10 +585,10 @@ InstallableFlake::getCursors(EvalState & state)
|
||||
|
||||
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
||||
{
|
||||
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||
lockFlagsApplyConfig.applyNixConfig = true;
|
||||
if (!_lockedFlake) {
|
||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags));
|
||||
_lockedFlake->flake.config.apply();
|
||||
// FIXME: send new config to the daemon.
|
||||
}
|
||||
return _lockedFlake;
|
||||
}
|
||||
@@ -654,17 +637,6 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||
for (auto & s : ss) {
|
||||
std::exception_ptr ex;
|
||||
|
||||
if (s.find('/') != std::string::npos) {
|
||||
try {
|
||||
result.push_back(std::make_shared<InstallableStorePath>(store, store->followLinksToStorePath(s)));
|
||||
continue;
|
||||
} catch (BadStorePath &) {
|
||||
} catch (...) {
|
||||
if (!ex)
|
||||
ex = std::current_exception();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath("."));
|
||||
result.push_back(std::make_shared<InstallableFlake>(
|
||||
@@ -679,7 +651,25 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||
ex = std::current_exception();
|
||||
}
|
||||
|
||||
if (s.find('/') != std::string::npos) {
|
||||
try {
|
||||
result.push_back(std::make_shared<InstallableStorePath>(store, store->followLinksToStorePath(s)));
|
||||
continue;
|
||||
} catch (BadStorePath &) {
|
||||
} catch (...) {
|
||||
if (!ex)
|
||||
ex = std::current_exception();
|
||||
}
|
||||
}
|
||||
|
||||
std::rethrow_exception(ex);
|
||||
|
||||
/*
|
||||
throw Error(
|
||||
pathExists(s)
|
||||
? "path '%s' is not a flake or a store path"
|
||||
: "don't know how to handle argument '%s'", s);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,27 +684,28 @@ std::shared_ptr<Installable> SourceExprCommand::parseInstallable(
|
||||
return installables.front();
|
||||
}
|
||||
|
||||
BuiltPaths getBuiltPaths(ref<Store> evalStore, ref<Store> store, const DerivedPaths & hopefullyBuiltPaths)
|
||||
BuiltPaths getBuiltPaths(ref<Store> store, DerivedPaths hopefullyBuiltPaths)
|
||||
{
|
||||
BuiltPaths res;
|
||||
for (const auto & b : hopefullyBuiltPaths)
|
||||
for (auto& b : hopefullyBuiltPaths)
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
[&](DerivedPath::Opaque bo) {
|
||||
res.push_back(BuiltPath::Opaque{bo.path});
|
||||
},
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
[&](DerivedPath::Built bfd) {
|
||||
OutputPathMap outputs;
|
||||
auto drv = evalStore->readDerivation(bfd.drvPath);
|
||||
auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
|
||||
auto drv = store->readDerivation(bfd.drvPath);
|
||||
auto outputHashes = staticOutputHashes(*store, drv);
|
||||
auto drvOutputs = drv.outputsAndOptPaths(*store);
|
||||
for (auto & output : bfd.outputs) {
|
||||
for (auto& output : bfd.outputs) {
|
||||
if (!outputHashes.count(output))
|
||||
throw Error(
|
||||
"the derivation '%s' doesn't have an output named '%s'",
|
||||
"the derivation '%s' doesn't have an output "
|
||||
"named '%s'",
|
||||
store->printStorePath(bfd.drvPath), output);
|
||||
if (settings.isExperimentalFeatureEnabled(
|
||||
Xp::CaDerivations)) {
|
||||
"ca-derivations")) {
|
||||
auto outputId =
|
||||
DrvOutput{outputHashes.at(output), output};
|
||||
auto realisation =
|
||||
@@ -722,7 +713,7 @@ BuiltPaths getBuiltPaths(ref<Store> evalStore, ref<Store> store, const DerivedPa
|
||||
if (!realisation)
|
||||
throw Error(
|
||||
"cannot operate on an output of unbuilt "
|
||||
"content-addressed derivation '%s'",
|
||||
"content-addresed derivation '%s'",
|
||||
outputId.to_string());
|
||||
outputs.insert_or_assign(
|
||||
output, realisation->outPath);
|
||||
@@ -743,12 +734,8 @@ BuiltPaths getBuiltPaths(ref<Store> evalStore, ref<Store> store, const DerivedPa
|
||||
return res;
|
||||
}
|
||||
|
||||
BuiltPaths build(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
const std::vector<std::shared_ptr<Installable>> & installables,
|
||||
BuildMode bMode)
|
||||
BuiltPaths build(ref<Store> store, Realise mode,
|
||||
std::vector<std::shared_ptr<Installable>> installables, BuildMode bMode)
|
||||
{
|
||||
if (mode == Realise::Nothing)
|
||||
settings.readOnlyMode = true;
|
||||
@@ -760,24 +747,23 @@ BuiltPaths build(
|
||||
pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end());
|
||||
}
|
||||
|
||||
if (mode == Realise::Nothing || mode == Realise::Derivation)
|
||||
if (mode == Realise::Nothing)
|
||||
printMissing(store, pathsToBuild, lvlError);
|
||||
else if (mode == Realise::Outputs)
|
||||
store->buildPaths(pathsToBuild, bMode, evalStore);
|
||||
store->buildPaths(pathsToBuild, bMode);
|
||||
|
||||
return getBuiltPaths(evalStore, store, pathsToBuild);
|
||||
return getBuiltPaths(store, pathsToBuild);
|
||||
}
|
||||
|
||||
BuiltPaths toBuiltPaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
const std::vector<std::shared_ptr<Installable>> & installables)
|
||||
std::vector<std::shared_ptr<Installable>> installables)
|
||||
{
|
||||
if (operateOn == OperateOn::Output)
|
||||
return build(evalStore, store, mode, installables);
|
||||
else {
|
||||
if (operateOn == OperateOn::Output) {
|
||||
return build(store, mode, installables);
|
||||
} else {
|
||||
if (mode == Realise::Nothing)
|
||||
settings.readOnlyMode = true;
|
||||
|
||||
@@ -788,27 +774,23 @@ BuiltPaths toBuiltPaths(
|
||||
}
|
||||
}
|
||||
|
||||
StorePathSet toStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
StorePathSet toStorePaths(ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
const std::vector<std::shared_ptr<Installable>> & installables)
|
||||
std::vector<std::shared_ptr<Installable>> installables)
|
||||
{
|
||||
StorePathSet outPaths;
|
||||
for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) {
|
||||
for (auto & path : toBuiltPaths(store, mode, operateOn, installables)) {
|
||||
auto thisOutPaths = path.outPaths();
|
||||
outPaths.insert(thisOutPaths.begin(), thisOutPaths.end());
|
||||
}
|
||||
return outPaths;
|
||||
}
|
||||
|
||||
StorePath toStorePath(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
StorePath toStorePath(ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
std::shared_ptr<Installable> installable)
|
||||
{
|
||||
auto paths = toStorePaths(evalStore, store, mode, operateOn, {installable});
|
||||
auto paths = toStorePaths(store, mode, operateOn, {installable});
|
||||
|
||||
if (paths.size() != 1)
|
||||
throw Error("argument '%s' should evaluate to one store path", installable->what());
|
||||
@@ -816,17 +798,15 @@ StorePath toStorePath(
|
||||
return *paths.begin();
|
||||
}
|
||||
|
||||
StorePathSet toDerivations(
|
||||
ref<Store> store,
|
||||
const std::vector<std::shared_ptr<Installable>> & installables,
|
||||
bool useDeriver)
|
||||
StorePathSet toDerivations(ref<Store> store,
|
||||
std::vector<std::shared_ptr<Installable>> installables, bool useDeriver)
|
||||
{
|
||||
StorePathSet drvPaths;
|
||||
|
||||
for (const auto & i : installables)
|
||||
for (const auto & b : i->toDerivedPaths())
|
||||
for (auto & i : installables)
|
||||
for (auto & b : i->toDerivedPaths())
|
||||
std::visit(overloaded {
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
[&](DerivedPath::Opaque bo) {
|
||||
if (!useDeriver)
|
||||
throw Error("argument '%s' did not evaluate to a derivation", i->what());
|
||||
auto derivers = store->queryValidDerivers(bo.path);
|
||||
@@ -835,7 +815,7 @@ StorePathSet toDerivations(
|
||||
// FIXME: use all derivers?
|
||||
drvPaths.insert(*derivers.begin());
|
||||
},
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
[&](DerivedPath::Built bfd) {
|
||||
drvPaths.insert(bfd.drvPath);
|
||||
},
|
||||
}, b.raw());
|
||||
|
||||
@@ -26,7 +26,7 @@ struct App
|
||||
struct UnresolvedApp
|
||||
{
|
||||
App unresolved;
|
||||
App resolve(ref<Store> evalStore, ref<Store> store);
|
||||
App resolve(ref<Store>);
|
||||
};
|
||||
|
||||
struct Installable
|
||||
@@ -41,10 +41,11 @@ struct Installable
|
||||
|
||||
UnresolvedApp toApp(EvalState & state);
|
||||
|
||||
virtual std::pair<Value *, Pos> toValue(EvalState & state)
|
||||
virtual std::vector<std::tuple<Value *, Pos, std::string>> toValues(EvalState & state)
|
||||
{
|
||||
throw Error("argument '%s' cannot be evaluated", what());
|
||||
}
|
||||
std::pair<Value *, Pos> toValue(EvalState & state);
|
||||
|
||||
/* Return a value only if this installable is a store path or a
|
||||
symlink to it. */
|
||||
@@ -109,7 +110,7 @@ struct InstallableFlake : InstallableValue
|
||||
|
||||
std::vector<DerivationInfo> toDerivations() override;
|
||||
|
||||
std::pair<Value *, Pos> toValue(EvalState & state) override;
|
||||
std::vector<std::tuple<Value *, Pos, std::string>> toValues(EvalState & state) override;
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||
getCursors(EvalState & state) override;
|
||||
|
||||
@@ -8,8 +8,8 @@ libcmd_SOURCES := $(wildcard $(d)/*.cc)
|
||||
|
||||
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
|
||||
|
||||
libcmd_LDFLAGS += -llowdown -pthread
|
||||
libcmd_LDFLAGS = -llowdown
|
||||
|
||||
libcmd_LIBS = libstore libutil libexpr libmain libfetchers
|
||||
|
||||
$(eval $(call install-file-in, $(d)/nix-cmd.pc, $(libdir)/pkgconfig, 0644))
|
||||
$(eval $(call install-file-in, $(d)/nix-cmd.pc, $(prefix)/lib/pkgconfig, 0644))
|
||||
|
||||
@@ -12,7 +12,7 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
|
||||
struct lowdown_opts opts {
|
||||
.type = LOWDOWN_TERM,
|
||||
.maxdepth = 20,
|
||||
.cols = std::max(getWindowSize().second, (unsigned short) 80),
|
||||
.cols = std::min(getWindowSize().second, (unsigned short) 80),
|
||||
.hmargin = 0,
|
||||
.vmargin = 0,
|
||||
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
|
||||
@@ -25,7 +25,7 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
|
||||
Finally freeDoc([&]() { lowdown_doc_free(doc); });
|
||||
|
||||
size_t maxn = 0;
|
||||
auto node = lowdown_doc_parse(doc, &maxn, markdown.data(), markdown.size(), nullptr);
|
||||
auto node = lowdown_doc_parse(doc, &maxn, markdown.data(), markdown.size());
|
||||
if (!node)
|
||||
throw Error("cannot parse Markdown document");
|
||||
Finally freeNode([&]() { lowdown_node_free(node); });
|
||||
@@ -40,11 +40,11 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
|
||||
throw Error("cannot allocate Markdown output buffer");
|
||||
Finally freeBuffer([&]() { lowdown_buf_free(buf); });
|
||||
|
||||
int rndr_res = lowdown_term_rndr(buf, renderer, node);
|
||||
int rndr_res = lowdown_term_rndr(buf, nullptr, renderer, node);
|
||||
if (!rndr_res)
|
||||
throw Error("allocation error while rendering Markdown");
|
||||
|
||||
return filterANSIEscapes(std::string(buf->data, buf->size), !shouldANSI());
|
||||
return std::string(buf->data, buf->size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ static Strings parseAttrPath(std::string_view s)
|
||||
++i;
|
||||
while (1) {
|
||||
if (i == s.end())
|
||||
throw ParseError("missing closing quote in selection path '%1%'", s);
|
||||
throw Error("missing closing quote in selection path '%1%'", s);
|
||||
if (*i == '"') break;
|
||||
cur.push_back(*i++);
|
||||
}
|
||||
@@ -58,26 +58,23 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||
Value * vNew = state.allocValue();
|
||||
state.autoCallFunction(autoArgs, *v, *vNew);
|
||||
v = vNew;
|
||||
state.forceValue(*v);
|
||||
|
||||
/* It should evaluate to either a set or an expression,
|
||||
according to what is specified in the attrPath. */
|
||||
|
||||
if (!attrIndex) {
|
||||
|
||||
if (v->type() != nAttrs)
|
||||
throw TypeError(
|
||||
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||
attrPath,
|
||||
showType(*v));
|
||||
if (attr.empty())
|
||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||
|
||||
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
||||
if (a == v->attrs->end())
|
||||
auto v2 = state.allocValue();
|
||||
auto gotField = state.lazyGetAttrField(*v, {state.symbols.create(attr)}, pos, *v2) != EvalState::LazyValueType::Missing;
|
||||
if (!gotField)
|
||||
throw AttrPathNotFound("attribute '%1%' in selection path '%2%' not found", attr, attrPath);
|
||||
v = &*a->value;
|
||||
pos = *a->pos;
|
||||
v = v2;
|
||||
/* Bindings::iterator a = v->attrs->find(state.symbols.create(attr)); */
|
||||
/* v = &*a->value; */
|
||||
/* pos = *a->pos; */
|
||||
}
|
||||
|
||||
else {
|
||||
@@ -100,7 +97,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||
}
|
||||
|
||||
|
||||
Pos findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||
Pos findDerivationFilename(EvalState & state, Value & v, std::string what)
|
||||
{
|
||||
Value * v2;
|
||||
try {
|
||||
@@ -116,14 +113,14 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||
|
||||
auto colon = pos.rfind(':');
|
||||
if (colon == std::string::npos)
|
||||
throw ParseError("cannot parse meta.position attribute '%s'", pos);
|
||||
throw Error("cannot parse meta.position attribute '%s'", pos);
|
||||
|
||||
std::string filename(pos, 0, colon);
|
||||
unsigned int lineno;
|
||||
try {
|
||||
lineno = std::stoi(std::string(pos, colon + 1));
|
||||
} catch (std::invalid_argument & e) {
|
||||
throw ParseError("cannot parse line number '%s'", pos);
|
||||
throw Error("cannot parse line number '%s'", pos);
|
||||
}
|
||||
|
||||
Symbol file = state.symbols.create(filename);
|
||||
|
||||
@@ -14,7 +14,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||
Bindings & autoArgs, Value & vIn);
|
||||
|
||||
/* Heuristic to find the filename and lineno or a nix value. */
|
||||
Pos findPackageFilename(EvalState & state, Value & v, std::string what);
|
||||
Pos findDerivationFilename(EvalState & state, Value & v, std::string what);
|
||||
|
||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "nixexpr.hh"
|
||||
#include "symbol-table.hh"
|
||||
#include "value-cache.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
@@ -17,8 +18,8 @@ struct Attr
|
||||
{
|
||||
Symbol name;
|
||||
Value * value;
|
||||
ptr<Pos> pos;
|
||||
Attr(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
|
||||
Pos * pos;
|
||||
Attr(Symbol name, Value * value, Pos * pos = &noPos)
|
||||
: name(name), value(value), pos(pos) { };
|
||||
Attr() : pos(&noPos) { };
|
||||
bool operator < (const Attr & a) const
|
||||
@@ -35,13 +36,14 @@ class Bindings
|
||||
{
|
||||
public:
|
||||
typedef uint32_t size_t;
|
||||
ptr<Pos> pos;
|
||||
Pos *pos;
|
||||
ValueCache eval_cache;
|
||||
|
||||
private:
|
||||
size_t size_, capacity_;
|
||||
Attr attrs[0];
|
||||
|
||||
Bindings(size_t capacity) : pos(&noPos), size_(0), capacity_(capacity) { }
|
||||
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
|
||||
Bindings(const Bindings & bindings) = delete;
|
||||
|
||||
public:
|
||||
|
||||
@@ -61,14 +61,6 @@ MixEvalArgs::MixEvalArgs()
|
||||
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
|
||||
}}
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "eval-store",
|
||||
.description = "The Nix store to use for evaluations.",
|
||||
.category = category,
|
||||
.labels = {"store-url"},
|
||||
.handler = {&evalStoreUrl},
|
||||
});
|
||||
}
|
||||
|
||||
Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
||||
|
||||
@@ -16,9 +16,8 @@ struct MixEvalArgs : virtual Args
|
||||
|
||||
Strings searchPath;
|
||||
|
||||
std::optional<std::string> evalStoreUrl;
|
||||
|
||||
private:
|
||||
|
||||
std::map<std::string, std::string> autoArgs;
|
||||
};
|
||||
|
||||
|
||||
21
src/libexpr/context.cc
Normal file
21
src/libexpr/context.cc
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "context.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
||||
name>. */
|
||||
std::pair<string, string> decodeContext(std::string_view s)
|
||||
{
|
||||
if (s.at(0) == '!') {
|
||||
size_t index = s.find("!", 1);
|
||||
return {std::string(s.substr(index + 1)), std::string(s.substr(1, index - 1))};
|
||||
} else
|
||||
return {s.at(0) == '/' ? std::string(s) : std::string(s.substr(1)), ""};
|
||||
}
|
||||
|
||||
std::string encodeContext(std::string_view name, std::string_view path)
|
||||
{
|
||||
return "!" + std::string(name) + "!" + std::string(path);
|
||||
}
|
||||
|
||||
}
|
||||
11
src/libexpr/context.hh
Normal file
11
src/libexpr/context.hh
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "util.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
||||
name>. */
|
||||
std::pair<string, string> decodeContext(std::string_view s);
|
||||
|
||||
std::string encodeContext(std::string_view name, std::string_view path);
|
||||
|
||||
}
|
||||
@@ -46,6 +46,12 @@ void EvalState::forceValue(Value & v, const Pos & pos)
|
||||
}
|
||||
else if (v.isApp())
|
||||
callFunction(*v.app.left, *v.app.right, v, noPos);
|
||||
else if (v.isCachedThunk()) {
|
||||
auto evalCache = v.getEvalCache();
|
||||
v.mkThunk(v.cachedThunk.thunk->env, v.cachedThunk.thunk->expr);
|
||||
forceValue(v, pos);
|
||||
v.setEvalCache(evalCache);
|
||||
}
|
||||
else if (v.isBlackhole())
|
||||
throwEvalError(pos, "infinite recursion encountered");
|
||||
}
|
||||
|
||||
1020
src/libexpr/eval.cc
1020
src/libexpr/eval.cc
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "attr-set.hh"
|
||||
#include "context.hh"
|
||||
#include "value.hh"
|
||||
#include "nixexpr.hh"
|
||||
#include "symbol-table.hh"
|
||||
#include "config.hh"
|
||||
#include "experimental-features.hh"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -95,14 +95,8 @@ public:
|
||||
|
||||
Value vEmptySet;
|
||||
|
||||
/* Store used to materialise .drv files. */
|
||||
const ref<Store> store;
|
||||
|
||||
/* Store used to build stuff. */
|
||||
const ref<Store> buildStore;
|
||||
|
||||
RootValue vCallFlake = nullptr;
|
||||
RootValue vImportedDrvToDerivation = nullptr;
|
||||
|
||||
private:
|
||||
SrcToStore srcToStore;
|
||||
@@ -122,6 +116,7 @@ private:
|
||||
typedef std::map<Path, Value> FileEvalCache;
|
||||
#endif
|
||||
FileEvalCache fileEvalCache;
|
||||
std::map<Hash, std::shared_ptr<tree_cache::Cache>> evalCache;
|
||||
|
||||
SearchPath searchPath;
|
||||
|
||||
@@ -135,31 +130,15 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
EvalState(
|
||||
const Strings & _searchPath,
|
||||
ref<Store> store,
|
||||
std::shared_ptr<Store> buildStore = nullptr);
|
||||
EvalState(const Strings & _searchPath, ref<Store> store);
|
||||
~EvalState();
|
||||
|
||||
void requireExperimentalFeatureOnEvaluation(
|
||||
const ExperimentalFeature &,
|
||||
const std::string_view fName,
|
||||
const Pos & pos
|
||||
);
|
||||
std::shared_ptr<tree_cache::Cache> openTreeCache(Hash);
|
||||
|
||||
void addToSearchPath(const string & s);
|
||||
|
||||
SearchPath getSearchPath() { return searchPath; }
|
||||
|
||||
/* Allow access to a path. */
|
||||
void allowPath(const Path & path);
|
||||
|
||||
/* Allow access to a store path. Note that this gets remapped to
|
||||
the real store path if `store` is a chroot store. */
|
||||
void allowPath(const StorePath & storePath);
|
||||
|
||||
/* Check whether access to a path is allowed and throw an error if
|
||||
not. Otherwise return the canonicalised path. */
|
||||
Path checkSourcePath(const Path & path);
|
||||
|
||||
void checkURI(const std::string & uri);
|
||||
@@ -188,14 +167,6 @@ public:
|
||||
trivial (i.e. doesn't require arbitrary computation). */
|
||||
void evalFile(const Path & path, Value & v, bool mustBeTrivial = false);
|
||||
|
||||
/* Like `cacheFile`, but with an already parsed expression. */
|
||||
void cacheFile(
|
||||
const Path & path,
|
||||
const Path & resolvedPath,
|
||||
Expr * e,
|
||||
Value & v,
|
||||
bool mustBeTrivial = false);
|
||||
|
||||
void resetFileCache();
|
||||
|
||||
/* Look up a file in the search path. */
|
||||
@@ -250,8 +221,7 @@ public:
|
||||
booleans and lists to a string. If `copyToStore' is set,
|
||||
referenced paths are copied to the Nix store as a side effect. */
|
||||
string coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||
bool coerceMore = false, bool copyToStore = true,
|
||||
bool canonicalizePath = true);
|
||||
bool coerceMore = false, bool copyToStore = true);
|
||||
|
||||
string copyPathToStore(PathSet & context, const Path & path);
|
||||
|
||||
@@ -277,8 +247,6 @@ private:
|
||||
|
||||
Value * addConstant(const string & name, Value & v);
|
||||
|
||||
void addConstant(const string & name, Value * v);
|
||||
|
||||
Value * addPrimOp(const string & name,
|
||||
size_t arity, PrimOpFun primOp);
|
||||
|
||||
@@ -318,14 +286,8 @@ public:
|
||||
|
||||
bool isFunctor(Value & fun);
|
||||
|
||||
// FIXME: use std::span
|
||||
void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos);
|
||||
|
||||
void callFunction(Value & fun, Value & arg, Value & vRes, const Pos & pos)
|
||||
{
|
||||
Value * args[] = {&arg};
|
||||
callFunction(fun, 1, args, vRes, pos);
|
||||
}
|
||||
void callFunction(Value & fun, Value & arg, Value & v, const Pos & pos);
|
||||
void callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos);
|
||||
|
||||
/* Automatically call a function for which each argument has a
|
||||
default value or has a binding in the `args' map. */
|
||||
@@ -343,7 +305,7 @@ public:
|
||||
void mkList(Value & v, size_t length);
|
||||
void mkAttrs(Value & v, size_t capacity);
|
||||
void mkThunk_(Value & v, Expr * expr);
|
||||
void mkPos(Value & v, ptr<Pos> pos);
|
||||
void mkPos(Value & v, Pos * pos);
|
||||
|
||||
void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos);
|
||||
|
||||
@@ -352,21 +314,25 @@ public:
|
||||
|
||||
void realiseContext(const PathSet & context);
|
||||
|
||||
void updateCacheStats(ValueCache::CacheResult);
|
||||
|
||||
private:
|
||||
|
||||
unsigned long nrEnvs = 0;
|
||||
unsigned long nrValuesInEnvs = 0;
|
||||
unsigned long nrValues = 0;
|
||||
unsigned long nrListElems = 0;
|
||||
unsigned long nrLookups = 0;
|
||||
unsigned long nrAttrsets = 0;
|
||||
unsigned long nrAttrsInAttrsets = 0;
|
||||
unsigned long nrAvoided = 0;
|
||||
unsigned long nrOpUpdates = 0;
|
||||
unsigned long nrOpUpdateValuesCopied = 0;
|
||||
unsigned long nrListConcats = 0;
|
||||
unsigned long nrPrimOpCalls = 0;
|
||||
unsigned long nrFunctionCalls = 0;
|
||||
unsigned long nrCacheHits = 0;
|
||||
unsigned long nrCacheMisses = 0;
|
||||
unsigned long nrUncacheable = 0;
|
||||
unsigned long nrUncached = 0;
|
||||
|
||||
bool countCalls;
|
||||
|
||||
@@ -383,14 +349,30 @@ private:
|
||||
|
||||
friend struct ExprOpUpdate;
|
||||
friend struct ExprOpConcatLists;
|
||||
friend struct ExprVar;
|
||||
friend struct ExprString;
|
||||
friend struct ExprInt;
|
||||
friend struct ExprFloat;
|
||||
friend struct ExprPath;
|
||||
friend struct ExprSelect;
|
||||
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
|
||||
public:
|
||||
|
||||
bool getAttrField(Value & attrs, const std::vector<Symbol> & selector, const Pos & pos, Value & dest);
|
||||
|
||||
enum struct LazyValueType {
|
||||
PlainValue,
|
||||
DelayedUncacheable,
|
||||
DelayedAttr,
|
||||
Missing,
|
||||
};
|
||||
|
||||
// Similar to `getAttrField`, but if the cache says that the result is an
|
||||
// attribute set, just return a thunk to it rather than forcing it.
|
||||
LazyValueType lazyGetAttrField(Value & attrs, const std::vector<Symbol> & selector, const Pos & pos, Value & dest);
|
||||
|
||||
// Similar to `getAttrField`, but throws an `Error` if the field can’t be
|
||||
// found
|
||||
void getAttrFieldThrow(Value & attrs, const std::vector<Symbol> & selector, const Pos & pos, Value & dest);
|
||||
|
||||
std::vector<Symbol> getFields(Value & attrs, const Pos & pos);
|
||||
};
|
||||
|
||||
|
||||
@@ -398,10 +380,6 @@ private:
|
||||
string showType(ValueType type);
|
||||
string showType(const Value & v);
|
||||
|
||||
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
||||
name>. */
|
||||
std::pair<string, string> decodeContext(std::string_view s);
|
||||
|
||||
/* If `path' refers to a directory, then append "/default.nix". */
|
||||
Path resolveExprPath(Path path);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ static void writeTrustedList(const TrustedList & trustedList)
|
||||
|
||||
void ConfigFile::apply()
|
||||
{
|
||||
std::set<std::string> whitelist{"bash-prompt", "bash-prompt-suffix", "flake-registry"};
|
||||
std::set<std::string> whitelist{"bash-prompt", "bash-prompt-suffix"};
|
||||
|
||||
for (auto & [name, value] : settings) {
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "flake.hh"
|
||||
#include "eval.hh"
|
||||
#include "lockfile.hh"
|
||||
#include "primops.hh"
|
||||
#include "eval-inline.hh"
|
||||
@@ -64,7 +63,8 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||
debug("got tree '%s' from '%s'",
|
||||
state.store->printStorePath(tree.storePath), lockedRef);
|
||||
|
||||
state.allowPath(tree.storePath);
|
||||
if (state.allowedPaths)
|
||||
state.allowedPaths->insert(tree.actualPath);
|
||||
|
||||
assert(!originalRef.input.getNarHash() || tree.storePath == originalRef.input.computeStorePath(*state.store));
|
||||
|
||||
@@ -88,12 +88,10 @@ static void expectType(EvalState & state, ValueType type,
|
||||
}
|
||||
|
||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
EvalState & state, Value * value, const Pos & pos,
|
||||
const std::optional<Path> & baseDir);
|
||||
EvalState & state, Value * value, const Pos & pos);
|
||||
|
||||
static FlakeInput parseFlakeInput(EvalState & state,
|
||||
const std::string & inputName, Value * value, const Pos & pos,
|
||||
const std::optional<Path> & baseDir)
|
||||
const std::string & inputName, Value * value, const Pos & pos)
|
||||
{
|
||||
expectType(state, nAttrs, *value, pos);
|
||||
|
||||
@@ -117,7 +115,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
expectType(state, nBool, *attr.value, *attr.pos);
|
||||
input.isFlake = attr.value->boolean;
|
||||
} else if (attr.name == sInputs) {
|
||||
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir);
|
||||
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos);
|
||||
} else if (attr.name == sFollows) {
|
||||
expectType(state, nString, *attr.value, *attr.pos);
|
||||
input.follows = parseInputPath(attr.value->string.s);
|
||||
@@ -155,7 +153,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
if (!attrs.empty())
|
||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
|
||||
if (url)
|
||||
input.ref = parseFlakeRef(*url, baseDir, true);
|
||||
input.ref = parseFlakeRef(*url, {}, true);
|
||||
}
|
||||
|
||||
if (!input.follows && !input.ref)
|
||||
@@ -165,8 +163,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
}
|
||||
|
||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
EvalState & state, Value * value, const Pos & pos,
|
||||
const std::optional<Path> & baseDir)
|
||||
EvalState & state, Value * value, const Pos & pos)
|
||||
{
|
||||
std::map<FlakeId, FlakeInput> inputs;
|
||||
|
||||
@@ -177,8 +174,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
parseFlakeInput(state,
|
||||
inputAttr.name,
|
||||
inputAttr.value,
|
||||
*inputAttr.pos,
|
||||
baseDir));
|
||||
*inputAttr.pos));
|
||||
}
|
||||
|
||||
return inputs;
|
||||
@@ -194,8 +190,7 @@ static Flake getFlake(
|
||||
state, originalRef, allowLookup, flakeCache);
|
||||
|
||||
// Guard against symlink attacks.
|
||||
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir);
|
||||
auto flakeFile = canonPath(flakeDir + "/flake.nix");
|
||||
auto flakeFile = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir + "/flake.nix");
|
||||
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
||||
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
||||
lockedRef, state.store->printStorePath(sourceInfo.storePath));
|
||||
@@ -223,20 +218,15 @@ static Flake getFlake(
|
||||
auto sInputs = state.symbols.create("inputs");
|
||||
|
||||
if (auto inputs = vInfo.attrs->get(sInputs))
|
||||
flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir);
|
||||
flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos);
|
||||
|
||||
auto sOutputs = state.symbols.create("outputs");
|
||||
|
||||
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
||||
expectType(state, nFunction, *outputs->value, *outputs->pos);
|
||||
|
||||
if (outputs->value->lambda.fun->args.size() != 1)
|
||||
throw Error("the 'outputs' attribute of flake '%s' is not a unary function", lockedRef);
|
||||
|
||||
auto & arg = outputs->value->lambda.fun->args[0];
|
||||
|
||||
if (arg.formals) {
|
||||
for (auto & formal : arg.formals->formals) {
|
||||
if (outputs->value->isLambda() && outputs->value->lambda.fun->matchAttrs) {
|
||||
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
||||
if (formal.name != state.sSelf)
|
||||
flake.inputs.emplace(formal.name, FlakeInput {
|
||||
.ref = parseFlakeRef(formal.name)
|
||||
@@ -302,18 +292,11 @@ LockedFlake lockFlake(
|
||||
const FlakeRef & topRef,
|
||||
const LockFlags & lockFlags)
|
||||
{
|
||||
settings.requireExperimentalFeature(Xp::Flakes);
|
||||
settings.requireExperimentalFeature("flakes");
|
||||
|
||||
FlakeCache flakeCache;
|
||||
|
||||
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
|
||||
|
||||
auto flake = getFlake(state, topRef, useRegistries, flakeCache);
|
||||
|
||||
if (lockFlags.applyNixConfig) {
|
||||
flake.config.apply();
|
||||
// FIXME: send new config to the daemon.
|
||||
}
|
||||
auto flake = getFlake(state, topRef, lockFlags.useRegistries, flakeCache);
|
||||
|
||||
try {
|
||||
|
||||
@@ -334,38 +317,25 @@ LockedFlake lockFlake(
|
||||
|
||||
std::vector<FlakeRef> parents;
|
||||
|
||||
struct LockParent {
|
||||
/* The path to this parent. */
|
||||
InputPath path;
|
||||
|
||||
/* Whether we are currently inside a top-level lockfile
|
||||
(inputs absolute) or subordinate lockfile (inputs
|
||||
relative). */
|
||||
bool absolute;
|
||||
};
|
||||
|
||||
std::function<void(
|
||||
const FlakeInputs & flakeInputs,
|
||||
std::shared_ptr<Node> node,
|
||||
const InputPath & inputPathPrefix,
|
||||
std::shared_ptr<const Node> oldNode,
|
||||
const LockParent & parent,
|
||||
const Path & parentPath)>
|
||||
std::shared_ptr<const Node> oldNode)>
|
||||
computeLocks;
|
||||
|
||||
computeLocks = [&](
|
||||
const FlakeInputs & flakeInputs,
|
||||
std::shared_ptr<Node> node,
|
||||
const InputPath & inputPathPrefix,
|
||||
std::shared_ptr<const Node> oldNode,
|
||||
const LockParent & parent,
|
||||
const Path & parentPath)
|
||||
std::shared_ptr<const Node> oldNode)
|
||||
{
|
||||
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
|
||||
|
||||
/* Get the overrides (i.e. attributes of the form
|
||||
'inputs.nixops.inputs.nixpkgs.url = ...'). */
|
||||
for (auto & [id, input] : flakeInputs) {
|
||||
// FIXME: check this
|
||||
for (auto & [id, input] : flake.inputs) {
|
||||
for (auto & [idOverride, inputOverride] : input.overrides) {
|
||||
auto inputPath(inputPathPrefix);
|
||||
inputPath.push_back(id);
|
||||
@@ -389,31 +359,22 @@ LockedFlake lockFlake(
|
||||
ancestors? */
|
||||
auto i = overrides.find(inputPath);
|
||||
bool hasOverride = i != overrides.end();
|
||||
if (hasOverride) {
|
||||
overridesUsed.insert(inputPath);
|
||||
// Respect the “flakeness” of the input even if we
|
||||
// override it
|
||||
i->second.isFlake = input2.isFlake;
|
||||
}
|
||||
if (hasOverride) overridesUsed.insert(inputPath);
|
||||
auto & input = hasOverride ? i->second : input2;
|
||||
|
||||
/* Resolve 'follows' later (since it may refer to an input
|
||||
path we haven't processed yet. */
|
||||
if (input.follows) {
|
||||
InputPath target;
|
||||
|
||||
if (parent.absolute && !hasOverride) {
|
||||
if (hasOverride || input.absolute)
|
||||
/* 'follows' from an override is relative to the
|
||||
root of the graph. */
|
||||
target = *input.follows;
|
||||
} else {
|
||||
if (hasOverride) {
|
||||
target = inputPathPrefix;
|
||||
target.pop_back();
|
||||
} else
|
||||
target = parent.path;
|
||||
|
||||
else {
|
||||
/* Otherwise, it's relative to the current flake. */
|
||||
target = inputPathPrefix;
|
||||
for (auto & i : *input.follows) target.push_back(i);
|
||||
}
|
||||
|
||||
debug("input '%s' follows '%s'", inputPathS, printInputPath(target));
|
||||
node->inputs.insert_or_assign(id, target);
|
||||
continue;
|
||||
@@ -459,7 +420,7 @@ LockedFlake lockFlake(
|
||||
if (hasChildUpdate) {
|
||||
auto inputFlake = getFlake(
|
||||
state, oldLock->lockedRef, false, flakeCache);
|
||||
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, parent, parentPath);
|
||||
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock);
|
||||
} else {
|
||||
/* No need to fetch this flake, we can be
|
||||
lazy. However there may be new overrides on the
|
||||
@@ -476,11 +437,12 @@ LockedFlake lockFlake(
|
||||
} else if (auto follows = std::get_if<1>(&i.second)) {
|
||||
fakeInputs.emplace(i.first, FlakeInput {
|
||||
.follows = *follows,
|
||||
.absolute = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
computeLocks(fakeInputs, childNode, inputPath, oldLock, parent, parentPath);
|
||||
computeLocks(fakeInputs, childNode, inputPath, oldLock);
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -492,15 +454,7 @@ LockedFlake lockFlake(
|
||||
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
|
||||
|
||||
if (input.isFlake) {
|
||||
Path localPath = parentPath;
|
||||
FlakeRef localRef = *input.ref;
|
||||
|
||||
// If this input is a path, recurse it down.
|
||||
// This allows us to resolve path inputs relative to the current flake.
|
||||
if (localRef.input.getType() == "path")
|
||||
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
|
||||
|
||||
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache);
|
||||
auto inputFlake = getFlake(state, *input.ref, lockFlags.useRegistries, flakeCache);
|
||||
|
||||
/* Note: in case of an --override-input, we use
|
||||
the *original* ref (input2.ref) for the
|
||||
@@ -521,13 +475,6 @@ LockedFlake lockFlake(
|
||||
parents.push_back(*input.ref);
|
||||
Finally cleanup([&]() { parents.pop_back(); });
|
||||
|
||||
// Follows paths from existing inputs in the top-level lockfile are absolute,
|
||||
// whereas paths in subordinate lockfiles are relative to those lockfiles.
|
||||
LockParent newParent {
|
||||
.path = inputPath,
|
||||
.absolute = oldLock ? true : false
|
||||
};
|
||||
|
||||
/* Recursively process the inputs of this
|
||||
flake. Also, unless we already have this flake
|
||||
in the top-level lock file, use this flake's
|
||||
@@ -537,13 +484,12 @@ LockedFlake lockFlake(
|
||||
oldLock
|
||||
? std::dynamic_pointer_cast<const Node>(oldLock)
|
||||
: LockFile::read(
|
||||
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
|
||||
newParent, localPath);
|
||||
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root);
|
||||
}
|
||||
|
||||
else {
|
||||
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, *input.ref, useRegistries, flakeCache);
|
||||
state, *input.ref, lockFlags.useRegistries, flakeCache);
|
||||
node->inputs.insert_or_assign(id,
|
||||
std::make_shared<LockedNode>(lockedRef, *input.ref, false));
|
||||
}
|
||||
@@ -556,17 +502,9 @@ LockedFlake lockFlake(
|
||||
}
|
||||
};
|
||||
|
||||
LockParent parent {
|
||||
.path = {},
|
||||
.absolute = true
|
||||
};
|
||||
|
||||
// Bring in the current ref for relative path resolution if we have it
|
||||
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir);
|
||||
|
||||
computeLocks(
|
||||
flake.inputs, newLockFile.root, {},
|
||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath);
|
||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root);
|
||||
|
||||
for (auto & i : lockFlags.inputOverrides)
|
||||
if (!overridesUsed.count(i.first))
|
||||
@@ -616,8 +554,8 @@ LockedFlake lockFlake(
|
||||
topRef.input.markChangedFile(
|
||||
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
|
||||
lockFlags.commitLockFile
|
||||
? std::optional<std::string>(fmt("%s: %s\n\nFlake lock file changes:\n\n%s",
|
||||
relPath, lockFileExists ? "Update" : "Add", filterANSIEscapes(diff, true)))
|
||||
? std::optional<std::string>(fmt("%s: %s\n\nFlake input changes:\n\n%s",
|
||||
relPath, lockFileExists ? "Update" : "Add", diff))
|
||||
: std::nullopt);
|
||||
|
||||
/* Rewriting the lockfile changed the top-level
|
||||
@@ -625,7 +563,7 @@ LockedFlake lockFlake(
|
||||
also just clear the 'rev' field... */
|
||||
auto prevLockedRef = flake.lockedRef;
|
||||
FlakeCache dummyCache;
|
||||
flake = getFlake(state, topRef, useRegistries, dummyCache);
|
||||
flake = getFlake(state, topRef, lockFlags.useRegistries, dummyCache);
|
||||
|
||||
if (lockFlags.commitLockFile &&
|
||||
flake.lockedRef.input.getRev() &&
|
||||
@@ -642,10 +580,8 @@ LockedFlake lockFlake(
|
||||
}
|
||||
} else
|
||||
throw Error("cannot write modified lock file of flake '%s' (use '--no-write-lock-file' to ignore)", topRef);
|
||||
} else {
|
||||
} else
|
||||
warn("not writing modified lock file of flake '%s':\n%s", topRef, chomp(diff));
|
||||
flake.forceDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return LockedFlake { .flake = std::move(flake), .lockFile = std::move(newLockFile) };
|
||||
@@ -660,40 +596,48 @@ void callFlake(EvalState & state,
|
||||
const LockedFlake & lockedFlake,
|
||||
Value & vRes)
|
||||
{
|
||||
auto vLocks = state.allocValue();
|
||||
auto lockExpr = new ExprString(
|
||||
state.symbols.create(lockedFlake.lockFile.to_string())
|
||||
);
|
||||
auto subdirExpr = new ExprString(
|
||||
state.symbols.create(lockedFlake.flake.lockedRef.subdir)
|
||||
);
|
||||
|
||||
auto vRootSrc = state.allocValue();
|
||||
auto vRootSubdir = state.allocValue();
|
||||
auto vTmp1 = state.allocValue();
|
||||
auto vTmp2 = state.allocValue();
|
||||
emitTreeAttrs(state, *lockedFlake.flake.sourceInfo, lockedFlake.flake.lockedRef.input, *vRootSrc);
|
||||
|
||||
mkString(*vLocks, lockedFlake.lockFile.to_string());
|
||||
|
||||
emitTreeAttrs(
|
||||
state,
|
||||
*lockedFlake.flake.sourceInfo,
|
||||
lockedFlake.flake.lockedRef.input,
|
||||
*vRootSrc,
|
||||
false,
|
||||
lockedFlake.flake.forceDirty);
|
||||
|
||||
mkString(*vRootSubdir, lockedFlake.flake.lockedRef.subdir);
|
||||
|
||||
if (!state.vCallFlake) {
|
||||
state.vCallFlake = allocRootValue(state.allocValue());
|
||||
state.eval(state.parseExprFromString(
|
||||
static Expr * callFlakeExpr = nullptr;
|
||||
if (!callFlakeExpr) {
|
||||
callFlakeExpr = state.parseExprFromString(
|
||||
#include "call-flake.nix.gen.hh"
|
||||
, "/"), **state.vCallFlake);
|
||||
, "/");
|
||||
}
|
||||
|
||||
state.callFunction(**state.vCallFlake, *vLocks, *vTmp1, noPos);
|
||||
state.callFunction(*vTmp1, *vRootSrc, *vTmp2, noPos);
|
||||
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
|
||||
auto resExpr = new ExprApp(
|
||||
new ExprApp(
|
||||
new ExprApp(
|
||||
callFlakeExpr,
|
||||
lockExpr
|
||||
),
|
||||
new ExprCastedVar(vRootSrc)
|
||||
),
|
||||
subdirExpr
|
||||
);
|
||||
auto thunk = (Thunk*)allocBytes(sizeof(Thunk));
|
||||
thunk->expr = resExpr;
|
||||
thunk->env = &state.baseEnv;
|
||||
auto fingerprint = lockedFlake.getFingerprint();
|
||||
auto treeCache = state.openTreeCache(fingerprint);
|
||||
auto cacheRoot = treeCache ? treeCache->getRoot() : nullptr;
|
||||
auto evalCache = new ValueCache(cacheRoot);
|
||||
vRes.mkCachedThunk(
|
||||
thunk,
|
||||
evalCache
|
||||
);
|
||||
}
|
||||
|
||||
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
|
||||
|
||||
auto flakeRefS = state.forceStringNoCtx(*args[0], pos);
|
||||
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
||||
if (evalSettings.pureEval && !flakeRef.input.isImmutable())
|
||||
@@ -703,13 +647,13 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
lockFlake(state, flakeRef,
|
||||
LockFlags {
|
||||
.updateLockFile = false,
|
||||
.useRegistries = !evalSettings.pureEval && settings.useRegistries,
|
||||
.useRegistries = !evalSettings.pureEval,
|
||||
.allowMutable = !evalSettings.pureEval,
|
||||
}),
|
||||
v);
|
||||
}
|
||||
|
||||
static RegisterPrimOp r2("__getFlake", 1, prim_getFlake);
|
||||
static RegisterPrimOp r2("__getFlake", 1, prim_getFlake, "flakes");
|
||||
|
||||
}
|
||||
|
||||
@@ -719,9 +663,8 @@ Fingerprint LockedFlake::getFingerprint() const
|
||||
// and we haven't changed it, then it's sufficient to use
|
||||
// flake.sourceInfo.storePath for the fingerprint.
|
||||
return hashString(htSHA256,
|
||||
fmt("%s;%s;%d;%d;%s",
|
||||
fmt("%s;%d;%d;%s",
|
||||
flake.sourceInfo->storePath.to_string(),
|
||||
flake.lockedRef.subdir,
|
||||
flake.lockedRef.input.getRevCount().value_or(0),
|
||||
flake.lockedRef.input.getLastModified().value_or(0),
|
||||
lockFile));
|
||||
|
||||
@@ -43,6 +43,7 @@ struct FlakeInput
|
||||
std::optional<FlakeRef> ref;
|
||||
bool isFlake = true; // true = process flake to get outputs, false = (fetched) static source path
|
||||
std::optional<InputPath> follows;
|
||||
bool absolute = false; // whether 'follows' is relative to the flake root
|
||||
FlakeInputs overrides;
|
||||
};
|
||||
|
||||
@@ -58,10 +59,9 @@ struct ConfigFile
|
||||
/* The contents of a flake.nix file. */
|
||||
struct Flake
|
||||
{
|
||||
FlakeRef originalRef; // the original flake specification (by the user)
|
||||
FlakeRef resolvedRef; // registry references and caching resolved to the specific underlying flake
|
||||
FlakeRef lockedRef; // the specific local store result of invoking the fetcher
|
||||
bool forceDirty = false; // pretend that 'lockedRef' is dirty
|
||||
FlakeRef originalRef; // the original flake specification (by the user)
|
||||
FlakeRef resolvedRef; // registry references and caching resolved to the specific underlying flake
|
||||
FlakeRef lockedRef; // the specific local store result of invoking the fetcher
|
||||
std::optional<std::string> description;
|
||||
std::shared_ptr<const fetchers::Tree> sourceInfo;
|
||||
FlakeInputs inputs;
|
||||
@@ -102,11 +102,7 @@ struct LockFlags
|
||||
|
||||
/* Whether to use the registries to lookup indirect flake
|
||||
references like 'nixpkgs'. */
|
||||
std::optional<bool> useRegistries = std::nullopt;
|
||||
|
||||
/* Whether to apply flake's nixConfig attribute to the configuration */
|
||||
|
||||
bool applyNixConfig = false;
|
||||
bool useRegistries = true;
|
||||
|
||||
/* Whether mutable flake references (i.e. those without a Git
|
||||
revision or similar) without a corresponding lock are
|
||||
@@ -141,8 +137,6 @@ void emitTreeAttrs(
|
||||
EvalState & state,
|
||||
const fetchers::Tree & tree,
|
||||
const fetchers::Input & input,
|
||||
Value & v,
|
||||
bool emptyRevFallback = false,
|
||||
bool forceDirty = false);
|
||||
Value & v, bool emptyRevFallback = false);
|
||||
|
||||
}
|
||||
|
||||
@@ -172,12 +172,8 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
auto parsedURL = parseURL(url);
|
||||
std::string fragment;
|
||||
std::swap(fragment, parsedURL.fragment);
|
||||
|
||||
auto input = Input::fromURL(parsedURL);
|
||||
input.parent = baseDir;
|
||||
|
||||
return std::make_pair(
|
||||
FlakeRef(std::move(input), get(parsedURL.query, "dir").value_or("")),
|
||||
FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")),
|
||||
fragment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#include "store-api.hh"
|
||||
#include "url-parts.hh"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace nix::flake {
|
||||
@@ -270,20 +268,10 @@ std::map<InputPath, Node::Edge> LockFile::getAllInputs() const
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::string describe(const FlakeRef & flakeRef)
|
||||
{
|
||||
auto s = fmt("'%s'", flakeRef.to_string());
|
||||
|
||||
if (auto lastModified = flakeRef.input.getLastModified())
|
||||
s += fmt(" (%s)", std::put_time(std::gmtime(&*lastModified), "%Y-%m-%d"));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream & operator <<(std::ostream & stream, const Node::Edge & edge)
|
||||
{
|
||||
if (auto node = std::get_if<0>(&edge))
|
||||
stream << describe((*node)->lockedRef);
|
||||
stream << "'" << (*node)->lockedRef << "'";
|
||||
else if (auto follows = std::get_if<1>(&edge))
|
||||
stream << fmt("follows '%s'", printInputPath(*follows));
|
||||
return stream;
|
||||
@@ -311,15 +299,14 @@ std::string LockFile::diff(const LockFile & oldLocks, const LockFile & newLocks)
|
||||
|
||||
while (i != oldFlat.end() || j != newFlat.end()) {
|
||||
if (j != newFlat.end() && (i == oldFlat.end() || i->first > j->first)) {
|
||||
res += fmt("• " ANSI_GREEN "Added input '%s':" ANSI_NORMAL "\n %s\n",
|
||||
printInputPath(j->first), j->second);
|
||||
res += fmt("* Added '%s': %s\n", printInputPath(j->first), j->second);
|
||||
++j;
|
||||
} else if (i != oldFlat.end() && (j == newFlat.end() || i->first < j->first)) {
|
||||
res += fmt("• " ANSI_RED "Removed input '%s'" ANSI_NORMAL "\n", printInputPath(i->first));
|
||||
res += fmt("* Removed '%s'\n", printInputPath(i->first));
|
||||
++i;
|
||||
} else {
|
||||
if (!equals(i->second, j->second)) {
|
||||
res += fmt("• " ANSI_BOLD "Updated input '%s':" ANSI_NORMAL "\n %s\n → %s\n",
|
||||
res += fmt("* Updated '%s': %s -> %s\n",
|
||||
printInputPath(i->first),
|
||||
i->second,
|
||||
j->second);
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
%s DEFAULT
|
||||
%x STRING
|
||||
%x IND_STRING
|
||||
%x INPATH
|
||||
%x INPATH_SLASH
|
||||
%x PATH_START
|
||||
|
||||
|
||||
%{
|
||||
@@ -28,8 +25,6 @@ using namespace nix;
|
||||
|
||||
namespace nix {
|
||||
|
||||
// backup to recover from yyless(0)
|
||||
YYLTYPE prev_yylloc;
|
||||
|
||||
static void initLoc(YYLTYPE * loc)
|
||||
{
|
||||
@@ -40,18 +35,14 @@ static void initLoc(YYLTYPE * loc)
|
||||
|
||||
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
{
|
||||
prev_yylloc = *loc;
|
||||
|
||||
loc->first_line = loc->last_line;
|
||||
loc->first_column = loc->last_column;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
while (len--) {
|
||||
switch (*s++) {
|
||||
case '\r':
|
||||
if (*s == '\n') { /* cr/lf */
|
||||
i++;
|
||||
if (*s == '\n') /* cr/lf */
|
||||
s++;
|
||||
}
|
||||
/* fall through */
|
||||
case '\n':
|
||||
++loc->last_line;
|
||||
@@ -64,7 +55,6 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
}
|
||||
|
||||
|
||||
// FIXME: optimize
|
||||
static Expr * unescapeStr(SymbolTable & symbols, const char * s, size_t length)
|
||||
{
|
||||
string t;
|
||||
@@ -105,12 +95,9 @@ ANY .|\n
|
||||
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
|
||||
INT [0-9]+
|
||||
FLOAT (([1-9][0-9]*\.[0-9]*)|(0?\.[0-9]+))([Ee][+-]?[0-9]+)?
|
||||
PATH_CHAR [a-zA-Z0-9\.\_\-\+]
|
||||
PATH {PATH_CHAR}*(\/{PATH_CHAR}+)+\/?
|
||||
PATH_SEG {PATH_CHAR}*\/
|
||||
HPATH \~(\/{PATH_CHAR}+)+\/?
|
||||
HPATH_START \~\/
|
||||
SPATH \<{PATH_CHAR}+(\/{PATH_CHAR}+)*\>
|
||||
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+\/?
|
||||
HPATH \~(\/[a-zA-Z0-9\.\_\-\+]+)+\/?
|
||||
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
|
||||
URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+
|
||||
|
||||
|
||||
@@ -211,75 +198,17 @@ or { return OR_KW; }
|
||||
return IND_STR;
|
||||
}
|
||||
|
||||
{PATH_SEG}\$\{ |
|
||||
{HPATH_START}\$\{ {
|
||||
PUSH_STATE(PATH_START);
|
||||
yyless(0);
|
||||
*yylloc = prev_yylloc;
|
||||
}
|
||||
|
||||
<PATH_START>{PATH_SEG} {
|
||||
POP_STATE();
|
||||
PUSH_STATE(INPATH_SLASH);
|
||||
yylval->path = strdup(yytext);
|
||||
return PATH;
|
||||
}
|
||||
|
||||
<PATH_START>{HPATH_START} {
|
||||
POP_STATE();
|
||||
PUSH_STATE(INPATH_SLASH);
|
||||
yylval->path = strdup(yytext);
|
||||
return HPATH;
|
||||
}
|
||||
|
||||
{PATH} {
|
||||
if (yytext[yyleng-1] == '/')
|
||||
PUSH_STATE(INPATH_SLASH);
|
||||
else
|
||||
PUSH_STATE(INPATH);
|
||||
yylval->path = strdup(yytext);
|
||||
return PATH;
|
||||
}
|
||||
{HPATH} {
|
||||
if (yytext[yyleng-1] == '/')
|
||||
PUSH_STATE(INPATH_SLASH);
|
||||
else
|
||||
PUSH_STATE(INPATH);
|
||||
yylval->path = strdup(yytext);
|
||||
return HPATH;
|
||||
}
|
||||
|
||||
<INPATH,INPATH_SLASH>\$\{ {
|
||||
POP_STATE();
|
||||
PUSH_STATE(INPATH);
|
||||
PUSH_STATE(DEFAULT);
|
||||
return DOLLAR_CURLY;
|
||||
}
|
||||
<INPATH,INPATH_SLASH>{PATH}|{PATH_SEG}|{PATH_CHAR}+ {
|
||||
POP_STATE();
|
||||
if (yytext[yyleng-1] == '/')
|
||||
PUSH_STATE(INPATH_SLASH);
|
||||
else
|
||||
PUSH_STATE(INPATH);
|
||||
yylval->e = new ExprString(data->symbols.create(string(yytext)));
|
||||
return STR;
|
||||
}
|
||||
<INPATH>{ANY} |
|
||||
<INPATH><<EOF>> {
|
||||
/* if we encounter a non-path character we inform the parser that the path has
|
||||
ended with a PATH_END token and re-parse this character in the default
|
||||
context (it may be ')', ';', or something of that sort) */
|
||||
POP_STATE();
|
||||
yyless(0);
|
||||
*yylloc = prev_yylloc;
|
||||
return PATH_END;
|
||||
}
|
||||
|
||||
<INPATH_SLASH>{ANY} |
|
||||
<INPATH_SLASH><<EOF>> {
|
||||
throw ParseError("path has a trailing slash");
|
||||
}
|
||||
|
||||
{PATH} { if (yytext[yyleng-1] == '/')
|
||||
throw ParseError("path '%s' has a trailing slash", yytext);
|
||||
yylval->path = strdup(yytext);
|
||||
return PATH;
|
||||
}
|
||||
{HPATH} { if (yytext[yyleng-1] == '/')
|
||||
throw ParseError("path '%s' has a trailing slash", yytext);
|
||||
yylval->path = strdup(yytext);
|
||||
return HPATH;
|
||||
}
|
||||
{SPATH} { yylval->path = strdup(yytext); return SPATH; }
|
||||
{URI} { yylval->uri = strdup(yytext); return URI; }
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ libexpr_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/lib
|
||||
|
||||
libexpr_LIBS = libutil libstore libfetchers
|
||||
|
||||
libexpr_LDFLAGS += -lboost_context -pthread
|
||||
ifdef HOST_LINUX
|
||||
libexpr_LDFLAGS = -lboost_context
|
||||
ifeq ($(OS), Linux)
|
||||
libexpr_LDFLAGS += -ldl
|
||||
endif
|
||||
|
||||
@@ -35,7 +35,7 @@ $(d)/lexer-tab.cc $(d)/lexer-tab.hh: $(d)/lexer.l
|
||||
|
||||
clean-files += $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh
|
||||
|
||||
$(eval $(call install-file-in, $(d)/nix-expr.pc, $(libdir)/pkgconfig, 0644))
|
||||
$(eval $(call install-file-in, $(d)/nix-expr.pc, $(prefix)/lib/pkgconfig, 0644))
|
||||
|
||||
$(foreach i, $(wildcard src/libexpr/flake/*.hh), \
|
||||
$(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644)))
|
||||
|
||||
@@ -124,36 +124,23 @@ void ExprList::show(std::ostream & str) const
|
||||
void ExprLambda::show(std::ostream & str) const
|
||||
{
|
||||
str << "(";
|
||||
for (auto & arg : args) {
|
||||
if (arg.formals) {
|
||||
str << "{ ";
|
||||
bool first = true;
|
||||
for (auto & i : arg.formals->formals) {
|
||||
if (first) first = false; else str << ", ";
|
||||
str << i.name;
|
||||
if (i.def) str << " ? " << *i.def;
|
||||
}
|
||||
if (arg.formals->ellipsis) {
|
||||
if (!first) str << ", ";
|
||||
str << "...";
|
||||
}
|
||||
str << " }";
|
||||
if (!arg.arg.empty()) str << " @ ";
|
||||
if (matchAttrs) {
|
||||
str << "{ ";
|
||||
bool first = true;
|
||||
for (auto & i : formals->formals) {
|
||||
if (first) first = false; else str << ", ";
|
||||
str << i.name;
|
||||
if (i.def) str << " ? " << *i.def;
|
||||
}
|
||||
if (!arg.arg.empty()) str << arg.arg;
|
||||
str << ": ";
|
||||
if (formals->ellipsis) {
|
||||
if (!first) str << ", ";
|
||||
str << "...";
|
||||
}
|
||||
str << " }";
|
||||
if (!arg.empty()) str << " @ ";
|
||||
}
|
||||
str << *body << ")";
|
||||
}
|
||||
|
||||
void ExprCall::show(std::ostream & str) const
|
||||
{
|
||||
str << '(' << *fun;
|
||||
for (auto e : args) {
|
||||
str << ' ';
|
||||
str << *e;
|
||||
}
|
||||
str << ')';
|
||||
if (!arg.empty()) str << arg;
|
||||
str << ": " << *body << ")";
|
||||
}
|
||||
|
||||
void ExprLet::show(std::ostream & str) const
|
||||
@@ -276,13 +263,14 @@ void ExprVar::bindVars(const StaticEnv & env)
|
||||
/* Check whether the variable appears in the environment. If so,
|
||||
set its level and displacement. */
|
||||
const StaticEnv * curEnv;
|
||||
Level level;
|
||||
unsigned int level;
|
||||
int withLevel = -1;
|
||||
for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++) {
|
||||
if (curEnv->isWith) {
|
||||
if (withLevel == -1) withLevel = level;
|
||||
} else {
|
||||
if (auto i = curEnv->get(name)) {
|
||||
StaticEnv::Vars::const_iterator i = curEnv->vars.find(name);
|
||||
if (i != curEnv->vars.end()) {
|
||||
fromWith = false;
|
||||
this->level = level;
|
||||
displ = i->second;
|
||||
@@ -323,16 +311,14 @@ void ExprOpHasAttr::bindVars(const StaticEnv & env)
|
||||
void ExprAttrs::bindVars(const StaticEnv & env)
|
||||
{
|
||||
const StaticEnv * dynamicEnv = &env;
|
||||
StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0);
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
if (recursive) {
|
||||
dynamicEnv = &newEnv;
|
||||
|
||||
Displacement displ = 0;
|
||||
unsigned int displ = 0;
|
||||
for (auto & i : attrs)
|
||||
newEnv.vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
|
||||
// No need to sort newEnv since attrs is in sorted order.
|
||||
newEnv.vars[i.first] = i.second.displ = displ++;
|
||||
|
||||
for (auto & i : attrs)
|
||||
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
||||
@@ -356,67 +342,30 @@ void ExprList::bindVars(const StaticEnv & env)
|
||||
|
||||
void ExprLambda::bindVars(const StaticEnv & env)
|
||||
{
|
||||
/* The parser adds arguments in reverse order. Let's fix that
|
||||
now. */
|
||||
std::reverse(args.begin(), args.end());
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
envSize = 0;
|
||||
unsigned int displ = 0;
|
||||
|
||||
for (auto & arg :args) {
|
||||
if (!arg.arg.empty()) envSize++;
|
||||
if (arg.formals) envSize += arg.formals->formals.size();
|
||||
if (!arg.empty()) newEnv.vars[arg] = displ++;
|
||||
|
||||
if (matchAttrs) {
|
||||
for (auto & i : formals->formals)
|
||||
newEnv.vars[i.name] = displ++;
|
||||
|
||||
for (auto & i : formals->formals)
|
||||
if (i.def) i.def->bindVars(newEnv);
|
||||
}
|
||||
|
||||
StaticEnv newEnv(false, &env, envSize);
|
||||
|
||||
Displacement displ = 0;
|
||||
|
||||
for (auto & arg : args) {
|
||||
if (!arg.arg.empty()) {
|
||||
if (auto i = const_cast<StaticEnv::Vars::value_type *>(newEnv.get(arg.arg)))
|
||||
i->second = displ++;
|
||||
else
|
||||
newEnv.vars.emplace_back(arg.arg, displ++);
|
||||
}
|
||||
|
||||
if (arg.formals) {
|
||||
for (auto & i : arg.formals->formals) {
|
||||
if (auto j = const_cast<StaticEnv::Vars::value_type *>(newEnv.get(i.name)))
|
||||
j->second = displ++;
|
||||
else
|
||||
newEnv.vars.emplace_back(i.name, displ++);
|
||||
}
|
||||
|
||||
newEnv.sort();
|
||||
|
||||
for (auto & i : arg.formals->formals)
|
||||
if (i.def) i.def->bindVars(newEnv);
|
||||
}
|
||||
}
|
||||
|
||||
assert(displ == envSize);
|
||||
|
||||
newEnv.sort();
|
||||
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
|
||||
void ExprCall::bindVars(const StaticEnv & env)
|
||||
{
|
||||
fun->bindVars(env);
|
||||
for (auto e : args)
|
||||
e->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprLet::bindVars(const StaticEnv & env)
|
||||
{
|
||||
StaticEnv newEnv(false, &env, attrs->attrs.size());
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
Displacement displ = 0;
|
||||
unsigned int displ = 0;
|
||||
for (auto & i : attrs->attrs)
|
||||
newEnv.vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
|
||||
// No need to sort newEnv since attrs->attrs is in sorted order.
|
||||
newEnv.vars[i.first] = i.second.displ = displ++;
|
||||
|
||||
for (auto & i : attrs->attrs)
|
||||
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
||||
@@ -430,7 +379,7 @@ void ExprWith::bindVars(const StaticEnv & env)
|
||||
level so that `lookupVar' can look up variables in the previous
|
||||
`with' if this one doesn't contain the desired attribute. */
|
||||
const StaticEnv * curEnv;
|
||||
Level level;
|
||||
unsigned int level;
|
||||
prevWith = 0;
|
||||
for (curEnv = &env, level = 1; curEnv; curEnv = curEnv->up, level++)
|
||||
if (curEnv->isWith) {
|
||||
@@ -491,6 +440,18 @@ string ExprLambda::showNamePos() const
|
||||
return (format("%1% at %2%") % (name.set() ? "'" + (string) name + "'" : "anonymous function") % pos).str();
|
||||
}
|
||||
|
||||
void ExprCastedVar::show(std::ostream & str) const {
|
||||
std::set<const Value*> active;
|
||||
printValue(str, active, *v);
|
||||
}
|
||||
|
||||
void ExprCastedVar::bindVars(const StaticEnv & env) {}
|
||||
void ExprCastedVar::eval(EvalState & state, Env & env, Value & v) {
|
||||
v = std::move(*this->v);
|
||||
}
|
||||
Value * ExprCastedVar::maybeThunk(EvalState & state, Env & env) {
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/* Symbol table. */
|
||||
@@ -503,4 +464,5 @@ size_t SymbolTable::totalSize() const
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "symbol-table.hh"
|
||||
#include "error.hh"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -133,9 +135,6 @@ struct ExprPath : Expr
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
|
||||
typedef uint32_t Level;
|
||||
typedef uint32_t Displacement;
|
||||
|
||||
struct ExprVar : Expr
|
||||
{
|
||||
Pos pos;
|
||||
@@ -151,8 +150,8 @@ struct ExprVar : Expr
|
||||
value is obtained by getting the attribute named `name' from
|
||||
the set stored in the environment that is `level' levels up
|
||||
from the current one.*/
|
||||
Level level;
|
||||
Displacement displ;
|
||||
unsigned int level;
|
||||
unsigned int displ;
|
||||
|
||||
ExprVar(const Symbol & name) : name(name) { };
|
||||
ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { };
|
||||
@@ -166,7 +165,12 @@ struct ExprSelect : Expr
|
||||
Expr * e, * def;
|
||||
AttrPath attrPath;
|
||||
ExprSelect(const Pos & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
|
||||
ExprSelect(const Pos & pos, Expr * e, const Symbol & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
||||
ExprSelect(const Pos & pos, Expr * e, const Symbol & name)
|
||||
: ExprSelect(pos, e, std::vector{name}) {};
|
||||
ExprSelect(const Pos & pos, Expr * e, const std::vector<Symbol> & symbolicAttrPath) : pos(pos), e(e), def(0) {
|
||||
for (auto & name : symbolicAttrPath)
|
||||
attrPath.push_back(AttrName(name));
|
||||
};
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@@ -186,7 +190,7 @@ struct ExprAttrs : Expr
|
||||
bool inherited;
|
||||
Expr * e;
|
||||
Pos pos;
|
||||
Displacement displ; // displacement
|
||||
unsigned int displ; // displacement
|
||||
AttrDef(Expr * e, const Pos & pos, bool inherited=false)
|
||||
: inherited(inherited), e(e), pos(pos) { };
|
||||
AttrDef() { };
|
||||
@@ -233,38 +237,24 @@ struct ExprLambda : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
|
||||
struct Arg
|
||||
{
|
||||
Symbol arg;
|
||||
Formals * formals;
|
||||
};
|
||||
|
||||
std::vector<Arg> args;
|
||||
|
||||
Symbol arg;
|
||||
bool matchAttrs;
|
||||
Formals * formals;
|
||||
Expr * body;
|
||||
|
||||
Displacement envSize = 0; // initialized by bindVars()
|
||||
|
||||
ExprLambda(const Pos & pos, Expr * body)
|
||||
: pos(pos), body(body)
|
||||
{ };
|
||||
ExprLambda(const Pos & pos, const Symbol & arg, bool matchAttrs, Formals * formals, Expr * body)
|
||||
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
|
||||
{
|
||||
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
||||
throw ParseError({
|
||||
.msg = hintfmt("duplicate formal function argument '%1%'", arg),
|
||||
.errPos = pos
|
||||
});
|
||||
};
|
||||
void setName(Symbol & name);
|
||||
string showNamePos() const;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprCall : Expr
|
||||
{
|
||||
Expr * fun;
|
||||
std::vector<Expr *> args;
|
||||
Pos pos;
|
||||
ExprCall(const Pos & pos, Expr * fun, std::vector<Expr *> && args)
|
||||
: fun(fun), args(args), pos(pos)
|
||||
{ }
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprLet : Expr
|
||||
{
|
||||
ExprAttrs * attrs;
|
||||
@@ -323,6 +313,7 @@ struct ExprOpNot : Expr
|
||||
void eval(EvalState & state, Env & env, Value & v); \
|
||||
};
|
||||
|
||||
MakeBinOp(ExprApp, "")
|
||||
MakeBinOp(ExprOpEq, "==")
|
||||
MakeBinOp(ExprOpNEq, "!=")
|
||||
MakeBinOp(ExprOpAnd, "&&")
|
||||
@@ -348,6 +339,14 @@ struct ExprPos : Expr
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprCastedVar : Expr
|
||||
{
|
||||
Value * v;
|
||||
ExprCastedVar(Value * v) : v(v) {};
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
||||
/* Static environments are used to map variable names onto (level,
|
||||
displacement) pairs used to obtain the value of the variable at
|
||||
@@ -356,28 +355,9 @@ struct StaticEnv
|
||||
{
|
||||
bool isWith;
|
||||
const StaticEnv * up;
|
||||
|
||||
// Note: these must be in sorted order.
|
||||
typedef std::vector<std::pair<Symbol, Displacement>> Vars;
|
||||
typedef std::map<Symbol, unsigned int> Vars;
|
||||
Vars vars;
|
||||
|
||||
StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) {
|
||||
vars.reserve(expectedSize);
|
||||
};
|
||||
|
||||
void sort()
|
||||
{
|
||||
std::sort(vars.begin(), vars.end(),
|
||||
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
|
||||
}
|
||||
|
||||
const Vars::value_type * get(const Symbol & name) const
|
||||
{
|
||||
Vars::value_type key(name, 0);
|
||||
auto i = std::lower_bound(vars.begin(), vars.end(), key);
|
||||
if (i != vars.end() && i->first == name) return &*i;
|
||||
return {};
|
||||
}
|
||||
StaticEnv(bool isWith, const StaticEnv * up) : isWith(isWith), up(up) { };
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -126,14 +126,14 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||
auto j2 = jAttrs->attrs.find(ad.first);
|
||||
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
|
||||
dupAttr(ad.first, j2->second.pos, ad.second.pos);
|
||||
jAttrs->attrs.emplace(ad.first, ad.second);
|
||||
jAttrs->attrs[ad.first] = ad.second;
|
||||
}
|
||||
} else {
|
||||
dupAttr(attrPath, pos, j->second.pos);
|
||||
}
|
||||
} else {
|
||||
// This attr path is not defined. Let's create it.
|
||||
attrs->attrs.emplace(i->symbol, ExprAttrs::AttrDef(e, pos));
|
||||
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(e, pos);
|
||||
e->setName(i->symbol);
|
||||
}
|
||||
} else {
|
||||
@@ -154,24 +154,6 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||
}
|
||||
|
||||
|
||||
static Expr * addArg(const Pos & pos, Expr * e, ExprLambda::Arg && arg)
|
||||
{
|
||||
if (!arg.arg.empty() && arg.formals && arg.formals->argNames.count(arg.arg))
|
||||
throw ParseError({
|
||||
.msg = hintfmt("duplicate formal function argument '%1%'", arg.arg),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
auto e2 = dynamic_cast<ExprLambda *>(e); // FIXME: slow?
|
||||
if (!e2)
|
||||
e2 = new ExprLambda(pos, e);
|
||||
else
|
||||
e2->pos = pos;
|
||||
e2->args.emplace_back(std::move(arg));
|
||||
return e2;
|
||||
}
|
||||
|
||||
|
||||
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Expr *> & es)
|
||||
{
|
||||
if (es.empty()) return new ExprString(symbols.create(""));
|
||||
@@ -301,20 +283,20 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||
}
|
||||
|
||||
%type <e> start expr expr_function expr_if expr_op
|
||||
%type <e> expr_select expr_simple expr_app
|
||||
%type <e> expr_app expr_select expr_simple
|
||||
%type <list> expr_list
|
||||
%type <attrs> binds
|
||||
%type <formals> formals
|
||||
%type <formal> formal
|
||||
%type <attrNames> attrs attrpath
|
||||
%type <string_parts> string_parts_interpolated ind_string_parts
|
||||
%type <e> path_start string_parts string_attr
|
||||
%type <e> string_parts string_attr
|
||||
%type <id> attr
|
||||
%token <id> ID ATTRPATH
|
||||
%token <e> STR IND_STR
|
||||
%token <n> INT
|
||||
%token <nf> FLOAT
|
||||
%token <path> PATH HPATH SPATH PATH_END
|
||||
%token <path> PATH HPATH SPATH
|
||||
%token <uri> URI
|
||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
|
||||
%token DOLLAR_CURLY /* == ${ */
|
||||
@@ -342,13 +324,13 @@ expr: expr_function;
|
||||
|
||||
expr_function
|
||||
: ID ':' expr_function
|
||||
{ $$ = addArg(CUR_POS, $3, {data->symbols.create($1), nullptr}); }
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), false, 0, $3); }
|
||||
| '{' formals '}' ':' expr_function
|
||||
{ $$ = addArg(CUR_POS, $5, {data->state.sEpsilon, $2}); }
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), true, $2, $5); }
|
||||
| '{' formals '}' '@' ID ':' expr_function
|
||||
{ $$ = addArg(CUR_POS, $7, {data->symbols.create($5), $2}); }
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), true, $2, $7); }
|
||||
| ID '@' '{' formals '}' ':' expr_function
|
||||
{ $$ = addArg(CUR_POS, $7, {data->symbols.create($1), $4}); }
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), true, $4, $7); }
|
||||
| ASSERT expr ';' expr_function
|
||||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||
| WITH expr ';' expr_function
|
||||
@@ -371,13 +353,13 @@ expr_if
|
||||
|
||||
expr_op
|
||||
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
|
||||
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {new ExprInt(0), $2}); }
|
||||
| '-' expr_op %prec NEGATE { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__sub")), new ExprInt(0)), $2); }
|
||||
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
||||
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
||||
| expr_op '<' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3}); }
|
||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); }
|
||||
| expr_op '>' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); }
|
||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); }
|
||||
| expr_op '<' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $1), $3); }
|
||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1)); }
|
||||
| expr_op '>' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1); }
|
||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $1), $3)); }
|
||||
| expr_op AND expr_op { $$ = new ExprOpAnd(CUR_POS, $1, $3); }
|
||||
| expr_op OR expr_op { $$ = new ExprOpOr(CUR_POS, $1, $3); }
|
||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl(CUR_POS, $1, $3); }
|
||||
@@ -385,22 +367,17 @@ expr_op
|
||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
||||
| expr_op '+' expr_op
|
||||
{ $$ = new ExprConcatStrings(CUR_POS, false, new vector<Expr *>({$1, $3})); }
|
||||
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
||||
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
||||
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
||||
| expr_op '-' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__sub")), $1), $3); }
|
||||
| expr_op '*' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__mul")), $1), $3); }
|
||||
| expr_op '/' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__div")), $1), $3); }
|
||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(CUR_POS, $1, $3); }
|
||||
| expr_app
|
||||
;
|
||||
|
||||
expr_app
|
||||
: expr_app expr_select {
|
||||
if (auto e2 = dynamic_cast<ExprCall *>($1)) {
|
||||
e2->args.push_back($2);
|
||||
$$ = $1;
|
||||
} else
|
||||
$$ = new ExprCall(CUR_POS, $1, {$2});
|
||||
}
|
||||
| expr_select
|
||||
: expr_app expr_select
|
||||
{ $$ = new ExprApp(CUR_POS, $1, $2); }
|
||||
| expr_select { $$ = $1; }
|
||||
;
|
||||
|
||||
expr_select
|
||||
@@ -411,7 +388,7 @@ expr_select
|
||||
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
||||
function named ‘or’, allow stuff like ‘map or [...]’. */
|
||||
expr_simple OR_KW
|
||||
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, data->symbols.create("or"))}); }
|
||||
{ $$ = new ExprApp(CUR_POS, $1, new ExprVar(CUR_POS, data->symbols.create("or"))); }
|
||||
| expr_simple { $$ = $1; }
|
||||
;
|
||||
|
||||
@@ -428,20 +405,17 @@ expr_simple
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
|
||||
}
|
||||
| path_start PATH_END { $$ = $1; }
|
||||
| path_start string_parts_interpolated PATH_END {
|
||||
$2->insert($2->begin(), $1);
|
||||
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
||||
}
|
||||
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
|
||||
| HPATH { $$ = new ExprPath(getHome() + string{$1 + 1}); }
|
||||
| SPATH {
|
||||
string path($1 + 1, strlen($1) - 2);
|
||||
$$ = new ExprCall(CUR_POS,
|
||||
new ExprVar(data->symbols.create("__findFile")),
|
||||
{new ExprVar(data->symbols.create("__nixPath")),
|
||||
new ExprString(data->symbols.create(path))});
|
||||
$$ = new ExprApp(CUR_POS,
|
||||
new ExprApp(new ExprVar(data->symbols.create("__findFile")),
|
||||
new ExprVar(data->symbols.create("__nixPath"))),
|
||||
new ExprString(data->symbols.create(path)));
|
||||
}
|
||||
| URI {
|
||||
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
|
||||
static bool noURLLiterals = settings.isExperimentalFeatureEnabled("no-url-literals");
|
||||
if (noURLLiterals)
|
||||
throw ParseError({
|
||||
.msg = hintfmt("URL literals are disabled"),
|
||||
@@ -464,7 +438,7 @@ expr_simple
|
||||
string_parts
|
||||
: STR
|
||||
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
|
||||
| { $$ = new ExprString(data->state.sEpsilon); }
|
||||
| { $$ = new ExprString(data->symbols.create("")); }
|
||||
;
|
||||
|
||||
string_parts_interpolated
|
||||
@@ -478,20 +452,6 @@ string_parts_interpolated
|
||||
}
|
||||
;
|
||||
|
||||
path_start
|
||||
: PATH {
|
||||
Path path(absPath($1, data->basePath));
|
||||
/* add back in the trailing '/' to the first segment */
|
||||
if ($1[strlen($1)-1] == '/' && strlen($1) > 1)
|
||||
path += "/";
|
||||
$$ = new ExprPath(path);
|
||||
}
|
||||
| HPATH {
|
||||
Path path(getHome() + string($1 + 1));
|
||||
$$ = new ExprPath(path);
|
||||
}
|
||||
;
|
||||
|
||||
ind_string_parts
|
||||
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
|
||||
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
|
||||
@@ -506,7 +466,7 @@ binds
|
||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||
dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
|
||||
Pos pos = makeCurPos(@3, data);
|
||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
||||
$$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true);
|
||||
}
|
||||
}
|
||||
| binds INHERIT '(' expr ')' attrs ';'
|
||||
@@ -515,7 +475,7 @@ binds
|
||||
for (auto & i : *$6) {
|
||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||
dupAttr(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[i.symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data));
|
||||
}
|
||||
}
|
||||
| { $$ = new ExprAttrs(makeCurPos(@0, data)); }
|
||||
@@ -775,7 +735,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
|
||||
res = { true, path };
|
||||
else {
|
||||
logWarning({
|
||||
.msg = hintfmt("Nix search path entry '%1%' does not exist, ignoring", elem.second)
|
||||
.msg = hintfmt("warning: Nix search path entry '%1%' does not exist, ignoring", elem.second)
|
||||
});
|
||||
res = { false, "" };
|
||||
}
|
||||
|
||||
@@ -52,13 +52,16 @@ void EvalState::realiseContext(const PathSet & context)
|
||||
if (drvs.empty()) return;
|
||||
|
||||
if (!evalSettings.enableImportFromDerivation)
|
||||
throw Error(
|
||||
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
|
||||
throw EvalError("attempted to realize '%1%' during evaluation but 'allow-import-from-derivation' is false",
|
||||
store->printStorePath(drvs.begin()->drvPath));
|
||||
|
||||
/* Build/substitute the context. */
|
||||
/* For performance, prefetch all substitute info. */
|
||||
StorePathSet willBuild, willSubstitute, unknown;
|
||||
uint64_t downloadSize, narSize;
|
||||
std::vector<DerivedPath> buildReqs;
|
||||
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
|
||||
store->queryMissing(buildReqs, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
|
||||
store->buildPaths(buildReqs);
|
||||
|
||||
/* Add the output of this derivations to the allowed
|
||||
@@ -121,7 +124,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
|
||||
});
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, "while importing '%s'", path);
|
||||
throw;
|
||||
throw e;
|
||||
}
|
||||
|
||||
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
|
||||
@@ -157,15 +160,16 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
|
||||
}
|
||||
w.attrs->sort();
|
||||
|
||||
if (!state.vImportedDrvToDerivation) {
|
||||
state.vImportedDrvToDerivation = allocRootValue(state.allocValue());
|
||||
static RootValue fun;
|
||||
if (!fun) {
|
||||
fun = allocRootValue(state.allocValue());
|
||||
state.eval(state.parseExprFromString(
|
||||
#include "imported-drv-to-derivation.nix.gen.hh"
|
||||
, "/"), **state.vImportedDrvToDerivation);
|
||||
, "/"), **fun);
|
||||
}
|
||||
|
||||
state.forceFunction(**state.vImportedDrvToDerivation, pos);
|
||||
mkApp(v, **state.vImportedDrvToDerivation, w);
|
||||
state.forceFunction(**fun, pos);
|
||||
mkApp(v, **fun, w);
|
||||
state.forceAttrs(v, pos);
|
||||
}
|
||||
|
||||
@@ -184,17 +188,14 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
|
||||
Env * env = &state.allocEnv(vScope->attrs->size());
|
||||
env->up = &state.baseEnv;
|
||||
|
||||
StaticEnv staticEnv(false, &state.staticBaseEnv, vScope->attrs->size());
|
||||
StaticEnv staticEnv(false, &state.staticBaseEnv);
|
||||
|
||||
unsigned int displ = 0;
|
||||
for (auto & attr : *vScope->attrs) {
|
||||
staticEnv.vars.emplace_back(attr.name, displ);
|
||||
staticEnv.vars[attr.name] = displ;
|
||||
env->values[displ++] = attr.value;
|
||||
}
|
||||
|
||||
// No need to call staticEnv.sort(), because
|
||||
// args[0]->attrs is already sorted.
|
||||
|
||||
printTalkative("evaluating file '%1%'", realPath);
|
||||
Expr * e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv);
|
||||
|
||||
@@ -413,7 +414,7 @@ static RegisterPrimOp primop_isNull({
|
||||
Return `true` if *e* evaluates to `null`, and `false` otherwise.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
>
|
||||
> This function is *deprecated*; just write `e == null` instead.
|
||||
)",
|
||||
.fun = prim_isNull,
|
||||
@@ -988,7 +989,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
}
|
||||
|
||||
if (i->name == state.sContentAddressed) {
|
||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
||||
settings.requireExperimentalFeature("ca-derivations");
|
||||
contentAddressed = state.forceBool(*i->value, pos);
|
||||
}
|
||||
|
||||
@@ -1173,7 +1174,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
// hash per output.
|
||||
auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true);
|
||||
std::visit(overloaded {
|
||||
[&](Hash & h) {
|
||||
[&](Hash h) {
|
||||
for (auto & i : outputs) {
|
||||
auto outPath = state.store->makeOutputPath(i, h, drvName);
|
||||
drv.env[i] = state.store->printStorePath(outPath);
|
||||
@@ -1185,11 +1186,11 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
});
|
||||
}
|
||||
},
|
||||
[&](CaOutputHashes &) {
|
||||
[&](CaOutputHashes) {
|
||||
// Shouldn't happen as the toplevel derivation is not CA.
|
||||
assert(false);
|
||||
},
|
||||
[&](DeferredHash &) {
|
||||
[&](DeferredHash _) {
|
||||
for (auto & i : outputs) {
|
||||
drv.outputs.insert_or_assign(i,
|
||||
DerivationOutput {
|
||||
@@ -1492,20 +1493,15 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
string type = state.forceStringNoCtx(*args[0], pos);
|
||||
std::optional<HashType> ht = parseHashType(type);
|
||||
if (!ht)
|
||||
throw Error({
|
||||
.msg = hintfmt("unknown hash type '%1%'", type),
|
||||
.errPos = pos
|
||||
});
|
||||
throw Error({
|
||||
.msg = hintfmt("unknown hash type '%1%'", type),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
PathSet context;
|
||||
Path path = state.coerceToPath(pos, *args[1], context);
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError("cannot read '%s' since path '%s' is not valid, at %s", path, e.path, pos);
|
||||
}
|
||||
PathSet context; // discarded
|
||||
Path p = state.coerceToPath(pos, *args[1], context);
|
||||
|
||||
mkString(v, hashFile(*ht, state.checkSourcePath(state.toRealPath(path, context))).to_string(Base16, false));
|
||||
mkString(v, hashFile(*ht, state.checkSourcePath(p)).to_string(Base16, false), context);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_hashFile({
|
||||
@@ -1716,7 +1712,7 @@ static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
parseJSON(state, s, v);
|
||||
} catch (JSONParseError &e) {
|
||||
e.addTrace(pos, "while decoding a JSON string");
|
||||
throw;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1846,79 +1842,50 @@ static RegisterPrimOp primop_toFile({
|
||||
.fun = prim_toFile,
|
||||
});
|
||||
|
||||
static void addPath(
|
||||
EvalState & state,
|
||||
const Pos & pos,
|
||||
const string & name,
|
||||
Path path,
|
||||
Value * filterFun,
|
||||
FileIngestionMethod method,
|
||||
const std::optional<Hash> expectedHash,
|
||||
Value & v,
|
||||
const PathSet & context)
|
||||
static void addPath(EvalState & state, const Pos & pos, const string & name, const Path & path_,
|
||||
Value * filterFun, FileIngestionMethod method, const std::optional<Hash> expectedHash, Value & v)
|
||||
{
|
||||
try {
|
||||
// FIXME: handle CA derivation outputs (where path needs to
|
||||
// be rewritten to the actual output).
|
||||
state.realiseContext(context);
|
||||
const auto path = evalSettings.pureEval && expectedHash ?
|
||||
path_ :
|
||||
state.checkSourcePath(path_);
|
||||
PathFilter filter = filterFun ? ([&](const Path & path) {
|
||||
auto st = lstat(path);
|
||||
|
||||
if (state.store->isInStore(path)) {
|
||||
auto [storePath, subPath] = state.store->toStorePath(path);
|
||||
auto info = state.store->queryPathInfo(storePath);
|
||||
if (!info->references.empty())
|
||||
throw EvalError("store path '%s' is not allowed to have references",
|
||||
state.store->printStorePath(storePath));
|
||||
path = state.store->toRealPath(storePath) + subPath;
|
||||
}
|
||||
/* Call the filter function. The first argument is the path,
|
||||
the second is a string indicating the type of the file. */
|
||||
Value arg1;
|
||||
mkString(arg1, path);
|
||||
|
||||
path = evalSettings.pureEval && expectedHash
|
||||
? path
|
||||
: state.checkSourcePath(path);
|
||||
Value fun2;
|
||||
state.callFunction(*filterFun, arg1, fun2, noPos);
|
||||
|
||||
PathFilter filter = filterFun ? ([&](const Path & path) {
|
||||
auto st = lstat(path);
|
||||
Value arg2;
|
||||
mkString(arg2,
|
||||
S_ISREG(st.st_mode) ? "regular" :
|
||||
S_ISDIR(st.st_mode) ? "directory" :
|
||||
S_ISLNK(st.st_mode) ? "symlink" :
|
||||
"unknown" /* not supported, will fail! */);
|
||||
|
||||
/* Call the filter function. The first argument is the path,
|
||||
the second is a string indicating the type of the file. */
|
||||
Value arg1;
|
||||
mkString(arg1, path);
|
||||
Value res;
|
||||
state.callFunction(fun2, arg2, res, noPos);
|
||||
|
||||
Value arg2;
|
||||
mkString(arg2,
|
||||
S_ISREG(st.st_mode) ? "regular" :
|
||||
S_ISDIR(st.st_mode) ? "directory" :
|
||||
S_ISLNK(st.st_mode) ? "symlink" :
|
||||
"unknown" /* not supported, will fail! */);
|
||||
return state.forceBool(res, pos);
|
||||
}) : defaultPathFilter;
|
||||
|
||||
Value * args []{&arg1, &arg2};
|
||||
Value res;
|
||||
state.callFunction(*filterFun, 2, args, res, pos);
|
||||
std::optional<StorePath> expectedStorePath;
|
||||
if (expectedHash)
|
||||
expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name);
|
||||
Path dstPath;
|
||||
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
||||
dstPath = state.store->printStorePath(settings.readOnlyMode
|
||||
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
|
||||
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair));
|
||||
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
|
||||
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
|
||||
} else
|
||||
dstPath = state.store->printStorePath(*expectedStorePath);
|
||||
|
||||
return state.forceBool(res, pos);
|
||||
}) : defaultPathFilter;
|
||||
|
||||
std::optional<StorePath> expectedStorePath;
|
||||
if (expectedHash)
|
||||
expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name);
|
||||
|
||||
Path dstPath;
|
||||
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
||||
dstPath = state.store->printStorePath(settings.readOnlyMode
|
||||
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
|
||||
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair));
|
||||
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
|
||||
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
|
||||
} else
|
||||
dstPath = state.store->printStorePath(*expectedStorePath);
|
||||
|
||||
mkString(v, dstPath, {dstPath});
|
||||
|
||||
state.allowPath(dstPath);
|
||||
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, "while adding path '%s'", path);
|
||||
throw;
|
||||
}
|
||||
mkString(v, dstPath, {dstPath});
|
||||
}
|
||||
|
||||
|
||||
@@ -1926,6 +1893,11 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
||||
{
|
||||
PathSet context;
|
||||
Path path = state.coerceToPath(pos, *args[1], context);
|
||||
if (!context.empty())
|
||||
throw EvalError({
|
||||
.msg = hintfmt("string '%1%' cannot refer to other paths", path),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
state.forceValue(*args[0], pos);
|
||||
if (args[0]->type() != nFunction)
|
||||
@@ -1936,26 +1908,13 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context);
|
||||
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_filterSource({
|
||||
.name = "__filterSource",
|
||||
.args = {"e1", "e2"},
|
||||
.doc = R"(
|
||||
> **Warning**
|
||||
>
|
||||
> `filterSource` should not be used to filter store paths. Since
|
||||
> `filterSource` uses the name of the input directory while naming
|
||||
> the output directory, doing so will produce a directory name in
|
||||
> the form of `<hash2>-<hash>-<name>`, where `<hash>-<name>` is
|
||||
> the name of the input directory. Since `<hash>` depends on the
|
||||
> unfiltered directory, the name of the output directory will
|
||||
> indirectly depend on files that are filtered out by the
|
||||
> function. This will trigger a rebuild even when a filtered out
|
||||
> file is changed. Use `builtins.path` instead, which allows
|
||||
> specifying the name of the output directory.
|
||||
|
||||
This function allows you to copy sources into the Nix store while
|
||||
filtering certain files. For instance, suppose that you want to use
|
||||
the directory `source-dir` as an input to a Nix expression, e.g.
|
||||
@@ -2002,13 +1961,18 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
Value * filterFun = nullptr;
|
||||
auto method = FileIngestionMethod::Recursive;
|
||||
std::optional<Hash> expectedHash;
|
||||
PathSet context;
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
const string & n(attr.name);
|
||||
if (n == "path")
|
||||
if (n == "path") {
|
||||
PathSet context;
|
||||
path = state.coerceToPath(*attr.pos, *attr.value, context);
|
||||
else if (attr.name == state.sName)
|
||||
if (!context.empty())
|
||||
throw EvalError({
|
||||
.msg = hintfmt("string '%1%' cannot refer to other paths", path),
|
||||
.errPos = *attr.pos
|
||||
});
|
||||
} else if (attr.name == state.sName)
|
||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else if (n == "filter") {
|
||||
state.forceValue(*attr.value, pos);
|
||||
@@ -2031,7 +1995,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
if (name.empty())
|
||||
name = baseNameOf(path);
|
||||
|
||||
addPath(state, pos, name, path, filterFun, method, expectedHash, v, context);
|
||||
addPath(state, pos, name, path, filterFun, method, expectedHash, v);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_path({
|
||||
@@ -2145,7 +2109,7 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
pos
|
||||
);
|
||||
// !!! add to stack trace?
|
||||
if (state.countCalls && *i->pos != noPos) state.attrSelects[*i->pos]++;
|
||||
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
|
||||
state.forceValue(*i->value, pos);
|
||||
v = *i->value;
|
||||
}
|
||||
@@ -2386,41 +2350,26 @@ static RegisterPrimOp primop_catAttrs({
|
||||
static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
|
||||
if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) {
|
||||
state.mkAttrs(v, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args[0]->isLambda() && !args[0]->isPartialApp())
|
||||
if (!args[0]->isLambda())
|
||||
throw TypeError({
|
||||
.msg = hintfmt("'functionArgs' requires a function"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
size_t argsDone = 0;
|
||||
auto lambda = args[0];
|
||||
while (lambda->isPartialApp()) {
|
||||
argsDone++;
|
||||
lambda = lambda->app.left;
|
||||
}
|
||||
assert(lambda->isLambda());
|
||||
|
||||
assert(argsDone < lambda->lambda.fun->args.size());
|
||||
|
||||
// FIXME: handle partially applied functions
|
||||
auto formals = lambda->lambda.fun->args[argsDone].formals;
|
||||
|
||||
if (!formals) {
|
||||
if (!args[0]->lambda.fun->matchAttrs) {
|
||||
state.mkAttrs(v, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
state.mkAttrs(v, formals->formals.size());
|
||||
for (auto & i : formals->formals) {
|
||||
state.mkAttrs(v, args[0]->lambda.fun->formals->formals.size());
|
||||
for (auto & i : args[0]->lambda.fun->formals->formals) {
|
||||
// !!! should optimise booleans (allocate only once)
|
||||
Value * value = state.allocValue();
|
||||
v.attrs->push_back(Attr(i.name, value, ptr(&i.pos)));
|
||||
v.attrs->push_back(Attr(i.name, value, &i.pos));
|
||||
mkBool(*value, i.def);
|
||||
}
|
||||
v.attrs->sort();
|
||||
@@ -2566,7 +2515,7 @@ static RegisterPrimOp primop_tail({
|
||||
the argument isn’t a list or is an empty list.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
>
|
||||
> This function should generally be avoided since it's inefficient:
|
||||
> unlike Haskell's `tail`, it takes O(n) time, so recursing over a
|
||||
> list by repeatedly calling `tail` takes O(n^2) time.
|
||||
@@ -2708,9 +2657,10 @@ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args,
|
||||
Value * vCur = args[1];
|
||||
|
||||
for (unsigned int n = 0; n < args[2]->listSize(); ++n) {
|
||||
Value * vs []{vCur, args[2]->listElems()[n]};
|
||||
Value vTmp;
|
||||
state.callFunction(*args[0], *vCur, vTmp, pos);
|
||||
vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
|
||||
state.callFunction(*args[0], 2, vs, *vCur, pos);
|
||||
state.callFunction(vTmp, *args[2]->listElems()[n], *vCur, pos);
|
||||
}
|
||||
state.forceValue(v, pos);
|
||||
} else {
|
||||
@@ -2831,16 +2781,17 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
v.listElems()[n] = args[1]->listElems()[n];
|
||||
}
|
||||
|
||||
|
||||
auto comparator = [&](Value * a, Value * b) {
|
||||
/* Optimization: if the comparator is lessThan, bypass
|
||||
callFunction. */
|
||||
if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan)
|
||||
return CompareValues()(a, b);
|
||||
|
||||
Value * vs[] = {a, b};
|
||||
Value vBool;
|
||||
state.callFunction(*args[0], 2, vs, vBool, pos);
|
||||
return state.forceBool(vBool, pos);
|
||||
Value vTmp1, vTmp2;
|
||||
state.callFunction(*args[0], *a, vTmp1, pos);
|
||||
state.callFunction(vTmp1, *b, vTmp2, pos);
|
||||
return state.forceBool(vTmp2, pos);
|
||||
};
|
||||
|
||||
/* FIXME: std::sort can segfault if the comparator is not a strict
|
||||
@@ -2946,7 +2897,7 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V
|
||||
state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos)));
|
||||
} catch (TypeError &e) {
|
||||
e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap"));
|
||||
throw;
|
||||
throw e;
|
||||
}
|
||||
len += lists[n].listSize();
|
||||
}
|
||||
@@ -3158,7 +3109,7 @@ static RegisterPrimOp primop_toString({
|
||||
|
||||
- A path (e.g., `toString /foo/bar` yields `"/foo/bar"`.
|
||||
|
||||
- A set containing `{ __toString = self: ...; }` or `{ outPath = ...; }`.
|
||||
- A set containing `{ __toString = self: ...; }`.
|
||||
|
||||
- An integer.
|
||||
|
||||
@@ -3243,7 +3194,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
|
||||
PathSet context; // discarded
|
||||
string s = state.forceString(*args[1], context, pos);
|
||||
|
||||
mkString(v, hashString(*ht, s).to_string(Base16, false));
|
||||
mkString(v, hashString(*ht, s).to_string(Base16, false), context);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_hashString({
|
||||
@@ -3650,13 +3601,15 @@ static RegisterPrimOp primop_splitVersion({
|
||||
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
|
||||
|
||||
|
||||
RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun)
|
||||
RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun,
|
||||
std::optional<std::string> requiredFeature)
|
||||
{
|
||||
if (!primOps) primOps = new PrimOps;
|
||||
primOps->push_back({
|
||||
.name = name,
|
||||
.args = {},
|
||||
.arity = arity,
|
||||
.requiredFeature = std::move(requiredFeature),
|
||||
.fun = fun
|
||||
});
|
||||
}
|
||||
@@ -3692,7 +3645,9 @@ void EvalState::createBaseEnv()
|
||||
if (!evalSettings.pureEval) {
|
||||
mkInt(v, time(0));
|
||||
addConstant("__currentTime", v);
|
||||
}
|
||||
|
||||
if (!evalSettings.pureEval) {
|
||||
mkString(v, settings.thisSystem.get());
|
||||
addConstant("__currentSystem", v);
|
||||
}
|
||||
@@ -3730,31 +3685,26 @@ void EvalState::createBaseEnv()
|
||||
|
||||
if (RegisterPrimOp::primOps)
|
||||
for (auto & primOp : *RegisterPrimOp::primOps)
|
||||
addPrimOp({
|
||||
.fun = primOp.fun,
|
||||
.arity = std::max(primOp.args.size(), primOp.arity),
|
||||
.name = symbols.create(primOp.name),
|
||||
.args = std::move(primOp.args),
|
||||
.doc = primOp.doc,
|
||||
});
|
||||
if (!primOp.requiredFeature || settings.isExperimentalFeatureEnabled(*primOp.requiredFeature))
|
||||
addPrimOp({
|
||||
.fun = primOp.fun,
|
||||
.arity = std::max(primOp.args.size(), primOp.arity),
|
||||
.name = symbols.create(primOp.name),
|
||||
.args = std::move(primOp.args),
|
||||
.doc = primOp.doc,
|
||||
});
|
||||
|
||||
/* Add a wrapper around the derivation primop that computes the
|
||||
`drvPath' and `outPath' attributes lazily. */
|
||||
sDerivationNix = symbols.create("//builtin/derivation.nix");
|
||||
auto vDerivation = allocValue();
|
||||
addConstant("derivation", vDerivation);
|
||||
eval(parse(
|
||||
#include "primops/derivation.nix.gen.hh"
|
||||
, foFile, sDerivationNix, "/", staticBaseEnv), v);
|
||||
addConstant("derivation", v);
|
||||
|
||||
/* Now that we've added all primops, sort the `builtins' set,
|
||||
because attribute lookups expect it to be sorted. */
|
||||
baseEnv.values[0]->attrs->sort();
|
||||
|
||||
staticBaseEnv.sort();
|
||||
|
||||
/* Note: we have to initialize the 'derivation' constant *after*
|
||||
building baseEnv/staticBaseEnv because it uses 'builtins'. */
|
||||
eval(parse(
|
||||
#include "primops/derivation.nix.gen.hh"
|
||||
, foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ struct RegisterPrimOp
|
||||
std::vector<std::string> args;
|
||||
size_t arity = 0;
|
||||
const char * doc;
|
||||
std::optional<std::string> requiredFeature;
|
||||
PrimOpFun fun;
|
||||
};
|
||||
|
||||
@@ -27,7 +28,8 @@ struct RegisterPrimOp
|
||||
RegisterPrimOp(
|
||||
std::string name,
|
||||
size_t arity,
|
||||
PrimOpFun fun);
|
||||
PrimOpFun fun,
|
||||
std::optional<std::string> requiredFeature = {});
|
||||
|
||||
RegisterPrimOp(Info && info);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||
std::string name = "source";
|
||||
PathSet context;
|
||||
|
||||
state.forceValue(*args[0], pos);
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
@@ -62,7 +62,6 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||
fetchers::Attrs attrs;
|
||||
attrs.insert_or_assign("type", "hg");
|
||||
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
|
||||
attrs.insert_or_assign("name", name);
|
||||
if (ref) attrs.insert_or_assign("ref", *ref);
|
||||
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
|
||||
auto input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||
@@ -84,7 +83,8 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
|
||||
v.attrs->sort();
|
||||
|
||||
state.allowPath(tree.storePath);
|
||||
if (state.allowedPaths)
|
||||
state.allowedPaths->insert(tree.actualPath);
|
||||
}
|
||||
|
||||
static RegisterPrimOp r_fetchMercurial("fetchMercurial", 1, prim_fetchMercurial);
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -16,8 +15,7 @@ void emitTreeAttrs(
|
||||
const fetchers::Tree & tree,
|
||||
const fetchers::Input & input,
|
||||
Value & v,
|
||||
bool emptyRevFallback,
|
||||
bool forceDirty)
|
||||
bool emptyRevFallback)
|
||||
{
|
||||
assert(input.isImmutable());
|
||||
|
||||
@@ -34,28 +32,24 @@ void emitTreeAttrs(
|
||||
mkString(*state.allocAttr(v, state.symbols.create("narHash")),
|
||||
narHash->to_string(SRI, true));
|
||||
|
||||
if (auto rev = input.getRev()) {
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev->gitRev());
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev->gitShortRev());
|
||||
} else if (emptyRevFallback) {
|
||||
// Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev
|
||||
auto emptyHash = Hash(htSHA1);
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), emptyHash.gitRev());
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), emptyHash.gitShortRev());
|
||||
}
|
||||
|
||||
if (input.getType() == "git")
|
||||
mkBool(*state.allocAttr(v, state.symbols.create("submodules")),
|
||||
fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
|
||||
|
||||
if (!forceDirty) {
|
||||
|
||||
if (auto rev = input.getRev()) {
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev->gitRev());
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev->gitShortRev());
|
||||
} else if (emptyRevFallback) {
|
||||
// Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev
|
||||
auto emptyHash = Hash(htSHA1);
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), emptyHash.gitRev());
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), emptyHash.gitShortRev());
|
||||
}
|
||||
|
||||
if (auto revCount = input.getRevCount())
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
|
||||
else if (emptyRevFallback)
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), 0);
|
||||
|
||||
}
|
||||
if (auto revCount = input.getRevCount())
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
|
||||
else if (emptyRevFallback)
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), 0);
|
||||
|
||||
if (auto lastModified = input.getLastModified()) {
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("lastModified")), *lastModified);
|
||||
@@ -66,71 +60,47 @@ void emitTreeAttrs(
|
||||
v.attrs->sort();
|
||||
}
|
||||
|
||||
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
|
||||
std::string fixURI(std::string uri, EvalState &state)
|
||||
{
|
||||
state.checkURI(uri);
|
||||
return uri.find("://") != std::string::npos ? uri : defaultScheme + "://" + uri;
|
||||
return uri.find("://") != std::string::npos ? uri : "file://" + uri;
|
||||
}
|
||||
|
||||
std::string fixURIForGit(std::string uri, EvalState & state)
|
||||
void addURI(EvalState &state, fetchers::Attrs &attrs, Symbol name, std::string v)
|
||||
{
|
||||
static std::regex scp_uri("([^/].*)@(.*):(.*)");
|
||||
if (uri[0] != '/' && std::regex_match(uri, scp_uri))
|
||||
return fixURI(std::regex_replace(uri, scp_uri, "$1@$2/$3"), state, "ssh");
|
||||
else
|
||||
return fixURI(uri, state);
|
||||
string n(name);
|
||||
attrs.emplace(name, n == "url" ? fixURI(v, state) : v);
|
||||
}
|
||||
|
||||
struct FetchTreeParams {
|
||||
bool emptyRevFallback = false;
|
||||
bool allowNameArgument = false;
|
||||
};
|
||||
|
||||
static void fetchTree(
|
||||
EvalState & state,
|
||||
const Pos & pos,
|
||||
Value * * args,
|
||||
Value & v,
|
||||
std::optional<std::string> type,
|
||||
const FetchTreeParams & params = FetchTreeParams{}
|
||||
EvalState &state,
|
||||
const Pos &pos,
|
||||
Value **args,
|
||||
Value &v,
|
||||
const std::optional<std::string> type,
|
||||
bool emptyRevFallback = false
|
||||
) {
|
||||
fetchers::Input input;
|
||||
PathSet context;
|
||||
|
||||
state.forceValue(*args[0], pos);
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
if (args[0]->type() == nAttrs) {
|
||||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
fetchers::Attrs attrs;
|
||||
|
||||
if (auto aType = args[0]->attrs->get(state.sType)) {
|
||||
if (type)
|
||||
throw Error({
|
||||
.msg = hintfmt("unexpected attribute 'type'"),
|
||||
.errPos = pos
|
||||
});
|
||||
type = state.forceStringNoCtx(*aType->value, *aType->pos);
|
||||
} else if (!type)
|
||||
throw Error({
|
||||
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
attrs.emplace("type", type.value());
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
if (attr.name == state.sType) continue;
|
||||
state.forceValue(*attr.value, *attr.pos);
|
||||
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
||||
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false);
|
||||
attrs.emplace(attr.name,
|
||||
attr.name == "url"
|
||||
? type == "git"
|
||||
? fixURIForGit(s, state)
|
||||
: fixURI(s, state)
|
||||
: s);
|
||||
}
|
||||
state.forceValue(*attr.value);
|
||||
if (attr.value->type() == nPath || attr.value->type() == nString)
|
||||
addURI(
|
||||
state,
|
||||
attrs,
|
||||
attr.name,
|
||||
state.coerceToString(*attr.pos, *attr.value, context, false, false)
|
||||
);
|
||||
else if (attr.value->type() == nString)
|
||||
addURI(state, attrs, attr.name, attr.value->string.s);
|
||||
else if (attr.value->type() == nBool)
|
||||
attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean});
|
||||
else if (attr.value->type() == nInt)
|
||||
@@ -140,24 +110,26 @@ static void fetchTree(
|
||||
attr.name, showType(*attr.value));
|
||||
}
|
||||
|
||||
if (!params.allowNameArgument)
|
||||
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
||||
throw Error({
|
||||
.msg = hintfmt("attribute 'name' isn’t supported in call to 'fetchTree'"),
|
||||
.errPos = pos
|
||||
});
|
||||
if (type)
|
||||
attrs.emplace("type", type.value());
|
||||
|
||||
if (!attrs.count("type"))
|
||||
throw Error({
|
||||
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||
} else {
|
||||
auto url = state.coerceToString(pos, *args[0], context, false, false);
|
||||
auto url = fixURI(state.coerceToString(pos, *args[0], context, false, false), state);
|
||||
|
||||
if (type == "git") {
|
||||
fetchers::Attrs attrs;
|
||||
attrs.emplace("type", "git");
|
||||
attrs.emplace("url", fixURIForGit(url, state));
|
||||
attrs.emplace("url", url);
|
||||
input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||
} else {
|
||||
input = fetchers::Input::fromURL(fixURI(url, state));
|
||||
input = fetchers::Input::fromURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,15 +141,16 @@ static void fetchTree(
|
||||
|
||||
auto [tree, input2] = input.fetch(state.store);
|
||||
|
||||
state.allowPath(tree.storePath);
|
||||
if (state.allowedPaths)
|
||||
state.allowedPaths->insert(tree.actualPath);
|
||||
|
||||
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
|
||||
emitTreeAttrs(state, tree, input2, v, emptyRevFallback);
|
||||
}
|
||||
|
||||
static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
settings.requireExperimentalFeature(Xp::Flakes);
|
||||
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
|
||||
settings.requireExperimentalFeature("flakes");
|
||||
fetchTree(state, pos, args, v, std::nullopt);
|
||||
}
|
||||
|
||||
// FIXME: document
|
||||
@@ -189,7 +162,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||
std::optional<std::string> url;
|
||||
std::optional<Hash> expectedHash;
|
||||
|
||||
state.forceValue(*args[0], pos);
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
@@ -233,18 +206,20 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath
|
||||
: fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath;
|
||||
|
||||
auto path = state.store->toRealPath(storePath);
|
||||
|
||||
if (expectedHash) {
|
||||
auto hash = unpack
|
||||
? state.store->queryPathInfo(storePath)->narHash
|
||||
: hashFile(htSHA256, state.store->toRealPath(storePath));
|
||||
: hashFile(htSHA256, path);
|
||||
if (hash != *expectedHash)
|
||||
throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
|
||||
*url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
|
||||
}
|
||||
|
||||
state.allowPath(storePath);
|
||||
if (state.allowedPaths)
|
||||
state.allowedPaths->insert(path);
|
||||
|
||||
auto path = state.store->printStorePath(storePath);
|
||||
mkString(v, path, PathSet({path}));
|
||||
}
|
||||
|
||||
@@ -287,13 +262,13 @@ static RegisterPrimOp primop_fetchTarball({
|
||||
stdenv.mkDerivation { … }
|
||||
```
|
||||
|
||||
The fetched tarball is cached for a certain amount of time (1
|
||||
hour by default) in `~/.cache/nix/tarballs/`. You can change the
|
||||
cache timeout either on the command line with `--tarball-ttl`
|
||||
*number-of-seconds* or in the Nix configuration file by adding
|
||||
the line `tarball-ttl = ` *number-of-seconds*.
|
||||
The fetched tarball is cached for a certain amount of time (1 hour
|
||||
by default) in `~/.cache/nix/tarballs/`. You can change the cache
|
||||
timeout either on the command line with `--option tarball-ttl number
|
||||
of seconds` or in the Nix configuration file with this option: `
|
||||
number of seconds to cache `.
|
||||
|
||||
Note that when obtaining the hash with `nix-prefetch-url` the
|
||||
Note that when obtaining the hash with ` nix-prefetch-url ` the
|
||||
option `--unpack` is required.
|
||||
|
||||
This function can also verify the contents against a hash. In that
|
||||
@@ -317,7 +292,7 @@ static RegisterPrimOp primop_fetchTarball({
|
||||
|
||||
static void prim_fetchGit(EvalState &state, const Pos &pos, Value **args, Value &v)
|
||||
{
|
||||
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
|
||||
fetchTree(state, pos, args, v, "git", true);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_fetchGit({
|
||||
@@ -393,7 +368,7 @@ static RegisterPrimOp primop_fetchGit({
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
>
|
||||
> It is nice to always specify the branch which a revision
|
||||
> belongs to. Without the branch being specified, the fetcher
|
||||
> might fail if the default branch changes. Additionally, it can
|
||||
@@ -430,12 +405,12 @@ static RegisterPrimOp primop_fetchGit({
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
>
|
||||
> Nix will refetch the branch in accordance with
|
||||
> the option `tarball-ttl`.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
>
|
||||
> This behavior is disabled in *Pure evaluation mode*.
|
||||
)",
|
||||
.fun = prim_fetchGit,
|
||||
|
||||
392
src/libexpr/tree-cache.cc
Normal file
392
src/libexpr/tree-cache.cc
Normal file
@@ -0,0 +1,392 @@
|
||||
#include "tree-cache.hh"
|
||||
#include "sqlite.hh"
|
||||
#include "store-api.hh"
|
||||
#include "context.hh"
|
||||
|
||||
namespace nix::tree_cache {
|
||||
|
||||
static const char * schema = R"sql(
|
||||
create table if not exists Attributes (
|
||||
id integer primary key autoincrement not null,
|
||||
parent integer not null,
|
||||
name text,
|
||||
type integer not null,
|
||||
value text,
|
||||
context text,
|
||||
unique (parent, name)
|
||||
);
|
||||
|
||||
create index if not exists IndexByParent on Attributes(parent, name);
|
||||
)sql";
|
||||
|
||||
struct AttrDb
|
||||
{
|
||||
std::atomic_bool failed{false};
|
||||
|
||||
struct State
|
||||
{
|
||||
SQLite db;
|
||||
SQLiteStmt insertAttribute;
|
||||
SQLiteStmt updateAttribute;
|
||||
SQLiteStmt insertAttributeWithContext;
|
||||
SQLiteStmt queryAttribute;
|
||||
SQLiteStmt queryAttributes;
|
||||
std::unique_ptr<SQLiteTxn> txn;
|
||||
};
|
||||
|
||||
std::unique_ptr<Sync<State>> _state;
|
||||
|
||||
AttrDb(const Hash & fingerprint)
|
||||
: _state(std::make_unique<Sync<State>>())
|
||||
{
|
||||
auto state(_state->lock());
|
||||
|
||||
Path cacheDir = getCacheDir() + "/nix/eval-cache-v3";
|
||||
createDirs(cacheDir);
|
||||
|
||||
Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite";
|
||||
|
||||
state->db = SQLite(dbPath);
|
||||
state->db.isCache();
|
||||
state->db.exec(schema);
|
||||
|
||||
state->insertAttribute.create(state->db,
|
||||
"insert into Attributes(parent, name, type, value) values (?, ?, ?, ?)");
|
||||
|
||||
state->updateAttribute.create(state->db,
|
||||
"update Attributes set type = ?, value = ?, context = ? where id = ?");
|
||||
|
||||
state->insertAttributeWithContext.create(state->db,
|
||||
"insert into Attributes(parent, name, type, value, context) values (?, ?, ?, ?, ?)");
|
||||
|
||||
state->queryAttribute.create(state->db,
|
||||
"select id, type, value, context from Attributes where parent = ? and name = ?");
|
||||
|
||||
state->queryAttributes.create(state->db,
|
||||
"select name, type from Attributes where parent = ?");
|
||||
|
||||
state->txn = std::make_unique<SQLiteTxn>(state->db);
|
||||
}
|
||||
|
||||
~AttrDb()
|
||||
{
|
||||
try {
|
||||
auto state(_state->lock());
|
||||
if (!failed)
|
||||
state->txn->commit();
|
||||
state->txn.reset();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
AttrId doSQLite(F && fun)
|
||||
{
|
||||
if (failed) return 0;
|
||||
try {
|
||||
return fun();
|
||||
} catch (SQLiteError &) {
|
||||
ignoreException();
|
||||
failed = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a leaf of the tree in the db
|
||||
*/
|
||||
AttrId addEntry(
|
||||
const AttrKey & key,
|
||||
const AttrValue & value)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
auto state(_state->lock());
|
||||
auto rawValue = RawValue::fromVariant(value);
|
||||
|
||||
state->insertAttributeWithContext.use()
|
||||
(key.first)
|
||||
(key.second)
|
||||
(rawValue.type)
|
||||
(rawValue.value.value_or(""), rawValue.value.has_value())
|
||||
(rawValue.serializeContext())
|
||||
.exec();
|
||||
AttrId rowId = state->db.getLastInsertedRowId();
|
||||
assert(rowId);
|
||||
return rowId;
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<AttrId> getId(const AttrKey& key)
|
||||
{
|
||||
auto state(_state->lock());
|
||||
|
||||
auto queryAttribute(state->queryAttribute.use()(key.first)(key.second));
|
||||
if (!queryAttribute.next()) return std::nullopt;
|
||||
|
||||
return (AttrType) queryAttribute.getInt(0);
|
||||
}
|
||||
|
||||
AttrId setOrUpdate(const AttrKey& key, const AttrValue& value)
|
||||
{
|
||||
debug("cache: miss for the attribute %s", key.second);
|
||||
if (auto existingId = getId(key)) {
|
||||
setValue(*existingId, value);
|
||||
return *existingId;
|
||||
}
|
||||
return addEntry(key, value);
|
||||
}
|
||||
|
||||
void setValue(const AttrId & id, const AttrValue & value)
|
||||
{
|
||||
auto state(_state->lock());
|
||||
auto rawValue = RawValue::fromVariant(value);
|
||||
|
||||
state->updateAttribute.use()
|
||||
(rawValue.type)
|
||||
(rawValue.value.value_or(""), rawValue.value.has_value())
|
||||
(rawValue.serializeContext())
|
||||
(id)
|
||||
.exec();
|
||||
}
|
||||
|
||||
std::optional<std::pair<AttrId, AttrValue>> getValue(AttrKey key)
|
||||
{
|
||||
auto state(_state->lock());
|
||||
|
||||
auto queryAttribute(state->queryAttribute.use()(key.first)(key.second));
|
||||
if (!queryAttribute.next()) return {};
|
||||
|
||||
auto rowId = (AttrType) queryAttribute.getInt(0);
|
||||
auto type = (AttrType) queryAttribute.getInt(1);
|
||||
|
||||
switch (type) {
|
||||
case AttrType::Attrs: {
|
||||
return {{rowId, attributeSet_t()}};
|
||||
}
|
||||
case AttrType::String: {
|
||||
std::vector<std::pair<Path, std::string>> context;
|
||||
if (!queryAttribute.isNull(3))
|
||||
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
|
||||
context.push_back(decodeContext(s));
|
||||
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
|
||||
}
|
||||
case AttrType::Bool:
|
||||
return {{rowId, wrapped_basetype<bool>{queryAttribute.getInt(2) != 0}}};
|
||||
case AttrType::Int:
|
||||
return {{rowId, wrapped_basetype<int64_t>{queryAttribute.getInt(2)}}};
|
||||
case AttrType::Double:
|
||||
return {{rowId, wrapped_basetype<double>{(double)queryAttribute.getInt(2)}}};
|
||||
case AttrType::Unknown:
|
||||
return {{rowId, unknown_t{}}};
|
||||
case AttrType::Thunk:
|
||||
return {{rowId, thunk_t{}}};
|
||||
case AttrType::Missing:
|
||||
return {{rowId, missing_t{key.second}}};
|
||||
case AttrType::Failed:
|
||||
return {{rowId, failed_t{queryAttribute.getStr(2)}}};
|
||||
default:
|
||||
throw Error("unexpected type in evaluation cache");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> getChildren(AttrId parentId)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
auto state(_state->lock());
|
||||
|
||||
auto queryAttributes(state->queryAttributes.use()(parentId));
|
||||
|
||||
while (queryAttributes.next()) {
|
||||
if (queryAttributes.getInt(1) != AttrType::Missing)
|
||||
res.push_back(queryAttributes.getStr(0));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
Cache::Cache(const Hash & useCache,
|
||||
SymbolTable & symbols)
|
||||
: db(std::make_shared<AttrDb>(useCache))
|
||||
, symbols(symbols)
|
||||
, rootSymbol(symbols.create("%root%"))
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> Cache::tryCreate(const Hash & useCache, SymbolTable & symbols)
|
||||
{
|
||||
try {
|
||||
return std::make_shared<Cache>(useCache, symbols);
|
||||
} catch (SQLiteError &) {
|
||||
ignoreException();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::commit()
|
||||
{
|
||||
if (db) {
|
||||
debug("Saving the cache");
|
||||
auto state(db->_state->lock());
|
||||
if (state->txn->active) {
|
||||
state->txn->commit();
|
||||
state->txn.reset();
|
||||
state->txn = std::make_unique<SQLiteTxn>(state->db);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cursor::Ref Cache::getRoot()
|
||||
{
|
||||
return new Cursor(ref(shared_from_this()), std::nullopt, thunk_t{});
|
||||
}
|
||||
|
||||
Cursor::Cursor(
|
||||
ref<Cache> root,
|
||||
const Parent & parent,
|
||||
const AttrValue& value
|
||||
)
|
||||
: root(root)
|
||||
, parentId(parent ? std::optional{parent->first.cachedValue.first} : std::nullopt)
|
||||
, label(parent ? parent->second : root->rootSymbol)
|
||||
, cachedValue({root->db->setOrUpdate(getKey(), value), value})
|
||||
{
|
||||
}
|
||||
|
||||
Cursor::Cursor(
|
||||
ref<Cache> root,
|
||||
const Parent & parent,
|
||||
const AttrId & id,
|
||||
const AttrValue & value
|
||||
)
|
||||
: root(root)
|
||||
, parentId(parent ? std::optional{parent->first.cachedValue.first} : std::nullopt)
|
||||
, label(parent ? parent->second : root->rootSymbol)
|
||||
, cachedValue({id, value})
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AttrKey Cursor::getKey()
|
||||
{
|
||||
if (!parentId)
|
||||
return {0, root->rootSymbol};
|
||||
return {*parentId, label};
|
||||
}
|
||||
|
||||
AttrValue Cursor::getCachedValue()
|
||||
{
|
||||
return cachedValue.second;
|
||||
}
|
||||
|
||||
void Cursor::setValue(const AttrValue & v)
|
||||
{
|
||||
root->db->setValue(cachedValue.first, v);
|
||||
cachedValue.second = v;
|
||||
}
|
||||
|
||||
Cursor::Ref Cursor::addChild(const Symbol & attrPath, const AttrValue & v)
|
||||
{
|
||||
Parent parent = {{*this, attrPath}};
|
||||
auto childCursor = new Cursor(
|
||||
root,
|
||||
parent,
|
||||
v
|
||||
);
|
||||
return childCursor;
|
||||
}
|
||||
|
||||
std::vector<std::string> Cursor::getChildren()
|
||||
{
|
||||
return root->db->getChildren(cachedValue.first);
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::string>> Cursor::getChildrenAtPath(const std::vector<Symbol> & attrPath)
|
||||
{
|
||||
auto cursorAtPath = findAlongAttrPath(attrPath);
|
||||
if (cursorAtPath)
|
||||
return cursorAtPath->getChildren();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Cursor::Ref Cursor::maybeGetAttr(const Symbol & name)
|
||||
{
|
||||
auto rawAttr = root->db->getValue({cachedValue.first, name});
|
||||
if (rawAttr) {
|
||||
Parent parent = {{*this, name}};
|
||||
debug("cache: hit for the attribute %s", cachedValue.first);
|
||||
return new Cursor (
|
||||
root, parent, rawAttr->first,
|
||||
rawAttr->second);
|
||||
}
|
||||
if (std::holds_alternative<attributeSet_t>(cachedValue.second)) {
|
||||
// If the parent is an attribute set but we're not present in the db,
|
||||
// then we're not a member of this attribute set. So mark as missing
|
||||
return addChild(name, missing_t{name});
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Cursor::Ref Cursor::findAlongAttrPath(const std::vector<Symbol> & attrPath)
|
||||
{
|
||||
auto currentCursor = this;
|
||||
for (auto & currentAccessor : attrPath) {
|
||||
currentCursor = currentCursor->maybeGetAttr(currentAccessor);
|
||||
if (!currentCursor)
|
||||
break;
|
||||
if (std::holds_alternative<missing_t>(currentCursor->cachedValue.second))
|
||||
break;
|
||||
if (std::holds_alternative<failed_t>(currentCursor->cachedValue.second))
|
||||
break;
|
||||
}
|
||||
return currentCursor;
|
||||
}
|
||||
|
||||
const RawValue RawValue::fromVariant(const AttrValue & value)
|
||||
{
|
||||
RawValue res;
|
||||
std::visit(overloaded{
|
||||
[&](attributeSet_t x) { res.type = AttrType::Attrs; },
|
||||
[&](string_t x) {
|
||||
res.type = AttrType::String;
|
||||
res.value = x.first;
|
||||
res.context = x.second;
|
||||
},
|
||||
[&](wrapped_basetype<bool> x) {
|
||||
res.type = AttrType::Bool;
|
||||
res.value = x.value ? "1" : "0";
|
||||
},
|
||||
[&](wrapped_basetype<int64_t> x) {
|
||||
res.type = AttrType::Int;
|
||||
res.value = std::to_string(x.value);
|
||||
},
|
||||
[&](wrapped_basetype<double> x) {
|
||||
res.type = AttrType::Double;
|
||||
res.value = std::to_string(x.value);
|
||||
},
|
||||
[&](unknown_t x) { res.type = AttrType::Unknown; },
|
||||
[&](missing_t x) { res.type = AttrType::Missing; },
|
||||
[&](thunk_t x) { res.type = AttrType::Thunk; },
|
||||
[&](failed_t x) {
|
||||
res.type = AttrType::Failed;
|
||||
res.value = x.error;
|
||||
}
|
||||
}, value);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string RawValue::serializeContext() const
|
||||
{
|
||||
std::string res;
|
||||
for (auto & elt : context) {
|
||||
res.append(encodeContext(elt.second, elt.first));
|
||||
res.push_back(' ');
|
||||
}
|
||||
if (!res.empty())
|
||||
res.pop_back(); // Remove the trailing space
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
156
src/libexpr/tree-cache.hh
Normal file
156
src/libexpr/tree-cache.hh
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* caching for a tree-like data structure (like Nix values)
|
||||
*
|
||||
* The cache is an sqlite db whose rows are the nodes of the tree, with a
|
||||
* pointer to their parent (except for the root of course)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sync.hh"
|
||||
#include "hash.hh"
|
||||
#include "symbol-table.hh"
|
||||
|
||||
#include <functional>
|
||||
#include <variant>
|
||||
|
||||
namespace nix::tree_cache {
|
||||
|
||||
struct AttrDb;
|
||||
class Cursor;
|
||||
|
||||
class Cache : public std::enable_shared_from_this<Cache>
|
||||
{
|
||||
private:
|
||||
friend class Cursor;
|
||||
|
||||
/**
|
||||
* The database holding the cache
|
||||
*/
|
||||
std::shared_ptr<AttrDb> db;
|
||||
|
||||
SymbolTable & symbols;
|
||||
|
||||
/**
|
||||
* Distinguished symbol indicating the root of the tree
|
||||
*/
|
||||
const Symbol rootSymbol;
|
||||
|
||||
public:
|
||||
|
||||
Cache(
|
||||
const Hash & useCache,
|
||||
SymbolTable & symbols
|
||||
);
|
||||
|
||||
static std::shared_ptr<Cache> tryCreate(const Hash & useCache, SymbolTable & symbols);
|
||||
|
||||
Cursor * getRoot();
|
||||
|
||||
/**
|
||||
* Flush the cache to disk
|
||||
*/
|
||||
void commit();
|
||||
};
|
||||
|
||||
enum AttrType {
|
||||
Unknown = 0,
|
||||
Attrs = 1,
|
||||
String = 2,
|
||||
Bool = 3,
|
||||
Int = 4,
|
||||
Double = 5,
|
||||
Thunk = 6,
|
||||
Missing = 7, // Missing fields of attribute sets
|
||||
Failed = 8,
|
||||
};
|
||||
|
||||
struct attributeSet_t {};
|
||||
struct unknown_t {};
|
||||
struct thunk_t {};
|
||||
struct failed_t { string error; };
|
||||
struct missing_t { Symbol attrName; };
|
||||
|
||||
// Putting several different primitive types in an `std::variant` partially
|
||||
// breaks the `std::visit(overloaded{...` hackery because of the implicit cast
|
||||
// from one to another which breaks the exhaustiveness check.
|
||||
// So we wrap them in a trivial class just to force the disambiguation
|
||||
template<typename T>
|
||||
struct wrapped_basetype{ T value; };
|
||||
|
||||
typedef uint64_t AttrId;
|
||||
|
||||
typedef std::pair<AttrId, Symbol> AttrKey;
|
||||
typedef std::pair<std::string, std::vector<std::pair<Path, std::string>>> string_t;
|
||||
|
||||
typedef std::variant<
|
||||
attributeSet_t,
|
||||
string_t,
|
||||
unknown_t,
|
||||
thunk_t,
|
||||
missing_t,
|
||||
failed_t,
|
||||
wrapped_basetype<bool>,
|
||||
wrapped_basetype<int64_t>,
|
||||
wrapped_basetype<double>
|
||||
> AttrValue;
|
||||
|
||||
struct RawValue {
|
||||
AttrType type;
|
||||
std::optional<std::string> value;
|
||||
std::vector<std::pair<Path, std::string>> context;
|
||||
|
||||
std::string serializeContext() const;
|
||||
|
||||
static const RawValue fromVariant(const AttrValue&);
|
||||
AttrValue toVariant() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* View inside the cache.
|
||||
*
|
||||
* A `Cursor` represents a node in the cached tree (be it a leaf or not)
|
||||
*/
|
||||
class Cursor : public std::enable_shared_from_this<Cursor>
|
||||
{
|
||||
/**
|
||||
* The overall cache of which this cursor is a view
|
||||
*/
|
||||
ref<Cache> root;
|
||||
|
||||
typedef std::optional<std::pair<Cursor&, Symbol>> Parent;
|
||||
|
||||
std::optional<AttrId> parentId;
|
||||
Symbol label;
|
||||
|
||||
std::pair<AttrId, AttrValue> cachedValue;
|
||||
|
||||
/**
|
||||
* Get the identifier for this node in the database
|
||||
*/
|
||||
AttrKey getKey();
|
||||
|
||||
public:
|
||||
|
||||
using Ref = Cursor*;
|
||||
|
||||
// Create a new cache entry
|
||||
Cursor(ref<Cache> root, const Parent & parent, const AttrValue&);
|
||||
// Build a cursor from an existing cache entry
|
||||
Cursor(ref<Cache> root, const Parent & parent, const AttrId& id, const AttrValue&);
|
||||
|
||||
AttrValue getCachedValue();
|
||||
|
||||
void setValue(const AttrValue & v);
|
||||
|
||||
Ref addChild(const Symbol & attrPath, const AttrValue & v);
|
||||
|
||||
Ref findAlongAttrPath(const std::vector<Symbol> & attrPath);
|
||||
Ref maybeGetAttr(const Symbol & attrPath);
|
||||
|
||||
|
||||
std::vector<std::string> getChildren();
|
||||
std::optional<std::vector<std::string>> getChildrenAtPath(const std::vector<Symbol> & attrPath);
|
||||
};
|
||||
|
||||
}
|
||||
51
src/libexpr/value-cache.hh
Normal file
51
src/libexpr/value-cache.hh
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "tree-cache.hh"
|
||||
|
||||
namespace nix {
|
||||
struct Value;
|
||||
class EvalState;
|
||||
class Bindings;
|
||||
|
||||
class ValueCache {
|
||||
tree_cache::Cursor::Ref rawCache;
|
||||
|
||||
public:
|
||||
|
||||
ValueCache(tree_cache::Cursor::Ref rawCache) : rawCache(rawCache) {}
|
||||
|
||||
static ValueCache empty;
|
||||
|
||||
bool isEmpty () { return rawCache == nullptr; }
|
||||
|
||||
enum ReturnCode {
|
||||
// The cache result was an attribute set, so we forward it later in the
|
||||
// chain
|
||||
Forward,
|
||||
CacheMiss,
|
||||
CacheHit,
|
||||
UnCacheable,
|
||||
NoCacheKey,
|
||||
};
|
||||
|
||||
struct CacheResult {
|
||||
ReturnCode returnCode;
|
||||
|
||||
// In case the query returns a `missing_t`, the symbol that's missing
|
||||
std::optional<Symbol> lastQueriedSymbolIfMissing;
|
||||
};
|
||||
std::pair<CacheResult, ValueCache> getValue(EvalState & state, const std::vector<Symbol> & selector, Value & dest);
|
||||
|
||||
ValueCache addChild(const Symbol & attrName, Value & value);
|
||||
ValueCache addFailedChild(const Symbol & attrName, const Error & error);
|
||||
ValueCache addNumChild(SymbolTable & symbols, int idx, const Value & value);
|
||||
void addAttrSetChilds(Bindings & children);
|
||||
void addListChilds(SymbolTable & symbols, Value** elems, int listSize);
|
||||
|
||||
std::optional<std::vector<Symbol>> listChildren(SymbolTable&);
|
||||
std::optional<std::vector<Symbol>> listChildrenAtPath(SymbolTable&, const std::vector<Symbol> & attrPath);
|
||||
|
||||
std::optional<tree_cache::AttrValue> getRawValue();
|
||||
|
||||
ValueCache() : rawCache(nullptr) {}
|
||||
};
|
||||
}
|
||||
@@ -42,7 +42,7 @@ static void showAttrs(EvalState & state, bool strict, bool location,
|
||||
|
||||
XMLAttrs xmlAttrs;
|
||||
xmlAttrs["name"] = i;
|
||||
if (location && a.pos != ptr(&noPos)) posToXML(xmlAttrs, *a.pos);
|
||||
if (location && a.pos != &noPos) posToXML(xmlAttrs, *a.pos);
|
||||
|
||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||
printValueAsXML(state, strict, location,
|
||||
@@ -126,28 +126,24 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
}
|
||||
|
||||
case nFunction: {
|
||||
|
||||
if (!v.isLambda()) {
|
||||
// FIXME: Serialize primops and partial apps
|
||||
// FIXME: Serialize primops and primopapps
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
break;
|
||||
}
|
||||
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
||||
auto & arg = v.lambda.fun->args[0];
|
||||
|
||||
if (arg.formals) {
|
||||
if (v.lambda.fun->matchAttrs) {
|
||||
XMLAttrs attrs;
|
||||
if (arg.arg != state.sEpsilon) attrs["name"] = arg.arg;
|
||||
if (arg.formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
XMLOpenElement _(doc, "attrspat", attrs);
|
||||
for (auto & i : arg.formals->formals)
|
||||
for (auto & i : v.lambda.fun->formals->formals)
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
|
||||
} else
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", arg.arg));
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ typedef enum {
|
||||
tListN,
|
||||
tThunk,
|
||||
tApp,
|
||||
tPartialApp,
|
||||
tLambda,
|
||||
tBlackhole,
|
||||
tCachedThunk,
|
||||
tPrimOp,
|
||||
tPrimOpApp,
|
||||
tExternal,
|
||||
@@ -57,6 +57,7 @@ struct Pos;
|
||||
class EvalState;
|
||||
class XMLWriter;
|
||||
class JSONPlaceholder;
|
||||
class ValueCache;
|
||||
|
||||
|
||||
typedef int64_t NixInt;
|
||||
@@ -104,6 +105,10 @@ class ExternalValueBase
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
|
||||
|
||||
struct Thunk {
|
||||
Env * env;
|
||||
Expr * expr;
|
||||
};
|
||||
|
||||
struct Value
|
||||
{
|
||||
@@ -123,10 +128,10 @@ public:
|
||||
inline bool isThunk() const { return internalType == tThunk; };
|
||||
inline bool isApp() const { return internalType == tApp; };
|
||||
inline bool isBlackhole() const { return internalType == tBlackhole; };
|
||||
inline bool isCachedThunk() const { return internalType == tCachedThunk; };
|
||||
|
||||
// type() == nFunction
|
||||
inline bool isLambda() const { return internalType == tLambda; };
|
||||
inline bool isPartialApp() const { return internalType == tPartialApp; };
|
||||
inline bool isPrimOp() const { return internalType == tPrimOp; };
|
||||
inline bool isPrimOpApp() const { return internalType == tPrimOpApp; };
|
||||
|
||||
@@ -167,10 +172,13 @@ public:
|
||||
Value * * elems;
|
||||
} bigList;
|
||||
Value * smallList[2];
|
||||
Thunk thunk;
|
||||
struct {
|
||||
Env * env;
|
||||
Expr * expr;
|
||||
} thunk;
|
||||
Thunk * thunk;
|
||||
// This is a pointer only to prevent a recursive import as
|
||||
// `EvalCache` is already a pointer so would fit very nicely here.
|
||||
ValueCache * cache;
|
||||
} cachedThunk;
|
||||
struct {
|
||||
Value * left, * right;
|
||||
} app;
|
||||
@@ -198,10 +206,10 @@ public:
|
||||
case tNull: return nNull;
|
||||
case tAttrs: return nAttrs;
|
||||
case tList1: case tList2: case tListN: return nList;
|
||||
case tLambda: case tPartialApp: case tPrimOp: case tPrimOpApp: return nFunction;
|
||||
case tLambda: case tPrimOp: case tPrimOpApp: return nFunction;
|
||||
case tExternal: return nExternal;
|
||||
case tFloat: return nFloat;
|
||||
case tThunk: case tApp: case tBlackhole: return nThunk;
|
||||
case tThunk: case tApp: case tBlackhole: case tCachedThunk: return nThunk;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
@@ -274,6 +282,13 @@ public:
|
||||
thunk.expr = ex;
|
||||
}
|
||||
|
||||
inline void mkCachedThunk(Thunk * t, ValueCache * cache)
|
||||
{
|
||||
internalType = tCachedThunk;
|
||||
cachedThunk.thunk = t;
|
||||
cachedThunk.cache = cache;
|
||||
}
|
||||
|
||||
inline void mkApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tApp;
|
||||
@@ -309,13 +324,6 @@ public:
|
||||
app.right = r;
|
||||
}
|
||||
|
||||
inline void mkPartialApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tPartialApp;
|
||||
app.left = l;
|
||||
app.right = r;
|
||||
}
|
||||
|
||||
inline void mkExternal(ExternalValueBase * e)
|
||||
{
|
||||
clearValue();
|
||||
@@ -358,6 +366,9 @@ public:
|
||||
bool isTrivial() const;
|
||||
|
||||
std::vector<std::pair<Path, std::string>> getContext();
|
||||
|
||||
ValueCache & getEvalCache();
|
||||
void setEvalCache(ValueCache &);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -200,17 +200,12 @@ void Input::markChangedFile(
|
||||
return scheme->markChangedFile(*this, file, commitMsg);
|
||||
}
|
||||
|
||||
std::string Input::getName() const
|
||||
{
|
||||
return maybeGetStrAttr(attrs, "name").value_or("source");
|
||||
}
|
||||
|
||||
StorePath Input::computeStorePath(Store & store) const
|
||||
{
|
||||
auto narHash = getNarHash();
|
||||
if (!narHash)
|
||||
throw Error("cannot compute store path for mutable input '%s'", to_string());
|
||||
return store.makeFixedOutputPath(FileIngestionMethod::Recursive, *narHash, getName());
|
||||
return store.makeFixedOutputPath(FileIngestionMethod::Recursive, *narHash, "source");
|
||||
}
|
||||
|
||||
std::string Input::getType() const
|
||||
|
||||
@@ -38,9 +38,6 @@ struct Input
|
||||
bool immutable = false;
|
||||
bool direct = true;
|
||||
|
||||
/* path of the parent of this input, used for relative path resolution */
|
||||
std::optional<Path> parent;
|
||||
|
||||
public:
|
||||
static Input fromURL(const std::string & url);
|
||||
|
||||
@@ -84,8 +81,6 @@ public:
|
||||
std::string_view file,
|
||||
std::optional<std::string> commitMsg) const;
|
||||
|
||||
std::string getName() const;
|
||||
|
||||
StorePath computeStorePath(Store & store) const;
|
||||
|
||||
// Convenience functions for common attributes.
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "tarfile.hh"
|
||||
#include "store-api.hh"
|
||||
#include "url-parts.hh"
|
||||
#include "pathlocks.hh"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -13,12 +12,6 @@ using namespace std::string_literals;
|
||||
|
||||
namespace nix::fetchers {
|
||||
|
||||
// Explicit initial branch of our bare repo to suppress warnings from new version of git.
|
||||
// The value itself does not matter, since we always fetch a specific revision or branch.
|
||||
// It is set with `-c init.defaultBranch=` instead of `--initial-branch=` to stay compatible with
|
||||
// old version of git, which will ignore unrecognized `-c` options.
|
||||
const std::string gitInitialBranch = "__nix_dummy_branch";
|
||||
|
||||
static std::string readHead(const Path & path)
|
||||
{
|
||||
return chomp(runProgram("git", true, { "-C", path, "rev-parse", "--abbrev-ref", "HEAD" }));
|
||||
@@ -67,7 +60,7 @@ struct GitInputScheme : InputScheme
|
||||
if (maybeGetStrAttr(attrs, "type") != "git") return {};
|
||||
|
||||
for (auto & [name, value] : attrs)
|
||||
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "shallow" && name != "submodules" && name != "lastModified" && name != "revCount" && name != "narHash" && name != "allRefs" && name != "name")
|
||||
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "shallow" && name != "submodules" && name != "lastModified" && name != "revCount" && name != "narHash" && name != "allRefs")
|
||||
throw Error("unsupported Git input attribute '%s'", name);
|
||||
|
||||
parseURL(getStrAttr(attrs, "url"));
|
||||
@@ -174,9 +167,9 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
||||
{
|
||||
Input input(_input);
|
||||
auto name = "source";
|
||||
|
||||
std::string name = input.getName();
|
||||
Input input(_input);
|
||||
|
||||
bool shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false);
|
||||
bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false);
|
||||
@@ -277,7 +270,7 @@ struct GitInputScheme : InputScheme
|
||||
return files.count(file);
|
||||
};
|
||||
|
||||
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
|
||||
// FIXME: maybe we should use the timestamp of the last
|
||||
// modified dirty file?
|
||||
@@ -324,17 +317,11 @@ struct GitInputScheme : InputScheme
|
||||
Path cacheDir = getCacheDir() + "/nix/gitv3/" + hashString(htSHA256, actualUrl).to_string(Base32, false);
|
||||
repoDir = cacheDir;
|
||||
|
||||
Path cacheDirLock = cacheDir + ".lock";
|
||||
createDirs(dirOf(cacheDir));
|
||||
AutoCloseFD lock = openLockFile(cacheDirLock, true);
|
||||
lockFile(lock.get(), ltWrite, true);
|
||||
|
||||
if (!pathExists(cacheDir)) {
|
||||
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", "--bare", repoDir });
|
||||
createDirs(dirOf(cacheDir));
|
||||
runProgram("git", true, { "init", "--bare", repoDir });
|
||||
}
|
||||
|
||||
deleteLockFile(cacheDirLock, lock.get());
|
||||
|
||||
Path localRefFile =
|
||||
input.getRef()->compare(0, 5, "refs/") == 0
|
||||
? cacheDir + "/" + *input.getRef()
|
||||
@@ -419,14 +406,17 @@ struct GitInputScheme : InputScheme
|
||||
AutoDelete delTmpDir(tmpDir, true);
|
||||
PathFilter filter = defaultPathFilter;
|
||||
|
||||
auto result = runProgram(RunOptions {
|
||||
.program = "git",
|
||||
.args = { "-C", repoDir, "cat-file", "commit", input.getRev()->gitRev() },
|
||||
.mergeStderrToStdout = true
|
||||
});
|
||||
RunOptions checkCommitOpts(
|
||||
"git",
|
||||
{ "-C", repoDir, "cat-file", "commit", input.getRev()->gitRev() }
|
||||
);
|
||||
checkCommitOpts.searchPath = true;
|
||||
checkCommitOpts.mergeStderrToStdout = true;
|
||||
|
||||
auto result = runProgram(checkCommitOpts);
|
||||
if (WEXITSTATUS(result.first) == 128
|
||||
&& result.second.find("bad file") != std::string::npos)
|
||||
{
|
||||
&& result.second.find("bad file") != std::string::npos
|
||||
) {
|
||||
throw Error(
|
||||
"Cannot find Git revision '%s' in ref '%s' of repository '%s'! "
|
||||
"Please make sure that the " ANSI_BOLD "rev" ANSI_NORMAL " exists on the "
|
||||
@@ -442,7 +432,7 @@ struct GitInputScheme : InputScheme
|
||||
Path tmpGitDir = createTempDir();
|
||||
AutoDelete delTmpGitDir(tmpGitDir, true);
|
||||
|
||||
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", tmpDir, "--separate-git-dir", tmpGitDir });
|
||||
runProgram("git", true, { "init", tmpDir, "--separate-git-dir", tmpGitDir });
|
||||
// TODO: repoDir might lack the ref (it only checks if rev
|
||||
// exists, see FIXME above) so use a big hammer and fetch
|
||||
// everything to ensure we get the rev.
|
||||
@@ -458,11 +448,9 @@ struct GitInputScheme : InputScheme
|
||||
// FIXME: should pipe this, or find some better way to extract a
|
||||
// revision.
|
||||
auto source = sinkToSource([&](Sink & sink) {
|
||||
runProgram2({
|
||||
.program = "git",
|
||||
.args = { "-C", repoDir, "archive", input.getRev()->gitRev() },
|
||||
.standardOut = &sink
|
||||
});
|
||||
RunOptions gitOptions("git", { "-C", repoDir, "archive", input.getRev()->gitRev() });
|
||||
gitOptions.standardOut = &sink;
|
||||
runProgram2(gitOptions);
|
||||
});
|
||||
|
||||
unpackTarfile(*source, tmpDir);
|
||||
|
||||
@@ -207,7 +207,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
|
||||
auto url = getDownloadUrl(input);
|
||||
|
||||
auto [tree, lastModified] = downloadTarball(store, url.url, input.getName(), true, url.headers);
|
||||
auto [tree, lastModified] = downloadTarball(store, url.url, "source", true, url.headers);
|
||||
|
||||
input.attrs.insert_or_assign("lastModified", uint64_t(lastModified));
|
||||
|
||||
@@ -273,9 +273,9 @@ struct GitHubInputScheme : GitArchiveInputScheme
|
||||
void clone(const Input & input, const Path & destDir) override
|
||||
{
|
||||
auto host = maybeGetStrAttr(input.attrs, "host").value_or("github.com");
|
||||
Input::fromURL(fmt("git+https://%s/%s/%s.git",
|
||||
Input::fromURL(fmt("git+ssh://git@%s/%s/%s.git",
|
||||
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
|
||||
.applyOverrides(input.getRef(), input.getRev())
|
||||
.applyOverrides(input.getRef().value_or("HEAD"), input.getRev())
|
||||
.clone(destDir);
|
||||
}
|
||||
};
|
||||
@@ -341,9 +341,9 @@ struct GitLabInputScheme : GitArchiveInputScheme
|
||||
{
|
||||
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
|
||||
// FIXME: get username somewhere
|
||||
Input::fromURL(fmt("git+https://%s/%s/%s.git",
|
||||
Input::fromURL(fmt("git+ssh://git@%s/%s/%s.git",
|
||||
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
|
||||
.applyOverrides(input.getRef(), input.getRev())
|
||||
.applyOverrides(input.getRef().value_or("HEAD"), input.getRev())
|
||||
.clone(destDir);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,6 +8,4 @@ libfetchers_SOURCES := $(wildcard $(d)/*.cc)
|
||||
|
||||
libfetchers_CXXFLAGS += -I src/libutil -I src/libstore
|
||||
|
||||
libfetchers_LDFLAGS += -pthread
|
||||
|
||||
libfetchers_LIBS = libutil libstore
|
||||
|
||||
@@ -11,32 +11,34 @@ using namespace std::string_literals;
|
||||
|
||||
namespace nix::fetchers {
|
||||
|
||||
static RunOptions hgOptions(const Strings & args)
|
||||
{
|
||||
auto env = getEnv();
|
||||
// Set HGPLAIN: this means we get consistent output from hg and avoids leakage from a user or system .hgrc.
|
||||
env["HGPLAIN"] = "";
|
||||
namespace {
|
||||
|
||||
return {
|
||||
.program = "hg",
|
||||
.searchPath = true,
|
||||
.args = args,
|
||||
.environment = env
|
||||
};
|
||||
RunOptions hgOptions(const Strings & args) {
|
||||
RunOptions opts("hg", args);
|
||||
opts.searchPath = true;
|
||||
|
||||
auto env = getEnv();
|
||||
// Set HGPLAIN: this means we get consistent output from hg and avoids leakage from a user or system .hgrc.
|
||||
env["HGPLAIN"] = "";
|
||||
opts.environment = env;
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
// runProgram wrapper that uses hgOptions instead of stock RunOptions.
|
||||
static string runHg(const Strings & args, const std::optional<std::string> & input = {})
|
||||
string runHg(const Strings & args, const std::optional<std::string> & input = {})
|
||||
{
|
||||
RunOptions opts = hgOptions(args);
|
||||
opts.input = input;
|
||||
RunOptions opts = hgOptions(args);
|
||||
opts.input = input;
|
||||
|
||||
auto res = runProgram(std::move(opts));
|
||||
auto res = runProgram(opts);
|
||||
|
||||
if (!statusOk(res.first))
|
||||
throw ExecError(res.first, fmt("hg %1%", statusToString(res.first)));
|
||||
if (!statusOk(res.first))
|
||||
throw ExecError(res.first, fmt("hg %1%", statusToString(res.first)));
|
||||
|
||||
return res.second;
|
||||
}
|
||||
|
||||
return res.second;
|
||||
}
|
||||
|
||||
struct MercurialInputScheme : InputScheme
|
||||
@@ -72,7 +74,7 @@ struct MercurialInputScheme : InputScheme
|
||||
if (maybeGetStrAttr(attrs, "type") != "hg") return {};
|
||||
|
||||
for (auto & [name, value] : attrs)
|
||||
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "revCount" && name != "narHash" && name != "name")
|
||||
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "revCount" && name != "narHash")
|
||||
throw Error("unsupported Mercurial input attribute '%s'", name);
|
||||
|
||||
parseURL(getStrAttr(attrs, "url"));
|
||||
@@ -145,9 +147,9 @@ struct MercurialInputScheme : InputScheme
|
||||
|
||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
||||
{
|
||||
Input input(_input);
|
||||
auto name = "source";
|
||||
|
||||
auto name = input.getName();
|
||||
Input input(_input);
|
||||
|
||||
auto [isLocal, actualUrl_] = getActualUrl(input);
|
||||
auto actualUrl = actualUrl_; // work around clang bug
|
||||
@@ -191,7 +193,7 @@ struct MercurialInputScheme : InputScheme
|
||||
return files.count(file);
|
||||
};
|
||||
|
||||
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
|
||||
return {
|
||||
Tree(store->toRealPath(storePath), std::move(storePath)),
|
||||
@@ -251,7 +253,9 @@ struct MercurialInputScheme : InputScheme
|
||||
have to pull again. */
|
||||
if (!(input.getRev()
|
||||
&& pathExists(cacheDir)
|
||||
&& runProgram(hgOptions({ "log", "-R", cacheDir, "-r", input.getRev()->gitRev(), "--template", "1" })).second == "1"))
|
||||
&& runProgram(
|
||||
hgOptions({ "log", "-R", cacheDir, "-r", input.getRev()->gitRev(), "--template", "1" })
|
||||
.killStderr(true)).second == "1"))
|
||||
{
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl));
|
||||
|
||||
|
||||
@@ -82,38 +82,18 @@ struct PathInputScheme : InputScheme
|
||||
|
||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
|
||||
{
|
||||
std::string absPath;
|
||||
auto path = getStrAttr(input.attrs, "path");
|
||||
|
||||
if (path[0] != '/') {
|
||||
if (!input.parent)
|
||||
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
||||
|
||||
auto parent = canonPath(*input.parent);
|
||||
|
||||
// the path isn't relative, prefix it
|
||||
absPath = nix::absPath(path, parent);
|
||||
|
||||
// for security, ensure that if the parent is a store path, it's inside it
|
||||
if (store->isInStore(parent)) {
|
||||
auto storePath = store->printStorePath(store->toStorePath(parent).first);
|
||||
if (!isInDir(absPath, storePath))
|
||||
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
||||
}
|
||||
} else
|
||||
absPath = path;
|
||||
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s'", absPath));
|
||||
|
||||
// FIXME: check whether access to 'path' is allowed.
|
||||
auto storePath = store->maybeParseStorePath(absPath);
|
||||
|
||||
auto storePath = store->maybeParseStorePath(path);
|
||||
|
||||
if (storePath)
|
||||
store->addTempRoot(*storePath);
|
||||
|
||||
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath))
|
||||
// FIXME: try to substitute storePath.
|
||||
storePath = store->addToStore("source", absPath);
|
||||
storePath = store->addToStore("source", path);
|
||||
|
||||
return {
|
||||
Tree(store->toRealPath(*storePath), std::move(*storePath)),
|
||||
|
||||
@@ -124,13 +124,6 @@ std::shared_ptr<Registry> getUserRegistry()
|
||||
return userRegistry;
|
||||
}
|
||||
|
||||
std::shared_ptr<Registry> getCustomRegistry(const Path & p)
|
||||
{
|
||||
static auto customRegistry =
|
||||
Registry::read(p, Registry::Custom);
|
||||
return customRegistry;
|
||||
}
|
||||
|
||||
static std::shared_ptr<Registry> flagRegistry =
|
||||
std::make_shared<Registry>(Registry::Flag);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user