mirror of
https://github.com/NixOS/nix
synced 2025-06-29 06:21:14 +02:00
Merge remote-tracking branch 'origin/master' into lazy-trees
This commit is contained in:
commit
12f141391c
63 changed files with 819 additions and 312 deletions
2
Makefile
2
Makefile
|
@ -36,4 +36,4 @@ endif
|
||||||
|
|
||||||
include mk/lib.mk
|
include mk/lib.mk
|
||||||
|
|
||||||
GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++20 -I src
|
GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++2a -I src
|
||||||
|
|
|
@ -5,3 +5,7 @@ h1:not(:first-of-type) {
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hljs-meta {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
|
@ -51,13 +51,13 @@ $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli
|
||||||
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
|
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
|
||||||
@rm -rf $@
|
@rm -rf $@
|
||||||
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix { toplevel = builtins.readFile $<; }'
|
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix { toplevel = builtins.readFile $<; }'
|
||||||
# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
@# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
||||||
$(trace-gen) sed -i $@.tmp/*.md -e 's^@docroot@^../..^g'
|
$(trace-gen) sed -i $@.tmp/*.md -e 's^@docroot@^../..^g'
|
||||||
@mv $@.tmp $@
|
@mv $@.tmp $@
|
||||||
|
|
||||||
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
|
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
|
||||||
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
|
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
|
||||||
# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
@# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
||||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-options.nix (builtins.fromJSON (builtins.readFile $<))' \
|
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-options.nix (builtins.fromJSON (builtins.readFile $<))' \
|
||||||
| sed -e 's^@docroot@^..^g'>> $@.tmp
|
| sed -e 's^@docroot@^..^g'>> $@.tmp
|
||||||
@mv $@.tmp $@
|
@mv $@.tmp $@
|
||||||
|
@ -72,7 +72,7 @@ $(d)/conf-file.json: $(bindir)/nix
|
||||||
|
|
||||||
$(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
|
$(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
|
||||||
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
|
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
|
||||||
# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
@# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
||||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' \
|
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' \
|
||||||
| sed -e 's^@docroot@^..^g' >> $@.tmp
|
| sed -e 's^@docroot@^..^g' >> $@.tmp
|
||||||
@cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
|
@cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
|
||||||
|
|
|
@ -91,3 +91,16 @@ Most Nix commands interpret the following environment variables:
|
||||||
variable sets the initial size of the heap in bytes. It defaults to
|
variable sets the initial size of the heap in bytes. It defaults to
|
||||||
384 MiB. Setting it to a low value reduces memory consumption, but
|
384 MiB. Setting it to a low value reduces memory consumption, but
|
||||||
will increase runtime due to the overhead of garbage collection.
|
will increase runtime due to the overhead of garbage collection.
|
||||||
|
|
||||||
|
## XDG Base Directory
|
||||||
|
|
||||||
|
New Nix commands conform to the [XDG Base Directory Specification], and use the following environment variables to determine locations of various state and configuration files:
|
||||||
|
|
||||||
|
- [`XDG_CONFIG_HOME`]{#env-XDG_CONFIG_HOME} (default `~/.config`)
|
||||||
|
- [`XDG_STATE_HOME`]{#env-XDG_STATE_HOME} (default `~/.local/state`)
|
||||||
|
- [`XDG_CACHE_HOME`]{#env-XDG_CACHE_HOME} (default `~/.cache`)
|
||||||
|
|
||||||
|
Classic Nix commands can also be made to follow this standard using the [`use-xdg-base-directories`] configuration option.
|
||||||
|
|
||||||
|
[XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
[`use-xdg-base-directories`]: ../command-ref/conf-file.md#conf-use-xdg-base-directories
|
|
@ -91,7 +91,7 @@ This is an incomplete overview of language features, by example.
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
`"hello ${ { a = "world" }.a }"`
|
`"hello ${ { a = "world"; }.a }"`
|
||||||
|
|
||||||
`"1 2 ${toString 3}"`
|
`"1 2 ${toString 3}"`
|
||||||
|
|
||||||
|
|
|
@ -500,6 +500,8 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
# System tests.
|
# System tests.
|
||||||
|
tests.authorization = runNixOSTestFor "x86_64-linux" ./tests/nixos/authorization.nix;
|
||||||
|
|
||||||
tests.remoteBuilds = runNixOSTestFor "x86_64-linux" ./tests/nixos/remote-builds.nix;
|
tests.remoteBuilds = runNixOSTestFor "x86_64-linux" ./tests/nixos/remote-builds.nix;
|
||||||
|
|
||||||
tests.nix-copy-closure = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy-closure.nix;
|
tests.nix-copy-closure = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy-closure.nix;
|
||||||
|
|
|
@ -67,6 +67,7 @@ define build-library
|
||||||
|
|
||||||
$(1)_LDFLAGS_USE :=
|
$(1)_LDFLAGS_USE :=
|
||||||
$(1)_LDFLAGS_USE_INSTALLED :=
|
$(1)_LDFLAGS_USE_INSTALLED :=
|
||||||
|
$(1)_LIB_CLOSURE := $(1)
|
||||||
|
|
||||||
$$(eval $$(call create-dir, $$(_d)))
|
$$(eval $$(call create-dir, $$(_d)))
|
||||||
|
|
||||||
|
@ -128,10 +129,12 @@ define build-library
|
||||||
+$$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$^
|
+$$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$^
|
||||||
$$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
|
$$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
|
||||||
|
|
||||||
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS)
|
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE))
|
||||||
|
|
||||||
$(1)_INSTALL_PATH := $$(libdir)/$$($(1)_NAME).a
|
$(1)_INSTALL_PATH := $$(libdir)/$$($(1)_NAME).a
|
||||||
|
|
||||||
|
$(1)_LIB_CLOSURE += $$($(1)_LIBS)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(1)_LDFLAGS_USE += $$($(1)_LDFLAGS_PROPAGATED)
|
$(1)_LDFLAGS_USE += $$($(1)_LDFLAGS_PROPAGATED)
|
||||||
|
|
|
@ -30,7 +30,7 @@ define build-program
|
||||||
_d := $(buildprefix)$$($(1)_DIR)
|
_d := $(buildprefix)$$($(1)_DIR)
|
||||||
_srcs := $$(sort $$(foreach src, $$($(1)_SOURCES), $$(src)))
|
_srcs := $$(sort $$(foreach src, $$($(1)_SOURCES), $$(src)))
|
||||||
$(1)_OBJS := $$(addprefix $(buildprefix), $$(addsuffix .o, $$(basename $$(_srcs))))
|
$(1)_OBJS := $$(addprefix $(buildprefix), $$(addsuffix .o, $$(basename $$(_srcs))))
|
||||||
_libs := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_PATH))
|
_libs := $$(foreach lib, $$($(1)_LIBS), $$(foreach lib2, $$($$(lib)_LIB_CLOSURE), $$($$(lib2)_PATH)))
|
||||||
$(1)_PATH := $$(_d)/$$($(1)_NAME)
|
$(1)_PATH := $$(_d)/$$($(1)_NAME)
|
||||||
|
|
||||||
$$(eval $$(call create-dir, $$(_d)))
|
$$(eval $$(call create-dir, $$(_d)))
|
||||||
|
@ -58,7 +58,7 @@ define build-program
|
||||||
else
|
else
|
||||||
|
|
||||||
$(DESTDIR)$$($(1)_INSTALL_PATH): $$($(1)_PATH) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
|
$(DESTDIR)$$($(1)_INSTALL_PATH): $$($(1)_PATH) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
|
||||||
install -t $(DESTDIR)$$($(1)_INSTALL_DIR) $$<
|
+$$(trace-install) install -t $(DESTDIR)$$($(1)_INSTALL_DIR) $$<
|
||||||
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
makefiles = local.mk
|
makefiles = local.mk
|
||||||
|
|
||||||
GLOBAL_CXXFLAGS += -g -Wall -std=c++20 -I ../src
|
GLOBAL_CXXFLAGS += -g -Wall -std=c++2a -I ../src
|
||||||
|
|
||||||
-include Makefile.config
|
-include Makefile.config
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ EOF
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
$step. Delete the files Nix added to your system:
|
$step. Delete the files Nix added to your system:
|
||||||
|
|
||||||
sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels
|
sudo rm -rf "/etc/nix" "$NIX_ROOT" "$ROOT_HOME/.nix-profile" "$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.local/state/nix" "$ROOT_HOME/.cache/nix" "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels" "$HOME/.local/state/nix" "$HOME/.cache/nix"
|
||||||
|
|
||||||
and that is it.
|
and that is it.
|
||||||
|
|
||||||
|
|
|
@ -188,6 +188,8 @@ fi
|
||||||
# shellcheck source=./nix-profile.sh.in
|
# shellcheck source=./nix-profile.sh.in
|
||||||
. "$nix/etc/profile.d/nix.sh"
|
. "$nix/etc/profile.d/nix.sh"
|
||||||
|
|
||||||
|
NIX_LINK="$HOME/.nix-profile"
|
||||||
|
|
||||||
if ! "$nix/bin/nix-env" -i "$nix"; then
|
if ! "$nix/bin/nix-env" -i "$nix"; then
|
||||||
echo "$0: unable to install Nix into your default profile" >&2
|
echo "$0: unable to install Nix into your default profile" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -196,7 +198,7 @@ fi
|
||||||
# Install an SSL certificate bundle.
|
# Install an SSL certificate bundle.
|
||||||
if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
|
if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
|
||||||
"$nix/bin/nix-env" -i "$cacert"
|
"$nix/bin/nix-env" -i "$cacert"
|
||||||
export NIX_SSL_CERT_FILE="$HOME/.nix-profile/etc/ssl/certs/ca-bundle.crt"
|
export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Subscribe the user to the Nixpkgs channel and fetch it.
|
# Subscribe the user to the Nixpkgs channel and fetch it.
|
||||||
|
@ -214,8 +216,8 @@ fi
|
||||||
|
|
||||||
added=
|
added=
|
||||||
p=
|
p=
|
||||||
p_sh=$HOME/.nix-profile/etc/profile.d/nix.sh
|
p_sh=$NIX_LINK/etc/profile.d/nix.sh
|
||||||
p_fish=$HOME/.nix-profile/etc/profile.d/nix.fish
|
p_fish=$NIX_LINK/etc/profile.d/nix.fish
|
||||||
if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
|
if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
|
||||||
# Make the shell source nix.sh during login.
|
# Make the shell source nix.sh during login.
|
||||||
for i in .bash_profile .bash_login .profile; do
|
for i in .bash_profile .bash_login .profile; do
|
||||||
|
|
|
@ -2,7 +2,33 @@
|
||||||
if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
|
if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
|
||||||
__ETC_PROFILE_NIX_SOURCED=1
|
__ETC_PROFILE_NIX_SOURCED=1
|
||||||
|
|
||||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
NIX_LINK=$HOME/.nix-profile
|
||||||
|
if [ -n "$XDG_STATE_HOME" ]; then
|
||||||
|
NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile"
|
||||||
|
else
|
||||||
|
NIX_LINK_NEW=$HOME/.local/state/nix/profile
|
||||||
|
fi
|
||||||
|
if ! [ -e "$NIX_LINK" ]; then
|
||||||
|
NIX_LINK="$NIX_LINK_NEW"
|
||||||
|
else
|
||||||
|
if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then
|
||||||
|
warning="\033[1;35mwarning:\033[0m"
|
||||||
|
printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2
|
||||||
|
if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then
|
||||||
|
printf " Since the profiles match, you can safely delete either of them.\n" 1>&2
|
||||||
|
else
|
||||||
|
# This should be an exceptionally rare occasion: the only way to get it would be to
|
||||||
|
# 1. Update to newer Nix;
|
||||||
|
# 2. Remove .nix-profile;
|
||||||
|
# 3. Set the $NIX_LINK_NEW to something other than the default user profile;
|
||||||
|
# 4. Roll back to older Nix.
|
||||||
|
# If someone did all that, they can probably figure out how to migrate the profile.
|
||||||
|
printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
|
||||||
|
|
||||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||||
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
|
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
|
||||||
|
@ -34,4 +60,5 @@ else
|
||||||
unset -f check_nix_profiles
|
unset -f check_nix_profiles
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export PATH="$HOME/.nix-profile/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
|
export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
|
||||||
|
unset NIX_LINK
|
||||||
|
|
|
@ -2,11 +2,35 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
|
||||||
|
|
||||||
# Set up the per-user profile.
|
# Set up the per-user profile.
|
||||||
|
|
||||||
NIX_LINK=$HOME/.nix-profile
|
NIX_LINK="$HOME/.nix-profile"
|
||||||
|
if [ -n "$XDG_STATE_HOME" ]; then
|
||||||
|
NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile"
|
||||||
|
else
|
||||||
|
NIX_LINK_NEW="$HOME/.local/state/nix/profile"
|
||||||
|
fi
|
||||||
|
if ! [ -e "$NIX_LINK" ]; then
|
||||||
|
NIX_LINK="$NIX_LINK_NEW"
|
||||||
|
else
|
||||||
|
if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then
|
||||||
|
warning="\033[1;35mwarning:\033[0m"
|
||||||
|
printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2
|
||||||
|
if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then
|
||||||
|
printf " Since the profiles match, you can safely delete either of them.\n" 1>&2
|
||||||
|
else
|
||||||
|
# This should be an exceptionally rare occasion: the only way to get it would be to
|
||||||
|
# 1. Update to newer Nix;
|
||||||
|
# 2. Remove .nix-profile;
|
||||||
|
# 3. Set the $NIX_LINK_NEW to something other than the default user profile;
|
||||||
|
# 4. Roll back to older Nix.
|
||||||
|
# If someone did all that, they can probably figure out how to migrate the profile.
|
||||||
|
printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Set up environment.
|
# Set up environment.
|
||||||
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
|
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
|
||||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
|
||||||
|
|
||||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||||
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
|
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||||
|
@ -31,5 +55,5 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export PATH="$NIX_LINK/bin:$PATH"
|
export PATH="$NIX_LINK/bin:$PATH"
|
||||||
unset NIX_LINK
|
unset NIX_LINK NIX_LINK_NEW
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -127,6 +127,16 @@ ref<EvalState> EvalCommand::getEvalState()
|
||||||
return ref<EvalState>(evalState);
|
return ref<EvalState>(evalState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MixOperateOnOptions::MixOperateOnOptions()
|
||||||
|
{
|
||||||
|
addFlag({
|
||||||
|
.longName = "derivation",
|
||||||
|
.description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
|
||||||
|
.category = installablesCategory,
|
||||||
|
.handler = {&operateOn, OperateOn::Derivation},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
||||||
: recursive(recursive)
|
: recursive(recursive)
|
||||||
{
|
{
|
||||||
|
|
|
@ -96,9 +96,6 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||||
std::optional<std::string> expr;
|
std::optional<std::string> expr;
|
||||||
bool readOnlyMode = false;
|
bool readOnlyMode = false;
|
||||||
|
|
||||||
// FIXME: move this; not all commands (e.g. 'nix run') use it.
|
|
||||||
OperateOn operateOn = OperateOn::Output;
|
|
||||||
|
|
||||||
SourceExprCommand(bool supportReadOnlyMode = false);
|
SourceExprCommand(bool supportReadOnlyMode = false);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Installable>> parseInstallables(
|
std::vector<std::shared_ptr<Installable>> parseInstallables(
|
||||||
|
@ -153,8 +150,15 @@ private:
|
||||||
std::string _installable{"."};
|
std::string _installable{"."};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MixOperateOnOptions : virtual Args
|
||||||
|
{
|
||||||
|
OperateOn operateOn = OperateOn::Output;
|
||||||
|
|
||||||
|
MixOperateOnOptions();
|
||||||
|
};
|
||||||
|
|
||||||
/* A command that operates on zero or more store paths. */
|
/* A command that operates on zero or more store paths. */
|
||||||
struct BuiltPathsCommand : public InstallablesCommand
|
struct BuiltPathsCommand : InstallablesCommand, virtual MixOperateOnOptions
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
70
src/libcmd/installable-derived-path.cc
Normal file
70
src/libcmd/installable-derived-path.cc
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#include "installable-derived-path.hh"
|
||||||
|
#include "derivations.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::string InstallableDerivedPath::what() const
|
||||||
|
{
|
||||||
|
return derivedPath.to_string(*store);
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedPathsWithInfo InstallableDerivedPath::toDerivedPaths()
|
||||||
|
{
|
||||||
|
return {{.path = derivedPath, .info = {} }};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<StorePath> InstallableDerivedPath::getStorePath()
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const DerivedPath::Built & bfd) {
|
||||||
|
return bfd.drvPath;
|
||||||
|
},
|
||||||
|
[&](const DerivedPath::Opaque & bo) {
|
||||||
|
return bo.path;
|
||||||
|
},
|
||||||
|
}, derivedPath.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallableDerivedPath InstallableDerivedPath::parse(
|
||||||
|
ref<Store> store,
|
||||||
|
std::string_view prefix,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec)
|
||||||
|
{
|
||||||
|
auto derivedPath = std::visit(overloaded {
|
||||||
|
// If the user did not use ^, we treat the output more liberally.
|
||||||
|
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
|
||||||
|
// First, we accept a symlink chain or an actual store path.
|
||||||
|
auto storePath = store->followLinksToStorePath(prefix);
|
||||||
|
// Second, we see if the store path ends in `.drv` to decide what sort
|
||||||
|
// of derived path they want.
|
||||||
|
//
|
||||||
|
// This handling predates the `^` syntax. The `^*` in
|
||||||
|
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
|
||||||
|
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
|
||||||
|
// also unambiguously mean "do the DerivedPath::Opaque` case".
|
||||||
|
//
|
||||||
|
// Issue #7261 tracks reconsidering this `.drv` dispatching.
|
||||||
|
return storePath.isDerivation()
|
||||||
|
? (DerivedPath) DerivedPath::Built {
|
||||||
|
.drvPath = std::move(storePath),
|
||||||
|
.outputs = OutputsSpec::All {},
|
||||||
|
}
|
||||||
|
: (DerivedPath) DerivedPath::Opaque {
|
||||||
|
.path = std::move(storePath),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// If the user did use ^, we just do exactly what is written.
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
|
||||||
|
return DerivedPath::Built {
|
||||||
|
.drvPath = store->parseStorePath(prefix),
|
||||||
|
.outputs = outputSpec,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}, extendedOutputsSpec.raw());
|
||||||
|
return InstallableDerivedPath {
|
||||||
|
store,
|
||||||
|
std::move(derivedPath),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
src/libcmd/installable-derived-path.hh
Normal file
28
src/libcmd/installable-derived-path.hh
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "installables.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct InstallableDerivedPath : Installable
|
||||||
|
{
|
||||||
|
ref<Store> store;
|
||||||
|
DerivedPath derivedPath;
|
||||||
|
|
||||||
|
InstallableDerivedPath(ref<Store> store, DerivedPath && derivedPath)
|
||||||
|
: store(store), derivedPath(std::move(derivedPath))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
std::string what() const override;
|
||||||
|
|
||||||
|
DerivedPathsWithInfo toDerivedPaths() override;
|
||||||
|
|
||||||
|
std::optional<StorePath> getStorePath() override;
|
||||||
|
|
||||||
|
static InstallableDerivedPath parse(
|
||||||
|
ref<Store> store,
|
||||||
|
std::string_view prefix,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "installables.hh"
|
#include "installables.hh"
|
||||||
|
#include "installable-derived-path.hh"
|
||||||
#include "outputs-spec.hh"
|
#include "outputs-spec.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
|
@ -167,13 +168,6 @@ SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
||||||
.handler = {&expr}
|
.handler = {&expr}
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
|
||||||
.longName = "derivation",
|
|
||||||
.description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
|
|
||||||
.category = installablesCategory,
|
|
||||||
.handler = {&operateOn, OperateOn::Derivation},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (supportReadOnlyMode) {
|
if (supportReadOnlyMode) {
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "read-only",
|
.longName = "read-only",
|
||||||
|
@ -397,38 +391,6 @@ static StorePath getDeriver(
|
||||||
return *derivers.begin();
|
return *derivers.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InstallableStorePath : Installable
|
|
||||||
{
|
|
||||||
ref<Store> store;
|
|
||||||
DerivedPath req;
|
|
||||||
|
|
||||||
InstallableStorePath(ref<Store> store, DerivedPath && req)
|
|
||||||
: store(store), req(std::move(req))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
std::string what() const override
|
|
||||||
{
|
|
||||||
return req.to_string(*store);
|
|
||||||
}
|
|
||||||
|
|
||||||
DerivedPathsWithInfo toDerivedPaths() override
|
|
||||||
{
|
|
||||||
return {{req}};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<StorePath> getStorePath() override
|
|
||||||
{
|
|
||||||
return std::visit(overloaded {
|
|
||||||
[&](const DerivedPath::Built & bfd) {
|
|
||||||
return bfd.drvPath;
|
|
||||||
},
|
|
||||||
[&](const DerivedPath::Opaque & bo) {
|
|
||||||
return bo.path;
|
|
||||||
},
|
|
||||||
}, req.raw());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InstallableAttrPath : InstallableValue
|
struct InstallableAttrPath : InstallableValue
|
||||||
{
|
{
|
||||||
SourceExprCommand & cmd;
|
SourceExprCommand & cmd;
|
||||||
|
@ -792,41 +754,10 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
auto prefix = std::move(prefix_);
|
auto prefix = std::move(prefix_);
|
||||||
auto extendedOutputsSpec = std::move(extendedOutputsSpec_);
|
auto extendedOutputsSpec = std::move(extendedOutputsSpec_);
|
||||||
|
|
||||||
auto found = prefix.find('/');
|
if (prefix.find('/') != std::string::npos) {
|
||||||
if (found != std::string::npos) {
|
|
||||||
try {
|
try {
|
||||||
auto derivedPath = std::visit(overloaded {
|
result.push_back(std::make_shared<InstallableDerivedPath>(
|
||||||
// If the user did not use ^, we treat the output more liberally.
|
InstallableDerivedPath::parse(store, prefix, extendedOutputsSpec)));
|
||||||
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
|
|
||||||
// First, we accept a symlink chain or an actual store path.
|
|
||||||
auto storePath = store->followLinksToStorePath(prefix);
|
|
||||||
// Second, we see if the store path ends in `.drv` to decide what sort
|
|
||||||
// of derived path they want.
|
|
||||||
//
|
|
||||||
// This handling predates the `^` syntax. The `^*` in
|
|
||||||
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
|
|
||||||
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
|
|
||||||
// also unambiguously mean "do the DerivedPath::Opaque` case".
|
|
||||||
//
|
|
||||||
// Issue #7261 tracks reconsidering this `.drv` dispatching.
|
|
||||||
return storePath.isDerivation()
|
|
||||||
? (DerivedPath) DerivedPath::Built {
|
|
||||||
.drvPath = std::move(storePath),
|
|
||||||
.outputs = OutputsSpec::All {},
|
|
||||||
}
|
|
||||||
: (DerivedPath) DerivedPath::Opaque {
|
|
||||||
.path = std::move(storePath),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
// If the user did use ^, we just do exactly what is written.
|
|
||||||
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
|
|
||||||
return DerivedPath::Built {
|
|
||||||
.drvPath = store->parseStorePath(prefix),
|
|
||||||
.outputs = outputSpec,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}, extendedOutputsSpec.raw());
|
|
||||||
result.push_back(std::make_shared<InstallableStorePath>(store, std::move(derivedPath)));
|
|
||||||
continue;
|
continue;
|
||||||
} catch (BadStorePath &) {
|
} catch (BadStorePath &) {
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
|
|
@ -6,4 +6,4 @@ Name: Nix
|
||||||
Description: Nix Package Manager
|
Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Libs: -L${libdir} -lnixcmd
|
Libs: -L${libdir} -lnixcmd
|
||||||
Cflags: -I${includedir}/nix -std=c++20
|
Cflags: -I${includedir}/nix -std=c++2a
|
||||||
|
|
|
@ -2491,7 +2491,7 @@ Strings EvalSettings::getDefaultNixPath()
|
||||||
res.push_back(s ? *s + "=" + p : p);
|
res.push_back(s ? *s + "=" + p : p);
|
||||||
};
|
};
|
||||||
|
|
||||||
add(getHome() + "/.nix-defexpr/channels");
|
add(settings.useXDGBaseDirectories ? getStateDir() + "/nix/defexpr/channels" : getHome() + "/.nix-defexpr/channels");
|
||||||
add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
|
add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
|
||||||
add(settings.nixStateDir + "/profiles/per-user/root/channels");
|
add(settings.nixStateDir + "/profiles/per-user/root/channels");
|
||||||
|
|
||||||
|
|
|
@ -7,4 +7,4 @@ Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Requires: nix-store bdw-gc
|
Requires: nix-store bdw-gc
|
||||||
Libs: -L${libdir} -lnixexpr
|
Libs: -L${libdir} -lnixexpr
|
||||||
Cflags: -I${includedir}/nix -std=c++20
|
Cflags: -I${includedir}/nix -std=c++2a
|
||||||
|
|
|
@ -185,7 +185,7 @@ struct ExprString : Expr
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
Value v;
|
Value v;
|
||||||
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
ExprString(std::string &&s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
||||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
@ -236,7 +236,7 @@ struct ExprSelect : Expr
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
Expr * e, * def;
|
Expr * e, * def;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
|
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath && attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { };
|
||||||
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
|
@ -246,7 +246,7 @@ struct ExprOpHasAttr : Expr
|
||||||
{
|
{
|
||||||
Expr * e;
|
Expr * e;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
|
ExprOpHasAttr(Expr * e, const AttrPath && attrPath) : e(e), attrPath(std::move(attrPath)) { };
|
||||||
PosIdx getPos() const override { return e->getPos(); }
|
PosIdx getPos() const override { return e->getPos(); }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
|
@ -90,7 +90,7 @@ static void dupAttr(const EvalState & state, Symbol attr, const PosIdx pos, cons
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
static void addAttr(ExprAttrs * attrs, AttrPath && attrPath,
|
||||||
Expr * e, const PosIdx pos, const nix::EvalState & state)
|
Expr * e, const PosIdx pos, const nix::EvalState & state)
|
||||||
{
|
{
|
||||||
AttrPath::iterator i;
|
AttrPath::iterator i;
|
||||||
|
@ -188,7 +188,7 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
||||||
|
|
||||||
|
|
||||||
static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
||||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> & es)
|
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||||
{
|
{
|
||||||
if (es.empty()) return new ExprString("");
|
if (es.empty()) return new ExprString("");
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
||||||
s2 = std::string(s2, 0, p + 1);
|
s2 = std::string(s2, 0, p + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
es2->emplace_back(i->first, new ExprString(s2));
|
es2->emplace_back(i->first, new ExprString(std::move(s2)));
|
||||||
};
|
};
|
||||||
for (; i != es.end(); ++i, --n) {
|
for (; i != es.end(); ++i, --n) {
|
||||||
std::visit(overloaded { trimExpr, trimString }, i->second);
|
std::visit(overloaded { trimExpr, trimString }, i->second);
|
||||||
|
@ -413,7 +413,7 @@ expr_op
|
||||||
| expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); }
|
| expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); }
|
| expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(makeCurPos(@2, data), $1, $3); }
|
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, std::move(*$3)); delete $3; }
|
||||||
| expr_op '+' expr_op
|
| expr_op '+' expr_op
|
||||||
{ $$ = new ExprConcatStrings(makeCurPos(@2, data), false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
{ $$ = new ExprConcatStrings(makeCurPos(@2, data), false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
||||||
| expr_op '-' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
| expr_op '-' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
||||||
|
@ -436,14 +436,14 @@ expr_app
|
||||||
|
|
||||||
expr_select
|
expr_select
|
||||||
: expr_simple '.' attrpath
|
: expr_simple '.' attrpath
|
||||||
{ $$ = new ExprSelect(CUR_POS, $1, *$3, 0); }
|
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), nullptr); delete $3; }
|
||||||
| expr_simple '.' attrpath OR_KW expr_select
|
| expr_simple '.' attrpath OR_KW expr_select
|
||||||
{ $$ = new ExprSelect(CUR_POS, $1, *$3, $5); }
|
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), $5); delete $3; }
|
||||||
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
||||||
function named ‘or’, allow stuff like ‘map or [...]’. */
|
function named ‘or’, allow stuff like ‘map or [...]’. */
|
||||||
expr_simple OR_KW
|
expr_simple OR_KW
|
||||||
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, data->symbols.create("or"))}); }
|
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, data->symbols.create("or"))}); }
|
||||||
| expr_simple { $$ = $1; }
|
| expr_simple
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_simple
|
expr_simple
|
||||||
|
@ -458,7 +458,8 @@ expr_simple
|
||||||
| FLOAT { $$ = new ExprFloat($1); }
|
| FLOAT { $$ = new ExprFloat($1); }
|
||||||
| '"' string_parts '"' { $$ = $2; }
|
| '"' string_parts '"' { $$ = $2; }
|
||||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||||
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
|
$$ = stripIndentation(CUR_POS, data->symbols, std::move(*$2));
|
||||||
|
delete $2;
|
||||||
}
|
}
|
||||||
| path_start PATH_END { $$ = $1.e; }
|
| path_start PATH_END { $$ = $1.e; }
|
||||||
| path_start string_parts_interpolated PATH_END {
|
| path_start string_parts_interpolated PATH_END {
|
||||||
|
@ -472,7 +473,7 @@ expr_simple
|
||||||
$$ = new ExprCall(CUR_POS,
|
$$ = new ExprCall(CUR_POS,
|
||||||
new ExprVar(data->symbols.create("__findFile")),
|
new ExprVar(data->symbols.create("__findFile")),
|
||||||
{new ExprVar(data->symbols.create("__nixPath")),
|
{new ExprVar(data->symbols.create("__nixPath")),
|
||||||
new ExprString(path)});
|
new ExprString(std::move(path))});
|
||||||
}
|
}
|
||||||
| URI {
|
| URI {
|
||||||
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
|
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
|
||||||
|
@ -546,7 +547,7 @@ ind_string_parts
|
||||||
;
|
;
|
||||||
|
|
||||||
binds
|
binds
|
||||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state); }
|
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, std::move(*$2), $4, makeCurPos(@2, data), data->state); delete $2; }
|
||||||
| binds INHERIT attrs ';'
|
| binds INHERIT attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
for (auto & i : *$3) {
|
for (auto & i : *$3) {
|
||||||
|
@ -555,6 +556,7 @@ binds
|
||||||
auto pos = makeCurPos(@3, data);
|
auto pos = makeCurPos(@3, data);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
||||||
}
|
}
|
||||||
|
delete $3;
|
||||||
}
|
}
|
||||||
| binds INHERIT '(' expr ')' attrs ';'
|
| binds INHERIT '(' expr ')' attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
|
@ -564,6 +566,7 @@ binds
|
||||||
dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
|
||||||
}
|
}
|
||||||
|
delete $6;
|
||||||
}
|
}
|
||||||
| { $$ = new ExprAttrs(makeCurPos(@0, data)); }
|
| { $$ = new ExprAttrs(makeCurPos(@0, data)); }
|
||||||
;
|
;
|
||||||
|
@ -609,7 +612,7 @@ attrpath
|
||||||
;
|
;
|
||||||
|
|
||||||
attr
|
attr
|
||||||
: ID { $$ = $1; }
|
: ID
|
||||||
| OR_KW { $$ = {"or", 2}; }
|
| OR_KW { $$ = {"or", 2}; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -625,9 +628,9 @@ expr_list
|
||||||
|
|
||||||
formals
|
formals
|
||||||
: formal ',' formals
|
: formal ',' formals
|
||||||
{ $$ = $3; $$->formals.push_back(*$1); }
|
{ $$ = $3; $$->formals.emplace_back(*$1); delete $1; }
|
||||||
| formal
|
| formal
|
||||||
{ $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
|
{ $$ = new ParserFormals; $$->formals.emplace_back(*$1); $$->ellipsis = false; delete $1; }
|
||||||
|
|
|
|
||||||
{ $$ = new ParserFormals; $$->ellipsis = false; }
|
{ $$ = new ParserFormals; $$->ellipsis = false; }
|
||||||
| ELLIPSIS
|
| ELLIPSIS
|
||||||
|
|
|
@ -713,15 +713,42 @@ struct GitInputScheme : InputScheme
|
||||||
AutoDelete delTmpGitDir(tmpGitDir, true);
|
AutoDelete delTmpGitDir(tmpGitDir, true);
|
||||||
|
|
||||||
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", tmpDir, "--separate-git-dir", tmpGitDir });
|
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", tmpDir, "--separate-git-dir", tmpGitDir });
|
||||||
|
|
||||||
|
{
|
||||||
// TODO: repoDir might lack the ref (it only checks if rev
|
// TODO: repoDir might lack the ref (it only checks if rev
|
||||||
// exists, see FIXME above) so use a big hammer and fetch
|
// exists, see FIXME above) so use a big hammer and fetch
|
||||||
// everything to ensure we get the rev.
|
// everything to ensure we get the rev.
|
||||||
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("making temporary clone of '%s'", repoDir));
|
||||||
runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
|
runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
|
||||||
"--update-head-ok", "--", repoDir, "refs/*:refs/*" });
|
"--update-head-ok", "--", repoDir, "refs/*:refs/*" });
|
||||||
|
}
|
||||||
|
|
||||||
runProgram("git", true, { "-C", tmpDir, "checkout", "--quiet", rev.gitRev() });
|
runProgram("git", true, { "-C", tmpDir, "checkout", "--quiet", rev.gitRev() });
|
||||||
runProgram("git", true, { "-C", tmpDir, "remote", "add", "origin", repoInfo.url });
|
|
||||||
|
/* Ensure that we use the correct origin for fetching
|
||||||
|
submodules. This matters for submodules with relative
|
||||||
|
URLs. */
|
||||||
|
if (repoInfo.isLocal) {
|
||||||
|
writeFile(tmpGitDir + "/config", readFile(repoDir + "/" + repoInfo.gitDir + "/config"));
|
||||||
|
|
||||||
|
/* Restore the config.bare setting we may have just
|
||||||
|
copied erroneously from the user's repo. */
|
||||||
|
runProgram("git", true, { "-C", tmpDir, "config", "core.bare", "false" });
|
||||||
|
} else
|
||||||
|
runProgram("git", true, { "-C", tmpDir, "config", "remote.origin.url", repoInfo.url });
|
||||||
|
|
||||||
|
/* As an optimisation, copy the modules directory of the
|
||||||
|
source repo if it exists. */
|
||||||
|
auto modulesPath = repoDir + "/" + repoInfo.gitDir + "/modules";
|
||||||
|
if (pathExists(modulesPath)) {
|
||||||
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying submodules of '%s'", repoInfo.url));
|
||||||
|
runProgram("cp", true, { "-R", "--", modulesPath, tmpGitDir + "/modules" });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching submodules of '%s'", repoInfo.url));
|
||||||
runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
|
runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
|
||||||
|
}
|
||||||
|
|
||||||
filter = isNotDotGitDirectory;
|
filter = isNotDotGitDirectory;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,4 +6,4 @@ Name: Nix
|
||||||
Description: Nix Package Manager
|
Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Libs: -L${libdir} -lnixmain
|
Libs: -L${libdir} -lnixmain
|
||||||
Cflags: -I${includedir}/nix -std=c++20
|
Cflags: -I${includedir}/nix -std=c++2a
|
||||||
|
|
|
@ -209,7 +209,7 @@ void LocalDerivationGoal::tryLocalBuild()
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
if (useChroot) {
|
if (useChroot) {
|
||||||
if (!mountNamespacesSupported() || !pidNamespacesSupported()) {
|
if (!mountAndPidNamespacesSupported()) {
|
||||||
if (!settings.sandboxFallback)
|
if (!settings.sandboxFallback)
|
||||||
throw Error("this system does not support the kernel namespaces that are required for sandboxing; use '--no-sandbox' to disable sandboxing");
|
throw Error("this system does not support the kernel namespaces that are required for sandboxing; use '--no-sandbox' to disable sandboxing");
|
||||||
debug("auto-disabling sandboxing because the prerequisite namespaces are not available");
|
debug("auto-disabling sandboxing because the prerequisite namespaces are not available");
|
||||||
|
@ -385,12 +385,6 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int childEntry(void * arg)
|
|
||||||
{
|
|
||||||
((LocalDerivationGoal *) arg)->runChild();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
static void linkOrCopy(const Path & from, const Path & to)
|
static void linkOrCopy(const Path & from, const Path & to)
|
||||||
{
|
{
|
||||||
|
@ -676,6 +670,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
nobody account. The latter is kind of a hack to support
|
nobody account. The latter is kind of a hack to support
|
||||||
Samba-in-QEMU. */
|
Samba-in-QEMU. */
|
||||||
createDirs(chrootRootDir + "/etc");
|
createDirs(chrootRootDir + "/etc");
|
||||||
|
if (parsedDrv->useUidRange())
|
||||||
chownToBuilder(chrootRootDir + "/etc");
|
chownToBuilder(chrootRootDir + "/etc");
|
||||||
|
|
||||||
if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536))
|
if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536))
|
||||||
|
@ -916,21 +911,15 @@ void LocalDerivationGoal::startBuilder()
|
||||||
if (getuid() == 0 && setgroups(0, 0) == -1)
|
if (getuid() == 0 && setgroups(0, 0) == -1)
|
||||||
throw SysError("setgroups failed");
|
throw SysError("setgroups failed");
|
||||||
|
|
||||||
size_t stackSize = 1 * 1024 * 1024;
|
ProcessOptions options;
|
||||||
char * stack = (char *) mmap(0, stackSize,
|
options.cloneFlags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
|
||||||
PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
|
||||||
if (stack == MAP_FAILED) throw SysError("allocating stack");
|
|
||||||
|
|
||||||
int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
|
|
||||||
if (privateNetwork)
|
if (privateNetwork)
|
||||||
flags |= CLONE_NEWNET;
|
options.cloneFlags |= CLONE_NEWNET;
|
||||||
if (usingUserNamespace)
|
if (usingUserNamespace)
|
||||||
flags |= CLONE_NEWUSER;
|
options.cloneFlags |= CLONE_NEWUSER;
|
||||||
|
|
||||||
pid_t child = clone(childEntry, stack + stackSize, flags, this);
|
pid_t child = startProcess([&]() { runChild(); }, options);
|
||||||
|
|
||||||
if (child == -1)
|
|
||||||
throw SysError("creating sandboxed builder process using clone()");
|
|
||||||
writeFull(builderOut.writeSide.get(),
|
writeFull(builderOut.writeSide.get(),
|
||||||
fmt("%d %d\n", usingUserNamespace, child));
|
fmt("%d %d\n", usingUserNamespace, child));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
|
@ -982,6 +971,10 @@ void LocalDerivationGoal::startBuilder()
|
||||||
"nobody:x:65534:65534:Nobody:/:/noshell\n",
|
"nobody:x:65534:65534:Nobody:/:/noshell\n",
|
||||||
sandboxUid(), sandboxGid(), settings.sandboxBuildDir));
|
sandboxUid(), sandboxGid(), settings.sandboxBuildDir));
|
||||||
|
|
||||||
|
/* Make /etc unwritable */
|
||||||
|
if (!parsedDrv->useUidRange())
|
||||||
|
chmod_(chrootRootDir + "/etc", 0555);
|
||||||
|
|
||||||
/* Save the mount- and user namespace of the child. We have to do this
|
/* Save the mount- and user namespace of the child. We have to do this
|
||||||
*before* the child does a chroot. */
|
*before* the child does a chroot. */
|
||||||
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
|
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
|
||||||
|
|
|
@ -101,6 +101,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
this->result.data.append(data);
|
this->result.data.append(data);
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
|
requestHeaders = curl_slist_append(requestHeaders, "Accept-Encoding: zstd, br, gzip, deflate, bzip2, xz");
|
||||||
if (!request.expectedETag.empty())
|
if (!request.expectedETag.empty())
|
||||||
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
|
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
|
||||||
if (!request.mimeType.empty())
|
if (!request.mimeType.empty())
|
||||||
|
|
|
@ -945,6 +945,27 @@ public:
|
||||||
resolves to a different location from that of the build machine. You
|
resolves to a different location from that of the build machine. You
|
||||||
can enable this setting if you are sure you're not going to do that.
|
can enable this setting if you are sure you're not going to do that.
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
|
Setting<bool> useXDGBaseDirectories{
|
||||||
|
this, false, "use-xdg-base-directories",
|
||||||
|
R"(
|
||||||
|
If set to `true`, Nix will conform to the [XDG Base Directory Specification] for files in `$HOME`.
|
||||||
|
The environment variables used to implement this are documented in the [Environment Variables section](@docroot@/installation/env-variables.md).
|
||||||
|
|
||||||
|
[XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
|
||||||
|
> **Warning**
|
||||||
|
> This changes the location of some well-known symlinks that Nix creates, which might break tools that rely on the old, non-XDG-conformant locations.
|
||||||
|
|
||||||
|
In particular, the following locations change:
|
||||||
|
|
||||||
|
| Old | New |
|
||||||
|
|-------------------|--------------------------------|
|
||||||
|
| `~/.nix-profile` | `$XDG_STATE_HOME/nix/profile` |
|
||||||
|
| `~/.nix-defexpr` | `$XDG_STATE_HOME/nix/defexpr` |
|
||||||
|
| `~/.nix-channels` | `$XDG_STATE_HOME/nix/channels` |
|
||||||
|
)"
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ public:
|
||||||
void init() override
|
void init() override
|
||||||
{
|
{
|
||||||
// FIXME: do this lazily?
|
// FIXME: do this lazily?
|
||||||
if (auto cacheInfo = diskCache->cacheExists(cacheUri)) {
|
if (auto cacheInfo = diskCache->upToDateCacheExists(cacheUri)) {
|
||||||
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
|
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
|
||||||
priority.setDefault(cacheInfo->priority);
|
priority.setDefault(cacheInfo->priority);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -84,11 +84,10 @@ public:
|
||||||
|
|
||||||
Sync<State> _state;
|
Sync<State> _state;
|
||||||
|
|
||||||
NarInfoDiskCacheImpl()
|
NarInfoDiskCacheImpl(Path dbPath = getCacheDir() + "/nix/binary-cache-v6.sqlite")
|
||||||
{
|
{
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
|
|
||||||
Path dbPath = getCacheDir() + "/nix/binary-cache-v6.sqlite";
|
|
||||||
createDirs(dirOf(dbPath));
|
createDirs(dirOf(dbPath));
|
||||||
|
|
||||||
state->db = SQLite(dbPath);
|
state->db = SQLite(dbPath);
|
||||||
|
@ -98,7 +97,7 @@ public:
|
||||||
state->db.exec(schema);
|
state->db.exec(schema);
|
||||||
|
|
||||||
state->insertCache.create(state->db,
|
state->insertCache.create(state->db,
|
||||||
"insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)");
|
"insert into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?1, ?2, ?3, ?4, ?5) on conflict (url) do update set timestamp = ?2, storeDir = ?3, wantMassQuery = ?4, priority = ?5 returning id;");
|
||||||
|
|
||||||
state->queryCache.create(state->db,
|
state->queryCache.create(state->db,
|
||||||
"select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ? and timestamp > ?");
|
"select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ? and timestamp > ?");
|
||||||
|
@ -166,6 +165,8 @@ public:
|
||||||
return i->second;
|
return i->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
std::optional<Cache> queryCacheRaw(State & state, const std::string & uri)
|
std::optional<Cache> queryCacheRaw(State & state, const std::string & uri)
|
||||||
{
|
{
|
||||||
auto i = state.caches.find(uri);
|
auto i = state.caches.find(uri);
|
||||||
|
@ -173,15 +174,21 @@ public:
|
||||||
auto queryCache(state.queryCache.use()(uri)(time(0) - cacheInfoTtl));
|
auto queryCache(state.queryCache.use()(uri)(time(0) - cacheInfoTtl));
|
||||||
if (!queryCache.next())
|
if (!queryCache.next())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
state.caches.emplace(uri,
|
auto cache = Cache {
|
||||||
Cache{(int) queryCache.getInt(0), queryCache.getStr(1), queryCache.getInt(2) != 0, (int) queryCache.getInt(3)});
|
.id = (int) queryCache.getInt(0),
|
||||||
|
.storeDir = queryCache.getStr(1),
|
||||||
|
.wantMassQuery = queryCache.getInt(2) != 0,
|
||||||
|
.priority = (int) queryCache.getInt(3),
|
||||||
|
};
|
||||||
|
state.caches.emplace(uri, cache);
|
||||||
}
|
}
|
||||||
return getCache(state, uri);
|
return getCache(state, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createCache(const std::string & uri, const Path & storeDir, bool wantMassQuery, int priority) override
|
public:
|
||||||
|
int createCache(const std::string & uri, const Path & storeDir, bool wantMassQuery, int priority) override
|
||||||
{
|
{
|
||||||
retrySQLite<void>([&]() {
|
return retrySQLite<int>([&]() {
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
SQLiteTxn txn(state->db);
|
SQLiteTxn txn(state->db);
|
||||||
|
|
||||||
|
@ -190,17 +197,29 @@ public:
|
||||||
auto cache(queryCacheRaw(*state, uri));
|
auto cache(queryCacheRaw(*state, uri));
|
||||||
|
|
||||||
if (cache)
|
if (cache)
|
||||||
return;
|
return cache->id;
|
||||||
|
|
||||||
state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority).exec();
|
Cache ret {
|
||||||
assert(sqlite3_changes(state->db) == 1);
|
.id = -1, // set below
|
||||||
state->caches[uri] = Cache{(int) sqlite3_last_insert_rowid(state->db), storeDir, wantMassQuery, priority};
|
.storeDir = storeDir,
|
||||||
|
.wantMassQuery = wantMassQuery,
|
||||||
|
.priority = priority,
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto r(state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority));
|
||||||
|
assert(r.next());
|
||||||
|
ret.id = (int) r.getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->caches[uri] = ret;
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
return ret.id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<CacheInfo> cacheExists(const std::string & uri) override
|
std::optional<CacheInfo> upToDateCacheExists(const std::string & uri) override
|
||||||
{
|
{
|
||||||
return retrySQLite<std::optional<CacheInfo>>([&]() -> std::optional<CacheInfo> {
|
return retrySQLite<std::optional<CacheInfo>>([&]() -> std::optional<CacheInfo> {
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
|
@ -208,6 +227,7 @@ public:
|
||||||
if (!cache)
|
if (!cache)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return CacheInfo {
|
return CacheInfo {
|
||||||
|
.id = cache->id,
|
||||||
.wantMassQuery = cache->wantMassQuery,
|
.wantMassQuery = cache->wantMassQuery,
|
||||||
.priority = cache->priority
|
.priority = cache->priority
|
||||||
};
|
};
|
||||||
|
@ -371,4 +391,9 @@ ref<NarInfoDiskCache> getNarInfoDiskCache()
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<NarInfoDiskCache> getTestNarInfoDiskCache(Path dbPath)
|
||||||
|
{
|
||||||
|
return make_ref<NarInfoDiskCacheImpl>(dbPath);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,16 +13,17 @@ public:
|
||||||
|
|
||||||
virtual ~NarInfoDiskCache() { }
|
virtual ~NarInfoDiskCache() { }
|
||||||
|
|
||||||
virtual void createCache(const std::string & uri, const Path & storeDir,
|
virtual int createCache(const std::string & uri, const Path & storeDir,
|
||||||
bool wantMassQuery, int priority) = 0;
|
bool wantMassQuery, int priority) = 0;
|
||||||
|
|
||||||
struct CacheInfo
|
struct CacheInfo
|
||||||
{
|
{
|
||||||
|
int id;
|
||||||
bool wantMassQuery;
|
bool wantMassQuery;
|
||||||
int priority;
|
int priority;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual std::optional<CacheInfo> cacheExists(const std::string & uri) = 0;
|
virtual std::optional<CacheInfo> upToDateCacheExists(const std::string & uri) = 0;
|
||||||
|
|
||||||
virtual std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
|
virtual std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
|
||||||
const std::string & uri, const std::string & hashPart) = 0;
|
const std::string & uri, const std::string & hashPart) = 0;
|
||||||
|
@ -45,4 +46,6 @@ public:
|
||||||
multiple threads. */
|
multiple threads. */
|
||||||
ref<NarInfoDiskCache> getNarInfoDiskCache();
|
ref<NarInfoDiskCache> getNarInfoDiskCache();
|
||||||
|
|
||||||
|
ref<NarInfoDiskCache> getTestNarInfoDiskCache(Path dbPath);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,4 @@ Name: Nix
|
||||||
Description: Nix Package Manager
|
Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Libs: -L${libdir} -lnixstore -lnixutil
|
Libs: -L${libdir} -lnixstore -lnixutil
|
||||||
Cflags: -I${includedir}/nix -std=c++20
|
Cflags: -I${includedir}/nix -std=c++2a
|
||||||
|
|
|
@ -282,7 +282,7 @@ std::string optimisticLockProfile(const Path & profile)
|
||||||
|
|
||||||
Path profilesDir()
|
Path profilesDir()
|
||||||
{
|
{
|
||||||
auto profileRoot = getDataDir() + "/nix/profiles";
|
auto profileRoot = createNixStateDir() + "/profiles";
|
||||||
createDirs(profileRoot);
|
createDirs(profileRoot);
|
||||||
return profileRoot;
|
return profileRoot;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ Path profilesDir()
|
||||||
|
|
||||||
Path getDefaultProfile()
|
Path getDefaultProfile()
|
||||||
{
|
{
|
||||||
Path profileLink = getHome() + "/.nix-profile";
|
Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile";
|
||||||
try {
|
try {
|
||||||
auto profile =
|
auto profile =
|
||||||
getuid() == 0
|
getuid() == 0
|
||||||
|
|
|
@ -72,8 +72,9 @@ std::string optimisticLockProfile(const Path & profile);
|
||||||
profiles. */
|
profiles. */
|
||||||
Path profilesDir();
|
Path profilesDir();
|
||||||
|
|
||||||
/* Resolve ~/.nix-profile. If ~/.nix-profile doesn't exist yet, create
|
/* Resolve the default profile (~/.nix-profile by default, $XDG_STATE_HOME/
|
||||||
it. */
|
nix/profile if XDG Base Directory Support is enabled), and create if doesn't
|
||||||
|
exist */
|
||||||
Path getDefaultProfile();
|
Path getDefaultProfile();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,7 +238,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
|
||||||
|
|
||||||
void init() override
|
void init() override
|
||||||
{
|
{
|
||||||
if (auto cacheInfo = diskCache->cacheExists(getUri())) {
|
if (auto cacheInfo = diskCache->upToDateCacheExists(getUri())) {
|
||||||
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
|
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
|
||||||
priority.setDefault(cacheInfo->priority);
|
priority.setDefault(cacheInfo->priority);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -41,6 +41,15 @@ SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int ex
|
||||||
throw SQLiteError(path, errMsg, err, exterr, offset, std::move(hf));
|
throw SQLiteError(path, errMsg, err, exterr, offset, std::move(hf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void traceSQL(void * x, const char * sql)
|
||||||
|
{
|
||||||
|
// wacky delimiters:
|
||||||
|
// so that we're quite unambiguous without escaping anything
|
||||||
|
// notice instead of trace:
|
||||||
|
// so that this can be enabled without getting the firehose in our face.
|
||||||
|
notice("SQL<[%1%]>", sql);
|
||||||
|
};
|
||||||
|
|
||||||
SQLite::SQLite(const Path & path, bool create)
|
SQLite::SQLite(const Path & path, bool create)
|
||||||
{
|
{
|
||||||
// useSQLiteWAL also indicates what virtual file system we need. Using
|
// useSQLiteWAL also indicates what virtual file system we need. Using
|
||||||
|
@ -58,6 +67,11 @@ SQLite::SQLite(const Path & path, bool create)
|
||||||
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
||||||
SQLiteError::throw_(db, "setting timeout");
|
SQLiteError::throw_(db, "setting timeout");
|
||||||
|
|
||||||
|
if (getEnv("NIX_DEBUG_SQLITE_TRACES") == "1") {
|
||||||
|
// To debug sqlite statements; trace all of them
|
||||||
|
sqlite3_trace(db, &traceSQL, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
exec("pragma foreign_keys = 1");
|
exec("pragma foreign_keys = 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
123
src/libstore/tests/nar-info-disk-cache.cc
Normal file
123
src/libstore/tests/nar-info-disk-cache.cc
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#include "nar-info-disk-cache.hh"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <rapidcheck/gtest.h>
|
||||||
|
#include "sqlite.hh"
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
TEST(NarInfoDiskCacheImpl, create_and_read) {
|
||||||
|
// This is a large single test to avoid some setup overhead.
|
||||||
|
|
||||||
|
int prio = 12345;
|
||||||
|
bool wantMassQuery = true;
|
||||||
|
|
||||||
|
Path tmpDir = createTempDir();
|
||||||
|
AutoDelete delTmpDir(tmpDir);
|
||||||
|
Path dbPath(tmpDir + "/test-narinfo-disk-cache.sqlite");
|
||||||
|
|
||||||
|
int savedId;
|
||||||
|
int barId;
|
||||||
|
SQLite db;
|
||||||
|
SQLiteStmt getIds;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto cache = getTestNarInfoDiskCache(dbPath);
|
||||||
|
|
||||||
|
// Set up "background noise" and check that different caches receive different ids
|
||||||
|
{
|
||||||
|
auto bc1 = cache->createCache("https://bar", "/nix/storedir", wantMassQuery, prio);
|
||||||
|
auto bc2 = cache->createCache("https://xyz", "/nix/storedir", false, 12);
|
||||||
|
ASSERT_NE(bc1, bc2);
|
||||||
|
barId = bc1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the fields are saved and returned correctly. This does not test
|
||||||
|
// the select statement yet, because of in-memory caching.
|
||||||
|
savedId = cache->createCache("http://foo", "/nix/storedir", wantMassQuery, prio);;
|
||||||
|
{
|
||||||
|
auto r = cache->upToDateCacheExists("http://foo");
|
||||||
|
ASSERT_TRUE(r);
|
||||||
|
ASSERT_EQ(r->priority, prio);
|
||||||
|
ASSERT_EQ(r->wantMassQuery, wantMassQuery);
|
||||||
|
ASSERT_EQ(savedId, r->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're going to pay special attention to the id field because we had a bug
|
||||||
|
// that changed it.
|
||||||
|
db = SQLite(dbPath);
|
||||||
|
getIds.create(db, "select id from BinaryCaches where url = 'http://foo'");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto q(getIds.use());
|
||||||
|
ASSERT_TRUE(q.next());
|
||||||
|
ASSERT_EQ(savedId, q.getInt(0));
|
||||||
|
ASSERT_FALSE(q.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretend that the caches are older, but keep one up to date, as "background noise"
|
||||||
|
db.exec("update BinaryCaches set timestamp = timestamp - 1 - 7 * 24 * 3600 where url <> 'https://xyz';");
|
||||||
|
|
||||||
|
// This shows that the in-memory cache works
|
||||||
|
{
|
||||||
|
auto r = cache->upToDateCacheExists("http://foo");
|
||||||
|
ASSERT_TRUE(r);
|
||||||
|
ASSERT_EQ(r->priority, prio);
|
||||||
|
ASSERT_EQ(r->wantMassQuery, wantMassQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// We can't clear the in-memory cache, so we use a new cache object. This is
|
||||||
|
// more realistic anyway.
|
||||||
|
auto cache2 = getTestNarInfoDiskCache(dbPath);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto r = cache2->upToDateCacheExists("http://foo");
|
||||||
|
ASSERT_FALSE(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Update", same data, check that the id number is reused
|
||||||
|
cache2->createCache("http://foo", "/nix/storedir", wantMassQuery, prio);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto r = cache2->upToDateCacheExists("http://foo");
|
||||||
|
ASSERT_TRUE(r);
|
||||||
|
ASSERT_EQ(r->priority, prio);
|
||||||
|
ASSERT_EQ(r->wantMassQuery, wantMassQuery);
|
||||||
|
ASSERT_EQ(r->id, savedId);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto q(getIds.use());
|
||||||
|
ASSERT_TRUE(q.next());
|
||||||
|
auto currentId = q.getInt(0);
|
||||||
|
ASSERT_FALSE(q.next());
|
||||||
|
ASSERT_EQ(currentId, savedId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the fields can be modified, and the id remains the same
|
||||||
|
{
|
||||||
|
auto r0 = cache2->upToDateCacheExists("https://bar");
|
||||||
|
ASSERT_FALSE(r0);
|
||||||
|
|
||||||
|
cache2->createCache("https://bar", "/nix/storedir", !wantMassQuery, prio + 10);
|
||||||
|
auto r = cache2->upToDateCacheExists("https://bar");
|
||||||
|
ASSERT_EQ(r->wantMassQuery, !wantMassQuery);
|
||||||
|
ASSERT_EQ(r->priority, prio + 10);
|
||||||
|
ASSERT_EQ(r->id, barId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Force update (no use case yet; we only retrieve cache metadata when stale based on timestamp)
|
||||||
|
// {
|
||||||
|
// cache2->createCache("https://bar", "/nix/storedir", wantMassQuery, prio + 20);
|
||||||
|
// auto r = cache2->upToDateCacheExists("https://bar");
|
||||||
|
// ASSERT_EQ(r->wantMassQuery, wantMassQuery);
|
||||||
|
// ASSERT_EQ(r->priority, prio + 20);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -29,7 +29,15 @@ void Args::removeFlag(const std::string & longName)
|
||||||
|
|
||||||
void Completions::add(std::string completion, std::string description)
|
void Completions::add(std::string completion, std::string description)
|
||||||
{
|
{
|
||||||
assert(description.find('\n') == std::string::npos);
|
description = trim(description);
|
||||||
|
// ellipsize overflowing content on the back of the description
|
||||||
|
auto end_index = description.find_first_of(".\n");
|
||||||
|
if (end_index != std::string::npos) {
|
||||||
|
auto needs_ellipsis = end_index != description.size() - 1;
|
||||||
|
description.resize(end_index);
|
||||||
|
if (needs_ellipsis)
|
||||||
|
description.append(" [...]");
|
||||||
|
}
|
||||||
insert(Completion {
|
insert(Completion {
|
||||||
.completion = completion,
|
.completion = completion,
|
||||||
.description = description
|
.description = description
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
|
||||||
#include <mntent.h>
|
#include <sys/mount.h>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -33,60 +33,57 @@ bool userNamespacesSupported()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
Pid pid = startProcess([&]()
|
Pid pid = startProcess([&]()
|
||||||
{
|
{
|
||||||
auto res = unshare(CLONE_NEWUSER);
|
_exit(0);
|
||||||
_exit(res ? 1 : 0);
|
}, {
|
||||||
|
.cloneFlags = CLONE_NEWUSER
|
||||||
});
|
});
|
||||||
|
|
||||||
bool supported = pid.wait() == 0;
|
auto r = pid.wait();
|
||||||
|
assert(!r);
|
||||||
|
} catch (SysError & e) {
|
||||||
|
debug("user namespaces do not work on this system: %s", e.msg());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!supported)
|
return true;
|
||||||
debug("user namespaces do not work on this system");
|
|
||||||
|
|
||||||
return supported;
|
|
||||||
}();
|
}();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mountNamespacesSupported()
|
bool mountAndPidNamespacesSupported()
|
||||||
{
|
{
|
||||||
static auto res = [&]() -> bool
|
static auto res = [&]() -> bool
|
||||||
{
|
{
|
||||||
bool useUserNamespace = userNamespacesSupported();
|
try {
|
||||||
|
|
||||||
Pid pid = startProcess([&]()
|
Pid pid = startProcess([&]()
|
||||||
{
|
{
|
||||||
auto res = unshare(CLONE_NEWNS | (useUserNamespace ? CLONE_NEWUSER : 0));
|
/* Make sure we don't remount the parent's /proc. */
|
||||||
_exit(res ? 1 : 0);
|
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
||||||
|
_exit(1);
|
||||||
|
|
||||||
|
/* Test whether we can remount /proc. The kernel disallows
|
||||||
|
this if /proc is not fully visible, i.e. if there are
|
||||||
|
filesystems mounted on top of files inside /proc. See
|
||||||
|
https://lore.kernel.org/lkml/87tvsrjai0.fsf@xmission.com/T/. */
|
||||||
|
if (mount("none", "/proc", "proc", 0, 0) == -1)
|
||||||
|
_exit(2);
|
||||||
|
|
||||||
|
_exit(0);
|
||||||
|
}, {
|
||||||
|
.cloneFlags = CLONE_NEWNS | CLONE_NEWPID | (userNamespacesSupported() ? CLONE_NEWUSER : 0)
|
||||||
});
|
});
|
||||||
|
|
||||||
bool supported = pid.wait() == 0;
|
if (pid.wait()) {
|
||||||
|
debug("PID namespaces do not work on this system: cannot remount /proc");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!supported)
|
} catch (SysError & e) {
|
||||||
debug("mount namespaces do not work on this system");
|
debug("mount namespaces do not work on this system: %s", e.msg());
|
||||||
|
|
||||||
return supported;
|
|
||||||
}();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pidNamespacesSupported()
|
|
||||||
{
|
|
||||||
static auto res = [&]() -> bool
|
|
||||||
{
|
|
||||||
/* Check whether /proc is fully visible, i.e. there are no
|
|
||||||
filesystems mounted on top of files inside /proc. If this
|
|
||||||
is not the case, then we cannot mount a new /proc inside
|
|
||||||
the sandbox that matches the sandbox's PID namespace.
|
|
||||||
See https://lore.kernel.org/lkml/87tvsrjai0.fsf@xmission.com/T/. */
|
|
||||||
auto fp = fopen("/proc/mounts", "r");
|
|
||||||
if (!fp) return false;
|
|
||||||
Finally delFP = [&]() { fclose(fp); };
|
|
||||||
|
|
||||||
while (auto ent = getmntent(fp))
|
|
||||||
if (hasPrefix(std::string_view(ent->mnt_dir), "/proc/")) {
|
|
||||||
debug("PID namespaces do not work because /proc is not fully visible; disabling sandboxing");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,7 @@ namespace nix {
|
||||||
|
|
||||||
bool userNamespacesSupported();
|
bool userNamespacesSupported();
|
||||||
|
|
||||||
bool mountNamespacesSupported();
|
bool mountAndPidNamespacesSupported();
|
||||||
|
|
||||||
bool pidNamespacesSupported();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#endif
|
#endif
|
||||||
|
@ -608,6 +609,19 @@ Path getDataDir()
|
||||||
return dataDir ? *dataDir : getHome() + "/.local/share";
|
return dataDir ? *dataDir : getHome() + "/.local/share";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Path getStateDir()
|
||||||
|
{
|
||||||
|
auto stateDir = getEnv("XDG_STATE_HOME");
|
||||||
|
return stateDir ? *stateDir : getHome() + "/.local/state";
|
||||||
|
}
|
||||||
|
|
||||||
|
Path createNixStateDir()
|
||||||
|
{
|
||||||
|
Path dir = getStateDir() + "/nix";
|
||||||
|
createDirs(dir);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::optional<Path> getSelfExe()
|
std::optional<Path> getSelfExe()
|
||||||
{
|
{
|
||||||
|
@ -1051,9 +1065,17 @@ static pid_t doFork(bool allowVfork, std::function<void()> fun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int childEntry(void * arg)
|
||||||
|
{
|
||||||
|
auto main = (std::function<void()> *) arg;
|
||||||
|
(*main)();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
||||||
{
|
{
|
||||||
auto wrapper = [&]() {
|
std::function<void()> wrapper = [&]() {
|
||||||
if (!options.allowVfork)
|
if (!options.allowVfork)
|
||||||
logger = makeSimpleLogger();
|
logger = makeSimpleLogger();
|
||||||
try {
|
try {
|
||||||
|
@ -1073,7 +1095,27 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
||||||
_exit(1);
|
_exit(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
pid_t pid = doFork(options.allowVfork, wrapper);
|
pid_t pid = -1;
|
||||||
|
|
||||||
|
if (options.cloneFlags) {
|
||||||
|
#ifdef __linux__
|
||||||
|
// Not supported, since then we don't know when to free the stack.
|
||||||
|
assert(!(options.cloneFlags & CLONE_VM));
|
||||||
|
|
||||||
|
size_t stackSize = 1 * 1024 * 1024;
|
||||||
|
auto stack = (char *) mmap(0, stackSize,
|
||||||
|
PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
||||||
|
if (stack == MAP_FAILED) throw SysError("allocating stack");
|
||||||
|
|
||||||
|
Finally freeStack([&]() { munmap(stack, stackSize); });
|
||||||
|
|
||||||
|
pid = clone(childEntry, stack + stackSize, options.cloneFlags | SIGCHLD, &wrapper);
|
||||||
|
#else
|
||||||
|
throw Error("clone flags are only supported on Linux");
|
||||||
|
#endif
|
||||||
|
} else
|
||||||
|
pid = doFork(options.allowVfork, wrapper);
|
||||||
|
|
||||||
if (pid == -1) throw SysError("unable to fork");
|
if (pid == -1) throw SysError("unable to fork");
|
||||||
|
|
||||||
return pid;
|
return pid;
|
||||||
|
|
|
@ -158,6 +158,12 @@ Path getDataDir();
|
||||||
/* Return the path of the current executable. */
|
/* Return the path of the current executable. */
|
||||||
std::optional<Path> getSelfExe();
|
std::optional<Path> getSelfExe();
|
||||||
|
|
||||||
|
/* Return $XDG_STATE_HOME or $HOME/.local/state. */
|
||||||
|
Path getStateDir();
|
||||||
|
|
||||||
|
/* Create the Nix state directory and return the path to it. */
|
||||||
|
Path createNixStateDir();
|
||||||
|
|
||||||
/* Create a directory and all its parents, if necessary. Returns the
|
/* Create a directory and all its parents, if necessary. Returns the
|
||||||
list of created directories, in order of creation. */
|
list of created directories, in order of creation. */
|
||||||
Paths createDirs(const Path & path);
|
Paths createDirs(const Path & path);
|
||||||
|
@ -301,6 +307,7 @@ struct ProcessOptions
|
||||||
bool dieWithParent = true;
|
bool dieWithParent = true;
|
||||||
bool runExitHandlers = false;
|
bool runExitHandlers = false;
|
||||||
bool allowVfork = false;
|
bool allowVfork = false;
|
||||||
|
int cloneFlags = 0; // use clone() with the specified flags (Linux only)
|
||||||
};
|
};
|
||||||
|
|
||||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
||||||
|
|
|
@ -164,8 +164,8 @@ static int main_nix_channel(int argc, char ** argv)
|
||||||
{
|
{
|
||||||
// Figure out the name of the `.nix-channels' file to use
|
// Figure out the name of the `.nix-channels' file to use
|
||||||
auto home = getHome();
|
auto home = getHome();
|
||||||
channelsList = home + "/.nix-channels";
|
channelsList = settings.useXDGBaseDirectories ? createNixStateDir() + "/channels" : home + "/.nix-channels";
|
||||||
nixDefExpr = home + "/.nix-defexpr";
|
nixDefExpr = settings.useXDGBaseDirectories ? createNixStateDir() + "/defexpr" : home + "/.nix-defexpr";
|
||||||
|
|
||||||
// Figure out the name of the channels profile.
|
// Figure out the name of the channels profile.
|
||||||
profile = profilesDir() + "/channels";
|
profile = profilesDir() + "/channels";
|
||||||
|
|
|
@ -1291,7 +1291,7 @@ static void opSwitchProfile(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
throw UsageError("exactly one argument expected");
|
throw UsageError("exactly one argument expected");
|
||||||
|
|
||||||
Path profile = absPath(opArgs.front());
|
Path profile = absPath(opArgs.front());
|
||||||
Path profileLink = getHome() + "/.nix-profile";
|
Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile";
|
||||||
|
|
||||||
switchLink(profileLink, profile);
|
switchLink(profileLink, profile);
|
||||||
}
|
}
|
||||||
|
@ -1391,14 +1391,14 @@ static int main_nix_env(int argc, char * * argv)
|
||||||
Operation op = 0;
|
Operation op = 0;
|
||||||
RepairFlag repair = NoRepair;
|
RepairFlag repair = NoRepair;
|
||||||
std::string file;
|
std::string file;
|
||||||
Path nixExprPath;
|
|
||||||
|
|
||||||
Globals globals;
|
Globals globals;
|
||||||
|
|
||||||
globals.instSource.type = srcUnknown;
|
globals.instSource.type = srcUnknown;
|
||||||
nixExprPath = getHome() + "/.nix-defexpr";
|
|
||||||
globals.instSource.systemFilter = "*";
|
globals.instSource.systemFilter = "*";
|
||||||
|
|
||||||
|
Path nixExprPath = settings.useXDGBaseDirectories ? createNixStateDir() + "/defexpr" : getHome() + "/.nix-defexpr";
|
||||||
|
|
||||||
if (!pathExists(nixExprPath)) {
|
if (!pathExists(nixExprPath)) {
|
||||||
try {
|
try {
|
||||||
createDirs(nixExprPath);
|
createDirs(nixExprPath);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "installables.hh"
|
#include "installables.hh"
|
||||||
|
#include "installable-derived-path.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "eval-cache.hh"
|
#include "eval-cache.hh"
|
||||||
|
@ -8,30 +9,6 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct InstallableDerivedPath : Installable
|
|
||||||
{
|
|
||||||
ref<Store> store;
|
|
||||||
const DerivedPath derivedPath;
|
|
||||||
|
|
||||||
InstallableDerivedPath(ref<Store> store, const DerivedPath & derivedPath)
|
|
||||||
: store(store)
|
|
||||||
, derivedPath(derivedPath)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string what() const override { return derivedPath.to_string(*store); }
|
|
||||||
|
|
||||||
DerivedPathsWithInfo toDerivedPaths() override
|
|
||||||
{
|
|
||||||
return {{derivedPath}};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<StorePath> getStorePath() override
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the rewrites that are needed to resolve a string whose context is
|
* Return the rewrites that are needed to resolve a string whose context is
|
||||||
* included in `dependencies`.
|
* included in `dependencies`.
|
||||||
|
@ -146,7 +123,7 @@ App UnresolvedApp::resolve(ref<Store> evalStore, ref<Store> store)
|
||||||
|
|
||||||
for (auto & ctxElt : unresolved.context)
|
for (auto & ctxElt : unresolved.context)
|
||||||
installableContext.push_back(
|
installableContext.push_back(
|
||||||
std::make_shared<InstallableDerivedPath>(store, ctxElt));
|
std::make_shared<InstallableDerivedPath>(store, DerivedPath { ctxElt }));
|
||||||
|
|
||||||
auto builtContext = Installable::build(evalStore, store, Realise::Outputs, installableContext);
|
auto builtContext = Installable::build(evalStore, store, Realise::Outputs, installableContext);
|
||||||
res.program = resolveString(*store, unresolved.program, builtContext);
|
res.program = resolveString(*store, unresolved.program, builtContext);
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
using namespace nix::daemon;
|
using namespace nix::daemon;
|
||||||
|
|
||||||
struct UserSettings : Config {
|
struct AuthorizationSettings : Config {
|
||||||
|
|
||||||
Setting<Strings> trustedUsers{
|
Setting<Strings> trustedUsers{
|
||||||
this, {"root"}, "trusted-users",
|
this, {"root"}, "trusted-users",
|
||||||
|
@ -67,9 +67,9 @@ struct UserSettings : Config {
|
||||||
)"};
|
)"};
|
||||||
};
|
};
|
||||||
|
|
||||||
UserSettings userSettings;
|
AuthorizationSettings authorizationSettings;
|
||||||
|
|
||||||
static GlobalConfig::Register rSettings(&userSettings);
|
static GlobalConfig::Register rSettings(&authorizationSettings);
|
||||||
|
|
||||||
#ifndef __linux__
|
#ifndef __linux__
|
||||||
#define SPLICE_F_MOVE 0
|
#define SPLICE_F_MOVE 0
|
||||||
|
@ -240,8 +240,8 @@ static void daemonLoop()
|
||||||
struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0;
|
struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0;
|
||||||
std::string group = gr ? gr->gr_name : std::to_string(peer.gid);
|
std::string group = gr ? gr->gr_name : std::to_string(peer.gid);
|
||||||
|
|
||||||
Strings trustedUsers = userSettings.trustedUsers;
|
Strings trustedUsers = authorizationSettings.trustedUsers;
|
||||||
Strings allowedUsers = userSettings.allowedUsers;
|
Strings allowedUsers = authorizationSettings.allowedUsers;
|
||||||
|
|
||||||
if (matchUser(user, group, trustedUsers))
|
if (matchUser(user, group, trustedUsers))
|
||||||
trusted = Trusted;
|
trusted = Trusted;
|
||||||
|
|
|
@ -106,7 +106,7 @@ void printClosureDiff(
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
struct CmdDiffClosures : SourceExprCommand
|
struct CmdDiffClosures : SourceExprCommand, MixOperateOnOptions
|
||||||
{
|
{
|
||||||
std::string _before, _after;
|
std::string _before, _after;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ static std::string filterPrintable(const std::string & s)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CmdWhyDepends : SourceExprCommand
|
struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||||
{
|
{
|
||||||
std::string _package, _dependency;
|
std::string _package, _dependency;
|
||||||
bool all = false;
|
bool all = false;
|
||||||
|
|
56
tests/build-delete.sh
Normal file
56
tests/build-delete.sh
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# https://github.com/NixOS/nix/issues/6572
|
||||||
|
issue_6572_independent_outputs() {
|
||||||
|
nix build -f multiple-outputs.nix --json independent --no-link > $TEST_ROOT/independent.json
|
||||||
|
|
||||||
|
# Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation.
|
||||||
|
p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths)
|
||||||
|
nix-store --delete "$p" # Clean up for next test
|
||||||
|
|
||||||
|
# Make sure that 'nix build' tracks input-outputs correctly when a single output is already present.
|
||||||
|
nix-store --delete "$(jq -r <$TEST_ROOT/independent.json .[0].outputs.first)"
|
||||||
|
p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths)
|
||||||
|
cmp $p <<EOF
|
||||||
|
first
|
||||||
|
second
|
||||||
|
EOF
|
||||||
|
nix-store --delete "$p" # Clean up for next test
|
||||||
|
|
||||||
|
# Make sure that 'nix build' tracks input-outputs correctly when a single output is already present.
|
||||||
|
nix-store --delete "$(jq -r <$TEST_ROOT/independent.json .[0].outputs.second)"
|
||||||
|
p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths)
|
||||||
|
cmp $p <<EOF
|
||||||
|
first
|
||||||
|
second
|
||||||
|
EOF
|
||||||
|
nix-store --delete "$p" # Clean up for next test
|
||||||
|
}
|
||||||
|
issue_6572_independent_outputs
|
||||||
|
|
||||||
|
|
||||||
|
# https://github.com/NixOS/nix/issues/6572
|
||||||
|
issue_6572_dependent_outputs() {
|
||||||
|
|
||||||
|
nix build -f multiple-outputs.nix --json a --no-link > $TEST_ROOT/a.json
|
||||||
|
|
||||||
|
# # Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation.
|
||||||
|
p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths)
|
||||||
|
nix-store --delete "$p" # Clean up for next test
|
||||||
|
|
||||||
|
# Make sure that 'nix build' tracks input-outputs correctly when a single output is already present.
|
||||||
|
nix-store --delete "$(jq -r <$TEST_ROOT/a.json .[0].outputs.second)"
|
||||||
|
p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths)
|
||||||
|
cmp $p <<EOF
|
||||||
|
first
|
||||||
|
second
|
||||||
|
EOF
|
||||||
|
nix-store --delete "$p" # Clean up for next test
|
||||||
|
}
|
||||||
|
if isDaemonNewer "2.12pre0"; then
|
||||||
|
issue_6572_dependent_outputs
|
||||||
|
fi
|
|
@ -107,62 +107,3 @@ nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status
|
||||||
(.drvPath | match(".*multiple-outputs-e.drv")) and
|
(.drvPath | match(".*multiple-outputs-e.drv")) and
|
||||||
(.outputs | keys == ["a_a", "b"]))
|
(.outputs | keys == ["a_a", "b"]))
|
||||||
'
|
'
|
||||||
|
|
||||||
testNormalization () {
|
|
||||||
clearStore
|
|
||||||
outPath=$(nix-build ./simple.nix --no-out-link)
|
|
||||||
test "$(stat -c %Y $outPath)" -eq 1
|
|
||||||
}
|
|
||||||
|
|
||||||
testNormalization
|
|
||||||
|
|
||||||
# https://github.com/NixOS/nix/issues/6572
|
|
||||||
issue_6572_independent_outputs() {
|
|
||||||
nix build -f multiple-outputs.nix --json independent --no-link > $TEST_ROOT/independent.json
|
|
||||||
|
|
||||||
# Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation.
|
|
||||||
p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths)
|
|
||||||
nix-store --delete "$p" # Clean up for next test
|
|
||||||
|
|
||||||
# Make sure that 'nix build' tracks input-outputs correctly when a single output is already present.
|
|
||||||
nix-store --delete "$(jq -r <$TEST_ROOT/independent.json .[0].outputs.first)"
|
|
||||||
p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths)
|
|
||||||
cmp $p <<EOF
|
|
||||||
first
|
|
||||||
second
|
|
||||||
EOF
|
|
||||||
nix-store --delete "$p" # Clean up for next test
|
|
||||||
|
|
||||||
# Make sure that 'nix build' tracks input-outputs correctly when a single output is already present.
|
|
||||||
nix-store --delete "$(jq -r <$TEST_ROOT/independent.json .[0].outputs.second)"
|
|
||||||
p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths)
|
|
||||||
cmp $p <<EOF
|
|
||||||
first
|
|
||||||
second
|
|
||||||
EOF
|
|
||||||
nix-store --delete "$p" # Clean up for next test
|
|
||||||
}
|
|
||||||
issue_6572_independent_outputs
|
|
||||||
|
|
||||||
|
|
||||||
# https://github.com/NixOS/nix/issues/6572
|
|
||||||
issue_6572_dependent_outputs() {
|
|
||||||
|
|
||||||
nix build -f multiple-outputs.nix --json a --no-link > $TEST_ROOT/a.json
|
|
||||||
|
|
||||||
# # Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation.
|
|
||||||
p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths)
|
|
||||||
nix-store --delete "$p" # Clean up for next test
|
|
||||||
|
|
||||||
# Make sure that 'nix build' tracks input-outputs correctly when a single output is already present.
|
|
||||||
nix-store --delete "$(jq -r <$TEST_ROOT/a.json .[0].outputs.second)"
|
|
||||||
p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths)
|
|
||||||
cmp $p <<EOF
|
|
||||||
first
|
|
||||||
second
|
|
||||||
EOF
|
|
||||||
nix-store --delete "$p" # Clean up for next test
|
|
||||||
}
|
|
||||||
if isDaemonNewer "2.12pre0"; then
|
|
||||||
issue_6572_dependent_outputs
|
|
||||||
fi
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
source common.sh
|
source common.sh
|
||||||
|
|
||||||
drv=$(nix-instantiate --experimental-features ca-derivations ./content-addressed.nix -A rootCA --arg seed 1)
|
drv=$(nix-instantiate --experimental-features ca-derivations ./content-addressed.nix -A rootCA --arg seed 1)
|
||||||
nix --experimental-features 'nix-command ca-derivations' show-derivation --derivation "$drv" --arg seed 1
|
nix --experimental-features 'nix-command ca-derivations' show-derivation "$drv" --arg seed 1
|
||||||
|
|
||||||
buildAttr () {
|
buildAttr () {
|
||||||
local derivationPath=$1
|
local derivationPath=$1
|
||||||
|
|
5
tests/ca/new-build-cmd.sh
Normal file
5
tests/ca/new-build-cmd.sh
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
export NIX_TESTS_CA_BY_DEFAULT=1
|
||||||
|
cd ..
|
||||||
|
source ./build.sh
|
|
@ -7,5 +7,3 @@ requireDaemonNewerThan "2.4pre20210623"
|
||||||
export NIX_TESTS_CA_BY_DEFAULT=1
|
export NIX_TESTS_CA_BY_DEFAULT=1
|
||||||
cd ..
|
cd ..
|
||||||
source ./recursive.sh
|
source ./recursive.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ if [[ -z "$COMMON_SH_SOURCED" ]]; then
|
||||||
|
|
||||||
COMMON_SH_SOURCED=1
|
COMMON_SH_SOURCED=1
|
||||||
|
|
||||||
|
export PS4='+(${BASH_SOURCE[0]}:$LINENO) '
|
||||||
|
|
||||||
export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default}
|
export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default}
|
||||||
export NIX_STORE_DIR
|
export NIX_STORE_DIR
|
||||||
if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then
|
if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then
|
||||||
|
@ -27,6 +29,8 @@ export NIX_REMOTE=$NIX_REMOTE_
|
||||||
unset NIX_PATH
|
unset NIX_PATH
|
||||||
export TEST_HOME=$TEST_ROOT/test-home
|
export TEST_HOME=$TEST_ROOT/test-home
|
||||||
export HOME=$TEST_HOME
|
export HOME=$TEST_HOME
|
||||||
|
unset XDG_STATE_HOME
|
||||||
|
unset XDG_DATA_HOME
|
||||||
unset XDG_CONFIG_HOME
|
unset XDG_CONFIG_HOME
|
||||||
unset XDG_CONFIG_DIRS
|
unset XDG_CONFIG_DIRS
|
||||||
unset XDG_CACHE_HOME
|
unset XDG_CACHE_HOME
|
||||||
|
@ -62,8 +66,8 @@ readLink() {
|
||||||
}
|
}
|
||||||
|
|
||||||
clearProfiles() {
|
clearProfiles() {
|
||||||
profiles="$HOME"/.local/share/nix/profiles
|
profiles="$HOME"/.local/state/nix/profiles
|
||||||
rm -rf $profiles
|
rm -rf "$profiles"
|
||||||
}
|
}
|
||||||
|
|
||||||
clearStore() {
|
clearStore() {
|
||||||
|
|
|
@ -104,3 +104,28 @@ noSubmoduleRepoBaseline=$(nix eval --raw --expr "(builtins.fetchGit { url = file
|
||||||
noSubmoduleRepo=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$subRepo; rev = \"$subRev\"; submodules = true; }).outPath")
|
noSubmoduleRepo=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$subRepo; rev = \"$subRev\"; submodules = true; }).outPath")
|
||||||
|
|
||||||
[[ $noSubmoduleRepoBaseline == $noSubmoduleRepo ]]
|
[[ $noSubmoduleRepoBaseline == $noSubmoduleRepo ]]
|
||||||
|
|
||||||
|
# Test relative submodule URLs.
|
||||||
|
rm $TEST_HOME/.cache/nix/fetcher-cache*
|
||||||
|
rm -rf $rootRepo/.git $rootRepo/.gitmodules $rootRepo/sub
|
||||||
|
initGitRepo $rootRepo
|
||||||
|
git -C $rootRepo submodule add ../gitSubmodulesSub sub
|
||||||
|
git -C $rootRepo commit -m "Add submodule"
|
||||||
|
rev2=$(git -C $rootRepo rev-parse HEAD)
|
||||||
|
pathWithRelative=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev2\"; submodules = true; }).outPath")
|
||||||
|
diff -r -x .gitmodules $pathWithSubmodules $pathWithRelative
|
||||||
|
|
||||||
|
# Test clones that have an upstream with relative submodule URLs.
|
||||||
|
rm $TEST_HOME/.cache/nix/fetcher-cache*
|
||||||
|
cloneRepo=$TEST_ROOT/a/b/gitSubmodulesClone # NB /a/b to make the relative path not work relative to $cloneRepo
|
||||||
|
git clone $rootRepo $cloneRepo
|
||||||
|
pathIndirect=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$cloneRepo; rev = \"$rev2\"; submodules = true; }).outPath")
|
||||||
|
[[ $pathIndirect = $pathWithRelative ]]
|
||||||
|
|
||||||
|
# Test that if the clone has the submodule already, we're not fetching
|
||||||
|
# it again.
|
||||||
|
git -C $cloneRepo submodule update --init
|
||||||
|
rm $TEST_HOME/.cache/nix/fetcher-cache*
|
||||||
|
rm -rf $subRepo
|
||||||
|
pathSubmoduleGone=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$cloneRepo; rev = \"$rev2\"; submodules = true; }).outPath")
|
||||||
|
[[ $pathSubmoduleGone = $pathWithRelative ]]
|
||||||
|
|
|
@ -37,3 +37,6 @@ nix-build check.nix -A nondeterministic --sandbox-paths /nix/store --no-out-link
|
||||||
(! nix-build check.nix -A nondeterministic --sandbox-paths /nix/store --no-out-link --check -K 2> $TEST_ROOT/log)
|
(! nix-build check.nix -A nondeterministic --sandbox-paths /nix/store --no-out-link --check -K 2> $TEST_ROOT/log)
|
||||||
if grep -q 'error: renaming' $TEST_ROOT/log; then false; fi
|
if grep -q 'error: renaming' $TEST_ROOT/log; then false; fi
|
||||||
grep -q 'may not be deterministic' $TEST_ROOT/log
|
grep -q 'may not be deterministic' $TEST_ROOT/log
|
||||||
|
|
||||||
|
# Test that sandboxed builds cannot write to /etc easily
|
||||||
|
(! nix-build -E 'with import ./config.nix; mkDerivation { name = "etc-write"; buildCommand = "echo > /etc/test"; }' --no-out-link --sandbox-paths /nix/store)
|
||||||
|
|
|
@ -22,6 +22,7 @@ nix_tests = \
|
||||||
binary-cache.sh \
|
binary-cache.sh \
|
||||||
multiple-outputs.sh \
|
multiple-outputs.sh \
|
||||||
ca/build.sh \
|
ca/build.sh \
|
||||||
|
ca/new-build-cmd.sh \
|
||||||
nix-build.sh \
|
nix-build.sh \
|
||||||
gc-concurrent.sh \
|
gc-concurrent.sh \
|
||||||
repair.sh \
|
repair.sh \
|
||||||
|
@ -104,6 +105,8 @@ nix_tests = \
|
||||||
ssh-relay.sh \
|
ssh-relay.sh \
|
||||||
plugins.sh \
|
plugins.sh \
|
||||||
build.sh \
|
build.sh \
|
||||||
|
build-delete.sh \
|
||||||
|
output-normalization.sh \
|
||||||
ca/nix-run.sh \
|
ca/nix-run.sh \
|
||||||
selfref-gc.sh ca/selfref-gc.sh \
|
selfref-gc.sh ca/selfref-gc.sh \
|
||||||
db-migration.sh \
|
db-migration.sh \
|
||||||
|
|
|
@ -12,6 +12,19 @@ nix-channel --remove xyzzy
|
||||||
[ -e $TEST_HOME/.nix-channels ]
|
[ -e $TEST_HOME/.nix-channels ]
|
||||||
[ "$(cat $TEST_HOME/.nix-channels)" = '' ]
|
[ "$(cat $TEST_HOME/.nix-channels)" = '' ]
|
||||||
|
|
||||||
|
# Test the XDG Base Directories support
|
||||||
|
|
||||||
|
export NIX_CONFIG="use-xdg-base-directories = true"
|
||||||
|
|
||||||
|
nix-channel --add http://foo/bar xyzzy
|
||||||
|
nix-channel --list | grep -q http://foo/bar
|
||||||
|
nix-channel --remove xyzzy
|
||||||
|
|
||||||
|
unset NIX_CONFIG
|
||||||
|
|
||||||
|
[ -e $TEST_HOME/.local/state/nix/channels ]
|
||||||
|
[ "$(cat $TEST_HOME/.local/state/nix/channels)" = '' ]
|
||||||
|
|
||||||
# Create a channel.
|
# Create a channel.
|
||||||
rm -rf $TEST_ROOT/foo
|
rm -rf $TEST_ROOT/foo
|
||||||
mkdir -p $TEST_ROOT/foo
|
mkdir -p $TEST_ROOT/foo
|
||||||
|
|
|
@ -56,6 +56,14 @@ nix profile history
|
||||||
nix profile history | grep "packages.$system.default: ∅ -> 1.0"
|
nix profile history | grep "packages.$system.default: ∅ -> 1.0"
|
||||||
nix profile diff-closures | grep 'env-manifest.nix: ε → ∅'
|
nix profile diff-closures | grep 'env-manifest.nix: ε → ∅'
|
||||||
|
|
||||||
|
# Test XDG Base Directories support
|
||||||
|
|
||||||
|
export NIX_CONFIG="use-xdg-base-directories = true"
|
||||||
|
nix profile remove 1
|
||||||
|
nix profile install $flake1Dir
|
||||||
|
[[ $($TEST_HOME/.local/state/nix/profile/bin/hello) = "Hello World" ]]
|
||||||
|
unset NIX_CONFIG
|
||||||
|
|
||||||
# Test upgrading a package.
|
# Test upgrading a package.
|
||||||
printf NixOS > $flake1Dir/who
|
printf NixOS > $flake1Dir/who
|
||||||
printf 2.0 > $flake1Dir/version
|
printf 2.0 > $flake1Dir/version
|
||||||
|
|
79
tests/nixos/authorization.nix
Normal file
79
tests/nixos/authorization.nix
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
{
|
||||||
|
name = "authorization";
|
||||||
|
|
||||||
|
nodes.machine = {
|
||||||
|
virtualisation.writableStore = true;
|
||||||
|
# TODO add a test without allowed-users setting. allowed-users is uncommon among NixOS users.
|
||||||
|
nix.settings.allowed-users = ["alice" "bob"];
|
||||||
|
nix.settings.trusted-users = ["alice"];
|
||||||
|
|
||||||
|
users.users.alice.isNormalUser = true;
|
||||||
|
users.users.bob.isNormalUser = true;
|
||||||
|
users.users.mallory.isNormalUser = true;
|
||||||
|
|
||||||
|
nix.settings.experimental-features = "nix-command";
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
let
|
||||||
|
pathFour = "/nix/store/20xfy868aiic0r0flgzq4n5dq1yvmxkn-four";
|
||||||
|
in
|
||||||
|
''
|
||||||
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
machine.succeed("""
|
||||||
|
exec 1>&2
|
||||||
|
echo kSELDhobKaF8/VdxIxdP7EQe+Q > one
|
||||||
|
diff $(nix store add-file one) one
|
||||||
|
""")
|
||||||
|
machine.succeed("""
|
||||||
|
su --login alice -c '
|
||||||
|
set -x
|
||||||
|
cd ~
|
||||||
|
echo ehHtmfuULXYyBV6NBk6QUi8iE0 > two
|
||||||
|
ls
|
||||||
|
diff $(echo $(nix store add-file two)) two' 1>&2
|
||||||
|
""")
|
||||||
|
machine.succeed("""
|
||||||
|
su --login bob -c '
|
||||||
|
set -x
|
||||||
|
cd ~
|
||||||
|
echo 0Jw8RNp7cK0W2AdNbcquofcOVk > three
|
||||||
|
diff $(nix store add-file three) three
|
||||||
|
' 1>&2
|
||||||
|
""")
|
||||||
|
|
||||||
|
# We're going to check that a path is not created
|
||||||
|
machine.succeed("""
|
||||||
|
! [[ -e ${pathFour} ]]
|
||||||
|
""")
|
||||||
|
machine.succeed("""
|
||||||
|
su --login mallory -c '
|
||||||
|
set -x
|
||||||
|
cd ~
|
||||||
|
echo 5mgtDj0ohrWkT50TLR0f4tIIxY > four;
|
||||||
|
(! nix store add-file four 2>&1) | grep -F "cannot open connection to remote store"
|
||||||
|
(! nix store add-file four 2>&1) | grep -F "Connection reset by peer"
|
||||||
|
! [[ -e ${pathFour} ]]
|
||||||
|
' 1>&2
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Check that the file _can_ be added, and matches the expected path we were checking
|
||||||
|
machine.succeed("""
|
||||||
|
exec 1>&2
|
||||||
|
echo 5mgtDj0ohrWkT50TLR0f4tIIxY > four
|
||||||
|
four="$(nix store add-file four)"
|
||||||
|
diff $four four
|
||||||
|
diff <(echo $four) <(echo ${pathFour})
|
||||||
|
""")
|
||||||
|
|
||||||
|
machine.succeed("""
|
||||||
|
su --login alice -c 'nix-store --verify --repair'
|
||||||
|
""")
|
||||||
|
|
||||||
|
machine.succeed("""
|
||||||
|
set -x
|
||||||
|
su --login bob -c '(! nix-store --verify --repair 2>&1)' | tee diag 1>&2
|
||||||
|
grep -F "you are not privileged to repair paths" diag
|
||||||
|
""")
|
||||||
|
'';
|
||||||
|
}
|
|
@ -56,12 +56,12 @@ runCommand "test"
|
||||||
# Make /run a tmpfs to shut up a systemd warning.
|
# Make /run a tmpfs to shut up a systemd warning.
|
||||||
mkdir /run
|
mkdir /run
|
||||||
mount -t tmpfs none /run
|
mount -t tmpfs none /run
|
||||||
chmod 0700 /run
|
|
||||||
|
|
||||||
mount -t cgroup2 none /sys/fs/cgroup
|
mount -t cgroup2 none /sys/fs/cgroup
|
||||||
|
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
|
|
||||||
|
chmod +w /etc
|
||||||
touch /etc/os-release
|
touch /etc/os-release
|
||||||
echo a5ea3f98dedc0278b6f3cc8c37eeaeac > /etc/machine-id
|
echo a5ea3f98dedc0278b6f3cc8c37eeaeac > /etc/machine-id
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,11 @@ let
|
||||||
{ services.openssh.enable = true;
|
{ services.openssh.enable = true;
|
||||||
virtualisation.writableStore = true;
|
virtualisation.writableStore = true;
|
||||||
nix.settings.sandbox = true;
|
nix.settings.sandbox = true;
|
||||||
|
|
||||||
|
# Regression test for use of PID namespaces when /proc has
|
||||||
|
# filesystems mounted on top of it
|
||||||
|
# (i.e. /proc/sys/fs/binfmt_misc).
|
||||||
|
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Trivial Nix expression to build remotely.
|
# Trivial Nix expression to build remotely.
|
||||||
|
|
9
tests/output-normalization.sh
Normal file
9
tests/output-normalization.sh
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
testNormalization () {
|
||||||
|
clearStore
|
||||||
|
outPath=$(nix-build ./simple.nix --no-out-link)
|
||||||
|
test "$(stat -c %Y $outPath)" -eq 1
|
||||||
|
}
|
||||||
|
|
||||||
|
testNormalization
|
Loading…
Add table
Add a link
Reference in a new issue