diff --git a/.github/labeler.yml b/.github/labeler.yml
index dc502b6d5..fce0d3aeb 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -2,5 +2,22 @@
- doc/manual/*
- src/nix/**/*.md
+"store":
+ - src/libstore/store-api.*
+ - src/libstore/*-store.*
+
+"fetching":
+ - src/libfetchers/**/*
+
+"repl":
+ - src/libcmd/repl.*
+ - src/nix/repl.*
+
+"new-cli":
+ - src/nix/**/*
+
"tests":
+ # Unit tests
+ - src/*/tests/**/*
+ # Functional and integration tests
- tests/**/*
diff --git a/.gitignore b/.gitignore
index e326966d6..8ceff4ef2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,9 +19,12 @@ perl/Makefile.config
/doc/manual/nix.json
/doc/manual/conf-file.json
/doc/manual/builtins.json
+/doc/manual/xp-features.json
/doc/manual/src/SUMMARY.md
/doc/manual/src/command-ref/new-cli
/doc/manual/src/command-ref/conf-file.md
+/doc/manual/src/command-ref/experimental-features-shortlist.md
+/doc/manual/src/contributing/experimental-feature-descriptions.md
/doc/manual/src/language/builtins.md
# /scripts/
diff --git a/.version b/.version
index c910885a0..752490696 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-2.15.0
\ No newline at end of file
+2.16.0
diff --git a/configure.ac b/configure.ac
index e587bd563..bb3f92e4d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -184,7 +184,7 @@ fi
# Look for OpenSSL, a required dependency. FIXME: this is only (maybe)
# used by S3BinaryCacheStore.
-PKG_CHECK_MODULES([OPENSSL], [libcrypto], [CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS"])
+PKG_CHECK_MODULES([OPENSSL], [libcrypto >= 1.1.1], [CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS"])
# Look for libarchive.
diff --git a/doc/manual/generate-builtins.nix b/doc/manual/generate-builtins.nix
index 115bb3f94..71f96153f 100644
--- a/doc/manual/generate-builtins.nix
+++ b/doc/manual/generate-builtins.nix
@@ -1,8 +1,12 @@
-builtinsDump:
+let
+ inherit (builtins) concatStringsSep attrNames;
+in
+
+builtinsInfo:
let
showBuiltin = name:
let
- inherit (builtinsDump.${name}) doc args;
+ inherit (builtinsInfo.${name}) doc args;
in
''
@@ -14,7 +18,7 @@ let
'';
- listArgs = args: builtins.concatStringsSep " " (map (s: "${s}") args);
+ listArgs = args: concatStringsSep " " (map (s: "${s}") args);
in
-with builtins; concatStringsSep "\n" (map showBuiltin (attrNames builtinsDump))
+concatStringsSep "\n" (map showBuiltin (attrNames builtinsInfo))
diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix
index 86f2ca567..fb34898f3 100644
--- a/doc/manual/generate-manpage.nix
+++ b/doc/manual/generate-manpage.nix
@@ -1,16 +1,24 @@
-cliDumpStr:
+let
+ inherit (builtins)
+ attrNames attrValues fromJSON listToAttrs mapAttrs
+ concatStringsSep concatMap length lessThan replaceStrings sort;
+ inherit (import ./utils.nix) concatStrings optionalString filterAttrs trim squash unique showSettings;
+in
-with builtins;
-with import ./utils.nix;
+commandDump:
let
+ commandInfo = fromJSON commandDump;
+
showCommand = { command, details, filename, toplevel }:
let
result = ''
> **Warning** \
- > This program is **experimental** and its interface is subject to change.
+ > This program is
+ > [**experimental**](@docroot@/contributing/experimental-features.md#xp-feature-nix-command)
+ > and its interface is subject to change.
# Name
@@ -29,19 +37,18 @@ let
showSynopsis = command: args:
let
- showArgument = arg: "*${arg.label}*" + (if arg ? arity then "" else "...");
+ showArgument = arg: "*${arg.label}*" + optionalString (! arg ? arity) "...";
arguments = concatStringsSep " " (map showArgument args);
in ''
`${command}` [*option*...] ${arguments}
'';
- maybeSubcommands = if details ? commands && details.commands != {}
- then ''
+ maybeSubcommands = optionalString (details ? commands && details.commands != {})
+ ''
where *subcommand* is one of the following:
${subcommands}
- ''
- else "";
+ '';
subcommands = if length categories > 1
then listCategories
@@ -63,12 +70,11 @@ let
* [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
'';
- maybeDocumentation =
- if details ? doc
- then replaceStrings ["@stores@"] [storeDocs] details.doc
- else "";
+ maybeDocumentation = optionalString
+ (details ? doc)
+ (replaceStrings ["@stores@"] [storeDocs] details.doc);
- maybeOptions = if details.flags == {} then "" else ''
+ maybeOptions = optionalString (details.flags != {}) ''
# Options
${showOptions details.flags toplevel.flags}
@@ -78,21 +84,25 @@ let
let
allOptions = options // commonOptions;
showCategory = cat: ''
- ${if cat != "" then "**${cat}:**" else ""}
+ ${optionalString (cat != "") "**${cat}:**"}
${listOptions (filterAttrs (n: v: v.category == cat) allOptions)}
'';
listOptions = opts: concatStringsSep "\n" (attrValues (mapAttrs showOption opts));
showOption = name: option:
let
- shortName = if option ? shortName then "/ `-${option.shortName}`" else "";
- labels = if option ? labels then (concatStringsSep " " (map (s: "*${s}*") option.labels)) else "";
+ shortName = optionalString
+ (option ? shortName)
+ ("/ `-${option.shortName}`");
+ labels = optionalString
+ (option ? labels)
+ (concatStringsSep " " (map (s: "*${s}*") option.labels));
in trim ''
- `--${name}` ${shortName} ${labels}
${option.description}
'';
- categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues allOptions)));
+ categories = sort lessThan (unique (map (cmd: cmd.category) (attrValues allOptions)));
in concatStrings (map showCategory categories);
in squash result;
@@ -113,13 +123,11 @@ let
};
in [ cmd ] ++ concatMap subcommand (attrNames details.commands or {});
- cliDump = builtins.fromJSON cliDumpStr;
-
manpages = processCommand {
command = "nix";
- details = cliDump.args;
+ details = commandInfo.args;
filename = "nix";
- toplevel = cliDump.args;
+ toplevel = commandInfo.args;
};
tableOfContents = let
@@ -139,6 +147,6 @@ let
${showSettings { useAnchors = false; } settings}
'';
- in concatStrings (attrValues (mapAttrs showStore cliDump.stores));
+ in concatStrings (attrValues (mapAttrs showStore commandInfo.stores));
in (listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; }
diff --git a/doc/manual/generate-xp-features-shortlist.nix b/doc/manual/generate-xp-features-shortlist.nix
new file mode 100644
index 000000000..30e211c96
--- /dev/null
+++ b/doc/manual/generate-xp-features-shortlist.nix
@@ -0,0 +1,9 @@
+with builtins;
+with import ./utils.nix;
+
+let
+ showExperimentalFeature = name: doc:
+ ''
+ - [`${name}`](@docroot@/contributing/experimental-features.md#xp-feature-${name})
+ '';
+in xps: indent " " (concatStrings (attrValues (mapAttrs showExperimentalFeature xps)))
diff --git a/doc/manual/generate-xp-features.nix b/doc/manual/generate-xp-features.nix
new file mode 100644
index 000000000..adb94355c
--- /dev/null
+++ b/doc/manual/generate-xp-features.nix
@@ -0,0 +1,11 @@
+with builtins;
+with import ./utils.nix;
+
+let
+ showExperimentalFeature = name: doc:
+ squash ''
+ ## [`${name}`]{#xp-feature-${name}}
+
+ ${doc}
+ '';
+in xps: (concatStringsSep "\n" (attrValues (mapAttrs showExperimentalFeature xps)))
diff --git a/doc/manual/local.mk b/doc/manual/local.mk
index df941d460..63e7e61e4 100644
--- a/doc/manual/local.mk
+++ b/doc/manual/local.mk
@@ -81,19 +81,20 @@ $(d)/%.8: $(d)/src/command-ref/%.md
$(d)/nix.conf.5: $(d)/src/command-ref/conf-file.md
@printf "Title: %s\n\n" "$$(basename $@ .5)" > $^.tmp
@cat $^ >> $^.tmp
+ @$(call process-includes,$^,$^.tmp)
$(trace-gen) lowdown -sT man --nroff-nolinks -M section=5 $^.tmp -o $@
@rm $^.tmp
-$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli
+$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md
@cp $< $@
@$(call process-includes,$@,$@)
-$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
+$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage.nix $(bindir)/nix
@rm -rf $@ $@.tmp
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix (builtins.readFile $<)'
@mv $@.tmp $@
-$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
+$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features-shortlist.md $(bindir)/nix
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
$(trace-gen) $(nix-eval) --expr '(import doc/manual/utils.nix).showSettings { useAnchors = true; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
@mv $@.tmp $@
@@ -106,6 +107,20 @@ $(d)/conf-file.json: $(bindir)/nix
$(trace-gen) $(dummy-env) $(bindir)/nix show-config --json --experimental-features nix-command > $@.tmp
@mv $@.tmp $@
+$(d)/src/contributing/experimental-feature-descriptions.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features.nix $(bindir)/nix
+ @rm -rf $@ $@.tmp
+ $(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-xp-features.nix (builtins.fromJSON (builtins.readFile $<))'
+ @mv $@.tmp $@
+
+$(d)/src/command-ref/experimental-features-shortlist.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features-shortlist.nix $(bindir)/nix
+ @rm -rf $@ $@.tmp
+ $(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-xp-features-shortlist.nix (builtins.fromJSON (builtins.readFile $<))'
+ @mv $@.tmp $@
+
+$(d)/xp-features.json: $(bindir)/nix
+ $(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp
+ @mv $@.tmp $@
+
$(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
@@ -145,7 +160,7 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli
done
@touch $@
-$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md
+$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md
$(trace-gen) \
tmp="$$(mktemp -d)"; \
cp -r doc/manual "$$tmp"; \
diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js
index 69f75d3a0..5cd6fdea2 100644
--- a/doc/manual/redirects.js
+++ b/doc/manual/redirects.js
@@ -338,6 +338,9 @@ const redirects = {
"strings": "#string",
"lists": "#list",
"attribute-sets": "#attribute-set"
+ },
+ "installation/installing-binary.html": {
+ "uninstalling": "uninstall.html"
}
};
diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in
index 5bf274550..1d5613354 100644
--- a/doc/manual/src/SUMMARY.md.in
+++ b/doc/manual/src/SUMMARY.md.in
@@ -15,6 +15,7 @@
- [Multi-User Mode](installation/multi-user.md)
- [Environment Variables](installation/env-variables.md)
- [Upgrading Nix](installation/upgrading.md)
+ - [Uninstalling Nix](installation/uninstall.md)
- [Package Management](package-management/package-management.md)
- [Basic Package Management](package-management/basic-package-mgmt.md)
- [Profiles](package-management/profiles.md)
@@ -99,6 +100,7 @@
- [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
+ - [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md)
- [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md)
- [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md)
- [Release 2.12 (2022-12-06)](release-notes/rl-2.12.md)
diff --git a/doc/manual/src/command-ref/experimental-commands.md b/doc/manual/src/command-ref/experimental-commands.md
index cfa6f8b73..286ddc6d6 100644
--- a/doc/manual/src/command-ref/experimental-commands.md
+++ b/doc/manual/src/command-ref/experimental-commands.md
@@ -1,6 +1,6 @@
# Experimental Commands
-This section lists experimental commands.
+This section lists [experimental commands](@docroot@/contributing/experimental-features.md#xp-feature-nix-command).
> **Warning**
>
diff --git a/doc/manual/src/command-ref/opt-common.md b/doc/manual/src/command-ref/opt-common.md
index a23b87e4e..7a012250d 100644
--- a/doc/manual/src/command-ref/opt-common.md
+++ b/doc/manual/src/command-ref/opt-common.md
@@ -203,10 +203,9 @@ Most Nix commands accept the following command-line options:
instead.
- [`-I`](#opt-I) *path*\
- Add a path to the Nix expression search path. This option may be
- given multiple times. See the `NIX_PATH` environment variable for
- information on the semantics of the Nix search path. Paths added
- through `-I` take precedence over `NIX_PATH`.
+ Add an entry to the [Nix expression search path](@docroot@/command-ref/conf-file.md#conf-nix-path).
+ This option may be given multiple times.
+ Paths added through `-I` take precedence over [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH).
- [`--option`](#opt-option) *name* *value*\
Set the Nix configuration option *name* to *value*. This overrides
diff --git a/doc/manual/src/contributing/experimental-features.md b/doc/manual/src/contributing/experimental-features.md
index f1db22751..ad5cffa91 100644
--- a/doc/manual/src/contributing/experimental-features.md
+++ b/doc/manual/src/contributing/experimental-features.md
@@ -89,3 +89,7 @@ However they serve different purposes:
It is primarily an issue of *design* and *communication*, targeting the broader community.
This means that experimental features and RFCs are orthogonal mechanisms, and can be used independently or together as needed.
+
+# Currently available experimental features
+
+{{#include ./experimental-feature-descriptions.md}}
diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md
index 4eedb2e93..eeb19ad50 100644
--- a/doc/manual/src/glossary.md
+++ b/doc/manual/src/glossary.md
@@ -127,7 +127,7 @@
builder can rely on external inputs such as the network or the
system time) but the Nix model assumes it.
- - Nix database{#gloss-nix-database}\
+ - [Nix database]{#gloss-nix-database}\
An SQlite database to track [reference]s between [store object]s.
This is an implementation detail of the [local store].
@@ -225,3 +225,9 @@
[string]: ./language/values.md#type-string
[path]: ./language/values.md#type-path
[attribute name]: ./language/values.md#attribute-set
+
+ - [experimental feature]{#gloss-experimental-feature}\
+ Not yet stabilized functionality guarded by named experimental feature flags.
+ These flags are enabled or disabled with the [`experimental-features`](./command-ref/conf-file.html#conf-experimental-features) setting.
+
+ See the contribution guide on the [purpose and lifecycle of experimental feaures](@docroot@/contributing/experimental-features.md).
diff --git a/doc/manual/src/installation/installing-binary.md b/doc/manual/src/installation/installing-binary.md
index e3fd962bd..ffabb250a 100644
--- a/doc/manual/src/installation/installing-binary.md
+++ b/doc/manual/src/installation/installing-binary.md
@@ -47,12 +47,6 @@ The install script will modify the first writable file from amongst
`NIX_INSTALLER_NO_MODIFY_PROFILE` environment variable before executing
the install script to disable this behaviour.
-You can uninstall Nix simply by running:
-
-```console
-$ rm -rf /nix
-```
-
# Multi User Installation
The multi-user Nix installation creates system users, and a system
@@ -84,154 +78,8 @@ The installer will modify `/etc/bashrc`, and `/etc/zshrc` if they exist.
The installer will first back up these files with a `.backup-before-nix`
extension. The installer will also create `/etc/profile.d/nix.sh`.
-## Uninstalling
-
-### Linux
-
-If you are on Linux with systemd:
-
-1. Remove the Nix daemon service:
-
- ```console
- sudo systemctl stop nix-daemon.service
- sudo systemctl disable nix-daemon.socket nix-daemon.service
- sudo systemctl daemon-reload
- ```
-
-1. Remove systemd service files:
-
- ```console
- sudo rm /etc/systemd/system/nix-daemon.service /etc/systemd/system/nix-daemon.socket
- ```
-
-1. The installer script uses systemd-tmpfiles to create the socket directory.
- You may also want to remove the configuration for that:
-
- ```console
- sudo rm /etc/tmpfiles.d/nix-daemon.conf
- ```
-
-Remove files created by Nix:
-
-```console
-sudo rm -rf /nix /etc/nix /etc/profile/nix.sh ~root/.nix-profile ~root/.nix-defexpr ~root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
-```
-
-Remove build users and their group:
-
-```console
-for i in $(seq 1 32); do
- sudo userdel nixbld$i
-done
-sudo groupdel nixbld
-```
-
-There may also be references to Nix in
-
-- `/etc/profile`
-- `/etc/bashrc`
-- `/etc/zshrc`
-
-which you may remove.
-
-### macOS
-
-1. Edit `/etc/zshrc` and `/etc/bashrc` to remove the lines sourcing
- `nix-daemon.sh`, which should look like this:
-
- ```bash
- # Nix
- if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
- . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
- fi
- # End Nix
- ```
-
- If these files haven't been altered since installing Nix you can simply put
- the backups back in place:
-
- ```console
- sudo mv /etc/zshrc.backup-before-nix /etc/zshrc
- sudo mv /etc/bashrc.backup-before-nix /etc/bashrc
- ```
-
- This will stop shells from sourcing the file and bringing everything you
- installed using Nix in scope.
-
-2. Stop and remove the Nix daemon services:
-
- ```console
- sudo launchctl unload /Library/LaunchDaemons/org.nixos.nix-daemon.plist
- sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
- sudo launchctl unload /Library/LaunchDaemons/org.nixos.darwin-store.plist
- sudo rm /Library/LaunchDaemons/org.nixos.darwin-store.plist
- ```
-
- This stops the Nix daemon and prevents it from being started next time you
- boot the system.
-
-3. Remove the `nixbld` group and the `_nixbuildN` users:
-
- ```console
- sudo dscl . -delete /Groups/nixbld
- for u in $(sudo dscl . -list /Users | grep _nixbld); do sudo dscl . -delete /Users/$u; done
- ```
-
- This will remove all the build users that no longer serve a purpose.
-
-4. Edit fstab using `sudo vifs` to remove the line mounting the Nix Store
- volume on `/nix`, which looks like
- `UUID= /nix apfs rw,noauto,nobrowse,suid,owners` or
- `LABEL=Nix\040Store /nix apfs rw,nobrowse`. This will prevent automatic
- mounting of the Nix Store volume.
-
-5. Edit `/etc/synthetic.conf` to remove the `nix` line. If this is the only
- line in the file you can remove it entirely, `sudo rm /etc/synthetic.conf`.
- This will prevent the creation of the empty `/nix` directory to provide a
- mountpoint for the Nix Store volume.
-
-6. Remove the files Nix added to your system:
-
- ```console
- sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
- ```
-
- This gets rid of any data Nix may have created except for the store which is
- removed next.
-
-7. Remove the Nix Store volume:
-
- ```console
- sudo diskutil apfs deleteVolume /nix
- ```
-
- This will remove the Nix Store volume and everything that was added to the
- store.
-
- If the output indicates that the command couldn't remove the volume, you should
- make sure you don't have an _unmounted_ Nix Store volume. Look for a
- "Nix Store" volume in the output of the following command:
-
- ```console
- diskutil list
- ```
-
- If you _do_ see a "Nix Store" volume, delete it by re-running the diskutil
- deleteVolume command, but replace `/nix` with the store volume's `diskXsY`
- identifier.
-
-> **Note**
->
-> After you complete the steps here, you will still have an empty `/nix`
-> directory. This is an expected sign of a successful uninstall. The empty
-> `/nix` directory will disappear the next time you reboot.
->
-> You do not have to reboot to finish uninstalling Nix. The uninstall is
-> complete. macOS (Catalina+) directly controls root directories and its
-> read-only root will prevent you from manually deleting the empty `/nix`
-> mountpoint.
-
# macOS Installation
+
[]{#sect-macos-installation-change-store-prefix}[]{#sect-macos-installation-encrypted-volume}[]{#sect-macos-installation-symlink}[]{#sect-macos-installation-recommended-notes}
@@ -280,19 +128,16 @@ this to run the installer, but it may help if you run into trouble:
# Installing a pinned Nix version from a URL
-NixOS.org hosts version-specific installation URLs for all Nix versions
-since 1.11.16, at `https://releases.nixos.org/nix/nix-version/install`.
+Version-specific installation URLs for all Nix versions
+since 1.11.16 can be found at [releases.nixos.org](https://releases.nixos.org/?prefix=nix/).
+The corresponding SHA-256 hash can be found in the directory for the given version.
-These install scripts can be used the same as the main NixOS.org
-installation script:
+These install scripts can be used the same as usual:
```console
-$ curl -L https://nixos.org/nix/install | sh
+$ curl -L https://releases.nixos.org/nix/nix-/install | sh
```
-In the same directory of the install script are sha256 sums, and gpg
-signature files.
-
# Installing from a binary tarball
You can also download a binary tarball that contains Nix and all its
diff --git a/doc/manual/src/installation/uninstall.md b/doc/manual/src/installation/uninstall.md
new file mode 100644
index 000000000..bd85b49ee
--- /dev/null
+++ b/doc/manual/src/installation/uninstall.md
@@ -0,0 +1,159 @@
+# Uninstalling Nix
+
+## Single User
+
+If you have a [single-user installation](./installing-binary.md#single-user-installation) of Nix, uninstall it by running:
+
+```console
+$ rm -rf /nix
+```
+
+## Multi User
+
+Removing a [multi-user installation](./installing-binary.md#multi-user-installation) of Nix is more involved, and depends on the operating system.
+
+### Linux
+
+If you are on Linux with systemd:
+
+1. Remove the Nix daemon service:
+
+ ```console
+ sudo systemctl stop nix-daemon.service
+ sudo systemctl disable nix-daemon.socket nix-daemon.service
+ sudo systemctl daemon-reload
+ ```
+
+1. Remove systemd service files:
+
+ ```console
+ sudo rm /etc/systemd/system/nix-daemon.service /etc/systemd/system/nix-daemon.socket
+ ```
+
+1. The installer script uses systemd-tmpfiles to create the socket directory.
+ You may also want to remove the configuration for that:
+
+ ```console
+ sudo rm /etc/tmpfiles.d/nix-daemon.conf
+ ```
+
+Remove files created by Nix:
+
+```console
+sudo rm -rf /nix /etc/nix /etc/profile/nix.sh ~root/.nix-profile ~root/.nix-defexpr ~root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
+```
+
+Remove build users and their group:
+
+```console
+for i in $(seq 1 32); do
+ sudo userdel nixbld$i
+done
+sudo groupdel nixbld
+```
+
+There may also be references to Nix in
+
+- `/etc/profile`
+- `/etc/bashrc`
+- `/etc/zshrc`
+
+which you may remove.
+
+### macOS
+
+1. Edit `/etc/zshrc`, `/etc/bashrc`, and `/etc/bash.bashrc` to remove the lines sourcing `nix-daemon.sh`, which should look like this:
+
+ ```bash
+ # Nix
+ if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
+ . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
+ fi
+ # End Nix
+ ```
+
+ If these files haven't been altered since installing Nix you can simply put
+ the backups back in place:
+
+ ```console
+ sudo mv /etc/zshrc.backup-before-nix /etc/zshrc
+ sudo mv /etc/bashrc.backup-before-nix /etc/bashrc
+ sudo mv /etc/bash.bashrc.backup-before-nix /etc/bash.bashrc
+ ```
+
+ This will stop shells from sourcing the file and bringing everything you
+ installed using Nix in scope.
+
+2. Stop and remove the Nix daemon services:
+
+ ```console
+ sudo launchctl unload /Library/LaunchDaemons/org.nixos.nix-daemon.plist
+ sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
+ sudo launchctl unload /Library/LaunchDaemons/org.nixos.darwin-store.plist
+ sudo rm /Library/LaunchDaemons/org.nixos.darwin-store.plist
+ ```
+
+ This stops the Nix daemon and prevents it from being started next time you
+ boot the system.
+
+3. Remove the `nixbld` group and the `_nixbuildN` users:
+
+ ```console
+ sudo dscl . -delete /Groups/nixbld
+ for u in $(sudo dscl . -list /Users | grep _nixbld); do sudo dscl . -delete /Users/$u; done
+ ```
+
+ This will remove all the build users that no longer serve a purpose.
+
+4. Edit fstab using `sudo vifs` to remove the line mounting the Nix Store
+ volume on `/nix`, which looks like
+ `UUID= /nix apfs rw,noauto,nobrowse,suid,owners` or
+ `LABEL=Nix\040Store /nix apfs rw,nobrowse`. This will prevent automatic
+ mounting of the Nix Store volume.
+
+5. Edit `/etc/synthetic.conf` to remove the `nix` line. If this is the only
+ line in the file you can remove it entirely, `sudo rm /etc/synthetic.conf`.
+ This will prevent the creation of the empty `/nix` directory to provide a
+ mountpoint for the Nix Store volume.
+
+6. Remove the files Nix added to your system:
+
+ ```console
+ sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
+ ```
+
+ This gets rid of any data Nix may have created except for the store which is
+ removed next.
+
+7. Remove the Nix Store volume:
+
+ ```console
+ sudo diskutil apfs deleteVolume /nix
+ ```
+
+ This will remove the Nix Store volume and everything that was added to the
+ store.
+
+ If the output indicates that the command couldn't remove the volume, you should
+ make sure you don't have an _unmounted_ Nix Store volume. Look for a
+ "Nix Store" volume in the output of the following command:
+
+ ```console
+ diskutil list
+ ```
+
+ If you _do_ see a "Nix Store" volume, delete it by re-running the diskutil
+ deleteVolume command, but replace `/nix` with the store volume's `diskXsY`
+ identifier.
+
+> **Note**
+>
+> After you complete the steps here, you will still have an empty `/nix`
+> directory. This is an expected sign of a successful uninstall. The empty
+> `/nix` directory will disappear the next time you reboot.
+>
+> You do not have to reboot to finish uninstalling Nix. The uninstall is
+> complete. macOS (Catalina+) directly controls root directories and its
+> read-only root will prevent you from manually deleting the empty `/nix`
+> mountpoint.
+
diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md
index 3e8c48890..307971434 100644
--- a/doc/manual/src/language/advanced-attributes.md
+++ b/doc/manual/src/language/advanced-attributes.md
@@ -208,12 +208,26 @@ Derivations can declare some infrequently used optional attributes.
about converting to and from base-32 notation.)
- [`__contentAddressed`]{#adv-attr-__contentAddressed}
- If this **experimental** attribute is set to true, then the derivation
+ > **Warning**
+ > This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md).
+ >
+ > To use this attribute, you must enable the
+ > [`ca-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-ca-derivations) experimental feature.
+ > For example, in [nix.conf](../command-ref/conf-file.md) you could add:
+ >
+ > ```
+ > extra-experimental-features = ca-derivations
+ > ```
+
+ If this attribute is set to `true`, then the derivation
outputs will be stored in a content-addressed location rather than the
traditional input-addressed one.
- This only has an effect if the `ca-derivations` experimental feature is enabled.
- Setting this attribute also requires setting `outputHashMode` and `outputHashAlgo` like for *fixed-output derivations* (see above).
+ Setting this attribute also requires setting
+ [`outputHashMode`](#adv-attr-outputHashMode)
+ and
+ [`outputHashAlgo`](#adv-attr-outputHashAlgo)
+ like for *fixed-output derivations* (see above).
- [`passAsFile`]{#adv-attr-passAsFile}\
A list of names of attributes that should be passed via files rather
@@ -307,9 +321,11 @@ Derivations can declare some infrequently used optional attributes.
- [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\
> **Warning**
- > This is an experimental feature.
+ > This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md).
>
- > To enable it, add the following to [nix.conf](../command-ref/conf-file.md):
+ > To use this attribute, you must enable the
+ > [`discard-references`](@docroot@/contributing/experimental-features.md#xp-feature-discard-references) experimental feature.
+ > For example, in [nix.conf](../command-ref/conf-file.md) you could add:
>
> ```
> extra-experimental-features = discard-references
diff --git a/doc/manual/src/quick-start.md b/doc/manual/src/quick-start.md
index 651134c25..1d2688ede 100644
--- a/doc/manual/src/quick-start.md
+++ b/doc/manual/src/quick-start.md
@@ -19,7 +19,7 @@ to subsequent chapters.
channel:
```console
- $ nix-env -qaP
+ $ nix-env --query --available --attr-path
nixpkgs.docbook_xml_dtd_43 docbook-xml-4.3
nixpkgs.docbook_xml_dtd_45 docbook-xml-4.5
nixpkgs.firefox firefox-33.0.2
@@ -31,7 +31,7 @@ to subsequent chapters.
1. Install some packages from the channel:
```console
- $ nix-env -iA nixpkgs.hello
+ $ nix-env --install --attr nixpkgs.hello
```
This should download pre-built packages; it should not build them
@@ -49,13 +49,13 @@ to subsequent chapters.
1. Uninstall a package:
```console
- $ nix-env -e hello
+ $ nix-env --uninstall hello
```
1. You can also test a package without installing it:
```console
- $ nix-shell -p hello
+ $ nix-shell --packages hello
```
This builds or downloads GNU Hello and its dependencies, then drops
@@ -76,7 +76,7 @@ to subsequent chapters.
```console
$ nix-channel --update nixpkgs
- $ nix-env -u '*'
+ $ nix-env --upgrade '*'
```
The latter command will upgrade each installed package for which
@@ -95,5 +95,5 @@ to subsequent chapters.
them:
```console
- $ nix-collect-garbage -d
+ $ nix-collect-garbage --delete-old
```
diff --git a/doc/manual/src/release-notes/rl-2.15.md b/doc/manual/src/release-notes/rl-2.15.md
new file mode 100644
index 000000000..133121999
--- /dev/null
+++ b/doc/manual/src/release-notes/rl-2.15.md
@@ -0,0 +1,58 @@
+# Release 2.15 (2023-04-11)
+
+* Commands which take installables on the command line can now read them from the standard input if
+ passed the `--stdin` flag. This is primarily useful when you have a large amount of paths which
+ exceed the OS argument limit.
+
+* The `nix-hash` command now supports Base64 and SRI. Use the flags `--base64`
+ or `--sri` to specify the format of output hash as Base64 or SRI, and `--to-base64`
+ or `--to-sri` to convert a hash to Base64 or SRI format, respectively.
+
+ As the choice of hash formats is no longer binary, the `--base16` flag is also added
+ to explicitly specify the Base16 format, which is still the default.
+
+* The special handling of an [installable](../command-ref/new-cli/nix.md#installables) with `.drv` suffix being interpreted as all of the given [store derivation](../glossary.md#gloss-store-derivation)'s output paths is removed, and instead taken as the literal store path that it represents.
+
+ The new `^` syntax for store paths introduced in Nix 2.13 allows explicitly referencing output paths of a derivation.
+ Using this is better and more clear than relying on the now-removed `.drv` special handling.
+
+ For example,
+ ```shell-session
+ $ nix path-info /nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv
+ ```
+
+ now gives info about the derivation itself, while
+
+ ```shell-session
+ $ nix path-info /nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv^*
+ ```
+ provides information about each of its outputs.
+
+* The experimental command `nix describe-stores` has been removed.
+
+* Nix stores and their settings are now documented in [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md).
+
+* Documentation for operations of `nix-store` and `nix-env` are now available on separate pages of the manual.
+ They include all common options that can be specified and common environment variables that affect these commands.
+
+ These pages can be viewed offline with `man` using
+
+ * `man nix-store-` and `man nix-env-`
+ * `nix-store --help --` and `nix-env --help --`.
+
+* Nix when used as a client now checks whether the store (the server) trusts the client.
+ (The store always had to check whether it trusts the client, but now the client is informed of the store's decision.)
+ This is useful for scripting interactions with (non-legacy-ssh) remote Nix stores.
+
+ `nix store ping` and `nix doctor` now display this information.
+
+* The new command `nix derivation add` allows adding derivations to the store without involving the Nix language.
+ It exists to round out our collection of basic utility/plumbing commands, and allow for a low barrier-to-entry way of experimenting with alternative front-ends to the Nix Store.
+ It uses the same JSON layout as `nix derivation show`, and is its inverse.
+
+* `nix show-derivation` has been renamed to `nix derivation show`.
+ This matches `nix derivation add`, and avoids bloating the top-level namespace.
+ The old name is still kept as an alias for compatibility, however.
+
+* The `nix derivation {add,show}` JSON format now includes the derivation name as a top-level field.
+ This is useful in general, but especially necessary for the `add` direction, as otherwise we would need to pass in the name out of band for certain cases.
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index 5b62836bf..78ae99f4b 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -1,58 +1,2 @@
# Release X.Y (202?-??-??)
-* Commands which take installables on the command line can now read them from the standard input if
- passed the `--stdin` flag. This is primarily useful when you have a large amount of paths which
- exceed the OS arg limit.
-
-* The `nix-hash` command now supports Base64 and SRI. Use the flags `--base64`
- or `--sri` to specify the format of output hash as Base64 or SRI, and `--to-base64`
- or `--to-sri` to convert a hash to Base64 or SRI format, respectively.
-
- As the choice of hash formats is no longer binary, the `--base16` flag is also added
- to explicitly specify the Base16 format, which is still the default.
-
-* The special handling of an [installable](../command-ref/new-cli/nix.md#installables) with `.drv` suffix being interpreted as all of the given [store derivation](../glossary.md#gloss-store-derivation)'s output paths is removed, and instead taken as the literal store path that it represents.
-
- The new `^` syntax for store paths introduced in Nix 2.13 allows explicitly referencing output paths of a derivation.
- Using this is better and more clear than relying on the now-removed `.drv` special handling.
-
- For example,
- ```shell-session
- $ nix path-info /nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv
- ```
-
- now gives info about the derivation itself, while
-
- ```shell-session
- $ nix path-info /nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv^*
- ```
- provides information about each of its outputs.
-
-* The experimental command `nix describe-stores` has been removed.
-
-* Nix stores and their settings are now documented in [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md).
-
-* Documentation for operations of `nix-store` and `nix-env` are now available on separate pages of the manual.
- They include all common options that can be specified and common environment variables that affect these commands.
-
- These pages can be viewed offline with `man` using
-
- * `man nix-store-` and `man nix-env-`
- * `nix-store --help --` and `nix-env --help --`.
-
-* Nix when used as a client now checks whether the store (the server) trusts the client.
- (The store always had to check whether it trusts the client, but now the client is informed of the store's decision.)
- This is useful for scripting interactions with (non-legacy-ssh) remote Nix stores.
-
- `nix store ping` and `nix doctor` now display this information.
-
-* A new command `nix derivation add` is created, to allow adding derivations to the store without involving the Nix language.
- It exists to round out our collection of basic utility/plumbing commands, and allow for a low barrier-to-entry way of experimenting with alternative front-ends to the Nix Store.
- It uses the same JSON layout as `nix show-derivation`, and is its inverse.
-
-* `nix show-derivation` has been renamed to `nix derivation show`.
- This matches `nix derivation add`, and avoids bloating the top-level namespace.
- The old name is still kept as an alias for compatibility, however.
-
-* The `nix derivation {add,show}` JSON format now includes the derivation name as a top-level field.
- This is useful in general, but especially necessary for the `add` direction, as otherwise we would need to pass in the name out of band for certain cases.
diff --git a/doc/manual/utils.nix b/doc/manual/utils.nix
index 5eacce0dd..9043dd8cd 100644
--- a/doc/manual/utils.nix
+++ b/doc/manual/utils.nix
@@ -5,6 +5,9 @@ rec {
concatStrings = concatStringsSep "";
+ attrsToList = a:
+ map (name: { inherit name; value = a.${name}; }) (builtins.attrNames a);
+
replaceStringsRec = from: to: string:
# recursively replace occurrences of `from` with `to` within `string`
# example:
@@ -39,7 +42,9 @@ rec {
filterAttrs = pred: set:
listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
- showSetting = { useAnchors }: name: { description, documentDefault, defaultValue, aliases, value }:
+ optionalString = cond: string: if cond then string else "";
+
+ showSetting = { useAnchors }: name: { description, documentDefault, defaultValue, aliases, value, experimentalFeature }:
let
result = squash ''
- ${if useAnchors
@@ -49,10 +54,28 @@ rec {
${indent " " body}
'';
+ experimentalFeatureNote = optionalString (experimentalFeature != null) ''
+ > **Warning**
+ > This setting is part of an
+ > [experimental feature](@docroot@/contributing/experimental-features.md).
+
+ To change this setting, you need to make sure the corresponding experimental feature,
+ [`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
+ is enabled.
+ For example, include the following in [`nix.conf`](#):
+
+ ```
+ extra-experimental-features = ${experimentalFeature}
+ ${name} = ...
+ ```
+ '';
+
# separate body to cleanly handle indentation
body = ''
${description}
+ ${experimentalFeatureNote}
+
**Default:** ${showDefault documentDefault defaultValue}
${showAliases aliases}
@@ -71,13 +94,13 @@ rec {
else "*machine-specific*";
showAliases = aliases:
- if aliases == [] then "" else
+ optionalString (aliases != [])
"**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}";
- indent = prefix: s:
- concatStringsSep "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s));
-
in result;
+ indent = prefix: s:
+ concatStringsSep "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s));
+
showSettings = args: settingsInfo: concatStrings (attrValues (mapAttrs (showSetting args) settingsInfo));
}
diff --git a/maintainers/README.md b/maintainers/README.md
index 618bfb4e4..d13349438 100644
--- a/maintainers/README.md
+++ b/maintainers/README.md
@@ -42,12 +42,12 @@ The team meets twice a week:
- Discussion meeting: [Fridays 13:00-14:00 CET](https://calendar.google.com/calendar/event?eid=MHNtOGVuNWtrZXNpZHR2bW1sM3QyN2ZjaGNfMjAyMjExMjVUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn)
- 1. Triage issues and pull requests from the _No Status_ column (30 min)
- 2. Discuss issues and pull requests from the _To discuss_ column (30 min)
+ 1. Triage issues and pull requests from the [No Status](#no-status) column (30 min)
+ 2. Discuss issues and pull requests from the [To discuss](#to-discuss) column (30 min)
- Work meeting: [Mondays 13:00-15:00 CET](https://calendar.google.com/calendar/event?eid=NTM1MG1wNGJnOGpmOTZhYms3bTB1bnY5cWxfMjAyMjExMjFUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn)
- 1. Code review on pull requests from _In review_.
+ 1. Code review on pull requests from [In review](#in-review).
2. Other chores and tasks.
Meeting notes are collected on a [collaborative scratchpad](https://pad.lassul.us/Cv7FpYx-Ri-4VjUykQOLAw), and published on Discourse under the [Nix category](https://discourse.nixos.org/c/dev/nix/50).
@@ -58,64 +58,74 @@ The team uses a [GitHub project board](https://github.com/orgs/NixOS/projects/19
Items on the board progress through the following states:
-- No Status
+### No Status
- During the discussion meeting, the team triages new items.
- To be considered, issues and pull requests must have a high-level description to provide the whole team with the necessary context at a glance.
+During the discussion meeting, the team triages new items.
+To be considered, issues and pull requests must have a high-level description to provide the whole team with the necessary context at a glance.
- On every meeting, at least one item from each of the following categories is inspected:
+On every meeting, at least one item from each of the following categories is inspected:
- 1. [critical](https://github.com/NixOS/nix/labels/critical)
- 2. [security](https://github.com/NixOS/nix/labels/security)
- 3. [regression](https://github.com/NixOS/nix/labels/regression)
- 4. [bug](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Abug+sort%3Areactions-%2B1-desc)
- 5. [tests of existing functionality](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Atests+-label%3Afeature+sort%3Areactions-%2B1-desc)
+1. [critical](https://github.com/NixOS/nix/labels/critical)
+2. [security](https://github.com/NixOS/nix/labels/security)
+3. [regression](https://github.com/NixOS/nix/labels/regression)
+4. [bug](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Abug+sort%3Areactions-%2B1-desc)
+5. [tests of existing functionality](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Atests+-label%3Afeature+sort%3Areactions-%2B1-desc)
- - [oldest pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Acreated-asc)
- - [most popular pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Areactions-%2B1-desc)
- - [oldest issues](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Acreated-asc)
- - [most popular issues](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc)
+- [oldest pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Acreated-asc)
+- [most popular pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Areactions-%2B1-desc)
+- [oldest issues](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Acreated-asc)
+- [most popular issues](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc)
- Team members can also add pull requests or issues they would like the whole team to consider.
+Team members can also add pull requests or issues they would like the whole team to consider.
+To ensure process quality and reliability, all non-trivial pull requests must be triaged before merging.
- If there is disagreement on the general idea behind an issue or pull request, it is moved to _To discuss_, otherwise to _In review_.
+If there is disagreement on the general idea behind an issue or pull request, it is moved to [To discuss](#to-discuss).
+Otherwise, the issue or pull request in questions get the label [`idea approved`](https://github.com/NixOS/nix/labels/idea%20approved).
+For issues this means that an implementation is welcome and will be prioritised for review.
+For pull requests this means that:
+- Unfinished work is encouraged to be continued.
+- A reviewer is assigned to take responsibility for getting the pull request merged.
+ The item is moved to the [Assigned](#assigned) column.
+- If needed, the team can decide to do a collarorative review.
+ Then the item is moved to the [In review](#in-review) column, and review session is scheduled.
- To ensure process quality and reliability, all non-trivial pull requests must be triaged before merging.
- What constitutes a trivial pull request is up to maintainers' judgement.
+What constitutes a trivial pull request is up to maintainers' judgement.
-- To discuss
+### To discuss
- Pull requests and issues that are deemed important and controversial are discussed by the team during discussion meetings.
+Pull requests and issues that are deemed important and controversial are discussed by the team during discussion meetings.
- This may be where the merit of the change itself or the implementation strategy is contested by a team member.
+This may be where the merit of the change itself or the implementation strategy is contested by a team member.
- As a general guideline, the order of items is determined as follows:
+As a general guideline, the order of items is determined as follows:
- - Prioritise pull requests over issues
+- Prioritise pull requests over issues
- Contributors who took the time to implement concrete change proposals should not wait indefinitely.
+ Contributors who took the time to implement concrete change proposals should not wait indefinitely.
- - Prioritise fixing bugs and testing over documentation, improvements or new features
+- Prioritise fixing bugs and testing over documentation, improvements or new features
- The team values stability and accessibility higher than raw functionality.
+ The team values stability and accessibility higher than raw functionality.
- - Interleave issues and PRs
+- Interleave issues and PRs
- This way issues without attempts at a solution get a chance to get addressed.
+ This way issues without attempts at a solution get a chance to get addressed.
-- In review
+### In review
- Pull requests in this column are reviewed together during work meetings.
- This is both for spreading implementation knowledge and for establishing common values in code reviews.
+Pull requests in this column are reviewed together during work meetings.
+This is both for spreading implementation knowledge and for establishing common values in code reviews.
- When the overall direction is agreed upon, even when further changes are required, the pull request is assigned to one team member.
+When the overall direction is agreed upon, even when further changes are required, the pull request is assigned to one team member.
-- Assigned for merging
+### Assigned
- One team member is assigned to each of these pull requests.
- They will communicate with the authors, and make the final approval once all remaining issues are addressed.
+One team member is assigned to each of these pull requests.
+They will communicate with the authors, and make the final approval once all remaining issues are addressed.
- If more substantive issues arise, the assignee can move the pull request back to _To discuss_ to involve the team again.
+If more substantive issues arise, the assignee can move the pull request back to [To discuss](#to-discuss) or [In review](#in-review) to involve the team again.
+
+### Flowchart
The process is illustrated in the following diagram:
diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs
index de91dc28d..41ecbbeb4 100644
--- a/perl/lib/Nix/Store.xs
+++ b/perl/lib/Nix/Store.xs
@@ -27,8 +27,6 @@ static ref store()
if (!_store) {
try {
initLibStore();
- loadConfFile();
- settings.lockCPU = false;
_store = openStore();
} catch (Error & e) {
croak("%s", e.what());
@@ -295,7 +293,13 @@ SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
try {
auto h = Hash::parseAny(hash, parseHashType(algo));
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
- auto path = store()->makeFixedOutputPath(method, h, name);
+ auto path = store()->makeFixedOutputPath(name, FixedOutputInfo {
+ .hash = {
+ .method = method,
+ .hash = h,
+ },
+ .references = {},
+ });
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(path).c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
diff --git a/scripts/install-systemd-multi-user.sh b/scripts/install-systemd-multi-user.sh
index 7dd567747..07b34033a 100755
--- a/scripts/install-systemd-multi-user.sh
+++ b/scripts/install-systemd-multi-user.sh
@@ -92,7 +92,7 @@ poly_configure_nix_daemon_service() {
task "Setting up the nix-daemon systemd service"
_sudo "to create the nix-daemon tmpfiles config" \
- ln -sfn /nix/var/nix/profiles/default/$TMPFILES_SRC $TMPFILES_DEST
+ ln -sfn "/nix/var/nix/profiles/default$TMPFILES_SRC" "$TMPFILES_DEST"
_sudo "to run systemd-tmpfiles once to pick that path up" \
systemd-tmpfiles --create --prefix=/nix/var/nix
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc
index cfc4baaca..ce9c7f45a 100644
--- a/src/build-remote/build-remote.cc
+++ b/src/build-remote/build-remote.cc
@@ -311,8 +311,9 @@ connected:
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
if (!store->queryRealisation(thisOutputId)) {
debug("missing output %s", outputName);
- assert(result.builtOutputs.count(thisOutputId));
- auto newRealisation = result.builtOutputs.at(thisOutputId);
+ auto i = result.builtOutputs.find(outputName);
+ assert(i != result.builtOutputs.end());
+ auto & newRealisation = i->second;
missingRealisations.insert(newRealisation);
missingPaths.insert(newRealisation.outPath);
}
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc
index bedf11e2c..6c4648b34 100644
--- a/src/libcmd/command.cc
+++ b/src/libcmd/command.cc
@@ -121,6 +121,8 @@ ref EvalCommand::getEvalState()
#endif
;
+ evalState->repair = repair;
+
if (startReplOnEvalErrors) {
evalState->debugRepl = &AbstractNixRepl::runSimple;
};
diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc
index 5b6477c82..ff3abd534 100644
--- a/src/libcmd/common-eval-args.cc
+++ b/src/libcmd/common-eval-args.cc
@@ -153,7 +153,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
for (auto & i : autoArgs) {
auto v = state.allocValue();
if (i.second[0] == 'E')
- state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), absPath(".")));
+ state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), state.rootPath(CanonPath::fromCwd())));
else
v->mkString(((std::string_view) i.second).substr(1));
res.insert(state.symbols.create(i.first), v);
@@ -161,19 +161,19 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
return res.finish();
}
-Path lookupFileArg(EvalState & state, std::string_view s)
+SourcePath lookupFileArg(EvalState & state, std::string_view s)
{
if (EvalSettings::isPseudoUrl(s)) {
auto storePath = fetchers::downloadTarball(
state.store, EvalSettings::resolvePseudoUrl(s), "source", false).first.storePath;
- return state.store->toRealPath(storePath);
+ return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
}
else if (hasPrefix(s, "flake:")) {
experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false);
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;
- return state.store->toRealPath(storePath);
+ return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
}
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
@@ -182,7 +182,7 @@ Path lookupFileArg(EvalState & state, std::string_view s)
}
else
- return absPath(std::string(s));
+ return state.rootPath(CanonPath::fromCwd(s));
}
}
diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh
index b69db11dd..b65cb5b20 100644
--- a/src/libcmd/common-eval-args.hh
+++ b/src/libcmd/common-eval-args.hh
@@ -2,14 +2,16 @@
///@file
#include "args.hh"
+#include "common-args.hh"
namespace nix {
class Store;
class EvalState;
class Bindings;
+struct SourcePath;
-struct MixEvalArgs : virtual Args
+struct MixEvalArgs : virtual Args, virtual MixRepair
{
static constexpr auto category = "Common evaluation options";
@@ -25,6 +27,6 @@ private:
std::map autoArgs;
};
-Path lookupFileArg(EvalState & state, std::string_view s);
+SourcePath lookupFileArg(EvalState & state, std::string_view s);
}
diff --git a/src/libcmd/editor-for.cc b/src/libcmd/editor-for.cc
index f674f32bd..a17c6f12a 100644
--- a/src/libcmd/editor-for.cc
+++ b/src/libcmd/editor-for.cc
@@ -3,8 +3,11 @@
namespace nix {
-Strings editorFor(const Path & file, uint32_t line)
+Strings editorFor(const SourcePath & file, uint32_t line)
{
+ auto path = file.getPhysicalPath();
+ if (!path)
+ throw Error("cannot open '%s' in an editor because it has no physical path", file);
auto editor = getEnv("EDITOR").value_or("cat");
auto args = tokenizeString(editor);
if (line > 0 && (
@@ -13,7 +16,7 @@ Strings editorFor(const Path & file, uint32_t line)
editor.find("vim") != std::string::npos ||
editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", line));
- args.push_back(file);
+ args.push_back(path->abs());
return args;
}
diff --git a/src/libcmd/editor-for.hh b/src/libcmd/editor-for.hh
index c8c4e9d9b..fbf4307c9 100644
--- a/src/libcmd/editor-for.hh
+++ b/src/libcmd/editor-for.hh
@@ -2,6 +2,7 @@
///@file
#include "types.hh"
+#include "input-accessor.hh"
namespace nix {
@@ -9,6 +10,6 @@ namespace nix {
* Helper function to generate args that invoke $EDITOR on
* filename:lineno.
*/
-Strings editorFor(const Path & file, uint32_t line);
+Strings editorFor(const SourcePath & file, uint32_t line);
}
diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc
index a3352af76..f0d322e6d 100644
--- a/src/libcmd/installable-flake.cc
+++ b/src/libcmd/installable-flake.cc
@@ -96,8 +96,7 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
auto v = attr->forceValue();
if (v.type() == nPath) {
- PathSet context;
- auto storePath = state->copyPathToStore(context, Path(v.path));
+ auto storePath = v.path().fetchToStore(state->store);
return {{
.path = DerivedPath::Opaque {
.path = std::move(storePath),
@@ -107,10 +106,10 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
}
else if (v.type() == nString) {
- PathSet context;
+ NixStringContext context;
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
auto storePath = state->store->maybeParseStorePath(s);
- if (storePath && context.count(std::string(s))) {
+ if (storePath && context.count(NixStringContextElem::Opaque { .path = *storePath })) {
return {{
.path = DerivedPath::Opaque {
.path = std::move(*storePath),
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 32ae46d9f..a2b882355 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -449,7 +449,7 @@ Installables SourceExprCommand::parseInstallables(
else if (file)
state->evalFile(lookupFileArg(*state, *file), *vFile);
else {
- auto e = state->parseExprFromString(*expr, absPath("."));
+ auto e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
state->eval(e, *vFile);
}
@@ -593,8 +593,8 @@ std::vector, BuiltPathWithResult>> Installable::build
std::visit(overloaded {
[&](const DerivedPath::Built & bfd) {
std::map outputs;
- for (auto & path : buildResult.builtOutputs)
- outputs.emplace(path.first.outputName, path.second.outPath);
+ for (auto & [outputName, realisation] : buildResult.builtOutputs)
+ outputs.emplace(outputName, realisation.outPath);
res.push_back({aux.installable, {
.path = BuiltPath::Built { bfd.drvPath, outputs },
.info = aux.info,
diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc
index 57848a5d3..4b160a100 100644
--- a/src/libcmd/repl.cc
+++ b/src/libcmd/repl.cc
@@ -40,6 +40,7 @@ extern "C" {
#include "markdown.hh"
#include "local-fs-store.hh"
#include "progress-bar.hh"
+#include "print.hh"
#if HAVE_BOEHMGC
#define GC_INCLUDE_NEW
@@ -54,8 +55,6 @@ struct NixRepl
, gc
#endif
{
- std::string curDir;
-
size_t debugTraceIndex;
Strings loadedFiles;
@@ -113,7 +112,6 @@ NixRepl::NixRepl(const Strings & searchPath, nix::ref store, refstaticBaseEnv.get()))
, historyFile(getDataDir() + "/nix/repl-history")
{
- curDir = absPath(".");
}
@@ -425,6 +423,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
}
+// FIXME: DRY and match or use the parser
static bool isVarName(std::string_view s)
{
if (s.size() == 0) return false;
@@ -592,14 +591,14 @@ bool NixRepl::processLine(std::string line)
Value v;
evalString(arg, v);
- const auto [path, line] = [&] () -> std::pair {
+ const auto [path, line] = [&] () -> std::pair {
if (v.type() == nPath || v.type() == nString) {
- PathSet context;
+ NixStringContext context;
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
return {path, 0};
} else if (v.isLambda()) {
auto pos = state->positions[v.lambda.fun->pos];
- if (auto path = std::get_if(&pos.origin))
+ if (auto path = std::get_if(&pos.origin))
return {*path, pos.line};
else
throw Error("'%s' cannot be shown in an editor", pos);
@@ -874,8 +873,7 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
Expr * NixRepl::parseString(std::string s)
{
- Expr * e = state->parseExprFromString(std::move(s), curDir, staticEnv);
- return e;
+ return state->parseExprFromString(std::move(s), state->rootPath(CanonPath::fromCwd()), staticEnv);
}
@@ -894,17 +892,6 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
}
-std::ostream & printStringValue(std::ostream & str, const char * string) {
- str << "\"";
- for (const char * i = string; *i; i++)
- if (*i == '\"' || *i == '\\') str << "\\" << *i;
- else if (*i == '\n') str << "\\n";
- else if (*i == '\r') str << "\\r";
- else if (*i == '\t') str << "\\t";
- else str << *i;
- str << "\"";
- return str;
-}
// FIXME: lot of cut&paste from Nix's eval.cc.
@@ -922,17 +909,19 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
break;
case nBool:
- str << ANSI_CYAN << (v.boolean ? "true" : "false") << ANSI_NORMAL;
+ str << ANSI_CYAN;
+ printLiteralBool(str, v.boolean);
+ str << ANSI_NORMAL;
break;
case nString:
str << ANSI_WARNING;
- printStringValue(str, v.string.s);
+ printLiteralString(str, v.string.s);
str << ANSI_NORMAL;
break;
case nPath:
- str << ANSI_GREEN << v.path << ANSI_NORMAL; // !!! escaping?
+ str << ANSI_GREEN << v.path().to_string() << ANSI_NORMAL; // !!! escaping?
break;
case nNull:
@@ -947,7 +936,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
if (isDrv) {
str << "«derivation ";
Bindings::iterator i = v.attrs->find(state->sDrvPath);
- PathSet context;
+ NixStringContext context;
if (i != v.attrs->end())
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
else
@@ -964,10 +953,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
sorted.emplace(state->symbols[i.name], i.value);
for (auto & i : sorted) {
- if (isVarName(i.first))
- str << i.first;
- else
- printStringValue(str, i.first.c_str());
+ printAttributeName(str, i.first);
str << " = ";
if (seen.count(i.second))
str << "«repeated»";
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index 7c0705091..ab654c1b0 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -106,7 +106,7 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin
}
-std::pair findPackageFilename(EvalState & state, Value & v, std::string what)
+std::pair findPackageFilename(EvalState & state, Value & v, std::string what)
{
Value * v2;
try {
@@ -118,21 +118,25 @@ std::pair findPackageFilename(EvalState & state, Value &
// FIXME: is it possible to extract the Pos object instead of doing this
// toString + parsing?
- auto pos = state.forceString(*v2, noPos, "while evaluating the 'meta.position' attribute of a derivation");
+ NixStringContext context;
+ auto path = state.coerceToPath(noPos, *v2, context, "while evaluating the 'meta.position' attribute of a derivation");
- auto colon = pos.rfind(':');
- if (colon == std::string::npos)
- throw ParseError("cannot parse meta.position attribute '%s'", pos);
+ auto fn = path.path.abs();
+
+ auto fail = [fn]() {
+ throw ParseError("cannot parse 'meta.position' attribute '%s'", fn);
+ };
- std::string filename(pos, 0, colon);
- unsigned int lineno;
try {
- lineno = std::stoi(std::string(pos, colon + 1, std::string::npos));
+ auto colon = fn.rfind(':');
+ if (colon == std::string::npos) fail();
+ std::string filename(fn, 0, colon);
+ auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
+ return {CanonPath(fn.substr(0, colon)), lineno};
} catch (std::invalid_argument & e) {
- throw ParseError("cannot parse line number '%s'", pos);
+ fail();
+ abort();
}
-
- return { std::move(filename), lineno };
}
diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh
index b2bfb5d04..eb00ffb93 100644
--- a/src/libexpr/attr-path.hh
+++ b/src/libexpr/attr-path.hh
@@ -20,7 +20,7 @@ std::pair findAlongAttrPath(
/**
* Heuristic to find the filename and lineno or a nix value.
*/
-std::pair findPackageFilename(EvalState & state, Value & v, std::string what);
+std::pair findPackageFilename(EvalState & state, Value & v, std::string what);
std::vector parseAttrPath(EvalState & state, std::string_view s);
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 1219b2471..9e734e654 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -47,7 +47,7 @@ struct AttrDb
{
auto state(_state->lock());
- Path cacheDir = getCacheDir() + "/nix/eval-cache-v4";
+ Path cacheDir = getCacheDir() + "/nix/eval-cache-v5";
createDirs(cacheDir);
Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite";
@@ -300,7 +300,7 @@ struct AttrDb
NixStringContext context;
if (!queryAttribute.isNull(3))
for (auto & s : tokenizeString>(queryAttribute.getStr(3), ";"))
- context.push_back(NixStringContextElem::parse(cfg, s));
+ context.insert(NixStringContextElem::parse(s));
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
}
case AttrType::Bool:
@@ -442,8 +442,10 @@ Value & AttrCursor::forceValue()
if (v.type() == nString)
cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context),
string_t{v.string.s, {}}};
- else if (v.type() == nPath)
- cachedValue = {root->db->setString(getKey(), v.path), string_t{v.path, {}}};
+ else if (v.type() == nPath) {
+ auto path = v.path().path;
+ cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
+ }
else if (v.type() == nBool)
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
else if (v.type() == nInt)
@@ -580,7 +582,7 @@ std::string AttrCursor::getString()
if (v.type() != nString && v.type() != nPath)
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow();
- return v.type() == nString ? v.string.s : v.path;
+ return v.type() == nString ? v.string.s : v.path().to_string();
}
string_t AttrCursor::getStringWithContext()
@@ -619,10 +621,13 @@ string_t AttrCursor::getStringWithContext()
auto & v = forceValue();
- if (v.type() == nString)
- return {v.string.s, v.getContext(*root->state.store)};
+ if (v.type() == nString) {
+ NixStringContext context;
+ copyContext(v, context);
+ return {v.string.s, std::move(context)};
+ }
else if (v.type() == nPath)
- return {v.path, {}};
+ return {v.path().to_string(), {}};
else
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow();
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 18cfd9531..e2b455b91 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -9,6 +9,7 @@
#include "filetransfer.hh"
#include "function-trace.hh"
#include "profiles.hh"
+#include "print.hh"
#include
#include
@@ -104,21 +105,13 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
str << integer;
break;
case tBool:
- str << (boolean ? "true" : "false");
+ printLiteralBool(str, boolean);
break;
case tString:
- str << "\"";
- for (const char * i = string.s; *i; i++)
- if (*i == '\"' || *i == '\\') str << "\\" << *i;
- else if (*i == '\n') str << "\\n";
- else if (*i == '\r') str << "\\r";
- else if (*i == '\t') str << "\\t";
- else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
- else str << *i;
- str << "\"";
+ printLiteralString(str, string.s);
break;
case tPath:
- str << path; // !!! escaping?
+ str << path().to_string(); // !!! escaping?
break;
case tNull:
str << "null";
@@ -542,6 +535,7 @@ EvalState::EvalState(
, sOutputSpecified(symbols.create("outputSpecified"))
, repair(NoRepair)
, emptyBindings(0)
+ , derivationInternal(rootPath(CanonPath("/builtin/derivation.nix")))
, store(store)
, buildStore(buildStore ? buildStore : store)
, debugRepl(nullptr)
@@ -616,15 +610,14 @@ void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value &
{
allowPath(storePath);
- auto path = store->printStorePath(storePath);
- v.mkString(path, PathSet({path}));
+ mkStorePathString(storePath, v);
}
-Path EvalState::checkSourcePath(const Path & path_)
+SourcePath EvalState::checkSourcePath(const SourcePath & path_)
{
if (!allowedPaths) return path_;
- auto i = resolvedPaths.find(path_);
+ auto i = resolvedPaths.find(path_.path.abs());
if (i != resolvedPaths.end())
return i->second;
@@ -634,9 +627,9 @@ Path EvalState::checkSourcePath(const Path & path_)
* attacker can't append ../../... to a path that would be in allowedPaths
* and thus leak symlink targets.
*/
- Path abspath = canonPath(path_);
+ Path abspath = canonPath(path_.path.abs());
- if (hasPrefix(abspath, corepkgsPrefix)) return abspath;
+ if (hasPrefix(abspath, corepkgsPrefix)) return CanonPath(abspath);
for (auto & i : *allowedPaths) {
if (isDirOrInDir(abspath, i)) {
@@ -654,11 +647,11 @@ Path EvalState::checkSourcePath(const Path & path_)
/* Resolve symlinks. */
debug("checking access to '%s'", abspath);
- Path path = canonPath(abspath, true);
+ SourcePath path = CanonPath(canonPath(abspath, true));
for (auto & i : *allowedPaths) {
- if (isDirOrInDir(path, i)) {
- resolvedPaths[path_] = path;
+ if (isDirOrInDir(path.path.abs(), i)) {
+ resolvedPaths.insert_or_assign(path_.path.abs(), path);
return path;
}
}
@@ -686,12 +679,12 @@ void EvalState::checkURI(const std::string & uri)
/* If the URI is a path, then check it against allowedPaths as
well. */
if (hasPrefix(uri, "/")) {
- checkSourcePath(uri);
+ checkSourcePath(CanonPath(uri));
return;
}
if (hasPrefix(uri, "file://")) {
- checkSourcePath(std::string(uri, 7));
+ checkSourcePath(CanonPath(std::string(uri, 7)));
return;
}
@@ -699,7 +692,7 @@ void EvalState::checkURI(const std::string & uri)
}
-Path EvalState::toRealPath(const Path & path, const PathSet & context)
+Path EvalState::toRealPath(const Path & path, const NixStringContext & context)
{
// FIXME: check whether 'path' is in 'context'.
return
@@ -951,34 +944,34 @@ void Value::mkString(std::string_view s)
}
-static void copyContextToValue(Value & v, const PathSet & context)
+static void copyContextToValue(Value & v, const NixStringContext & context)
{
if (!context.empty()) {
size_t n = 0;
v.string.context = (const char * *)
allocBytes((context.size() + 1) * sizeof(char *));
for (auto & i : context)
- v.string.context[n++] = dupString(i.c_str());
+ v.string.context[n++] = dupString(i.to_string().c_str());
v.string.context[n] = 0;
}
}
-void Value::mkString(std::string_view s, const PathSet & context)
+void Value::mkString(std::string_view s, const NixStringContext & context)
{
mkString(s);
copyContextToValue(*this, context);
}
-void Value::mkStringMove(const char * s, const PathSet & context)
+void Value::mkStringMove(const char * s, const NixStringContext & context)
{
mkString(s);
copyContextToValue(*this, context);
}
-void Value::mkPath(std::string_view s)
+void Value::mkPath(const SourcePath & path)
{
- mkPath(makeImmutableString(s));
+ mkPath(makeImmutableString(path.path.abs()));
}
@@ -1034,9 +1027,9 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
void EvalState::mkPos(Value & v, PosIdx p)
{
auto pos = positions[p];
- if (auto path = std::get_if(&pos.origin)) {
+ if (auto path = std::get_if(&pos.origin)) {
auto attrs = buildBindings(3);
- attrs.alloc(sFile).mkString(*path);
+ attrs.alloc(sFile).mkString(path->path.abs());
attrs.alloc(sLine).mkInt(pos.line);
attrs.alloc(sColumn).mkInt(pos.column);
v.mkAttrs(attrs);
@@ -1045,6 +1038,16 @@ void EvalState::mkPos(Value & v, PosIdx p)
}
+void EvalState::mkStorePathString(const StorePath & p, Value & v)
+{
+ v.mkString(
+ store->printStorePath(p),
+ NixStringContext {
+ NixStringContextElem::Opaque { .path = p },
+ });
+}
+
+
/* Create a thunk for the delayed computation of the given expression
in the given environment. But if the expression is a variable,
then look it up right away. This significantly reduces the number
@@ -1092,7 +1095,7 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
}
-void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial)
+void EvalState::evalFile(const SourcePath & path_, Value & v, bool mustBeTrivial)
{
auto path = checkSourcePath(path_);
@@ -1102,7 +1105,7 @@ void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial)
return;
}
- Path resolvedPath = resolveExprPath(path);
+ auto resolvedPath = resolveExprPath(path);
if ((i = fileEvalCache.find(resolvedPath)) != fileEvalCache.end()) {
v = i->second;
return;
@@ -1130,8 +1133,8 @@ void EvalState::resetFileCache()
void EvalState::cacheFile(
- const Path & path,
- const Path & resolvedPath,
+ const SourcePath & path,
+ const SourcePath & resolvedPath,
Expr * e,
Value & v,
bool mustBeTrivial)
@@ -1145,7 +1148,7 @@ void EvalState::cacheFile(
*e,
this->baseEnv,
e->getPos() ? static_cast>(positions[e->getPos()]) : nullptr,
- "while evaluating the file '%1%':", resolvedPath)
+ "while evaluating the file '%1%':", resolvedPath.to_string())
: nullptr;
// Enforce that 'flake.nix' is a direct attrset, not a
@@ -1155,7 +1158,7 @@ void EvalState::cacheFile(
error("file '%s' must be an attribute set", path).debugThrow();
eval(e, v);
} catch (Error & e) {
- addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath);
+ addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath.to_string());
throw;
}
@@ -1416,8 +1419,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} catch (Error & e) {
if (pos2) {
auto pos2r = state.positions[pos2];
- auto origin = std::get_if(&pos2r.origin);
- if (!(origin && *origin == state.derivationNixPath))
+ auto origin = std::get_if(&pos2r.origin);
+ if (!(origin && *origin == state.derivationInternal))
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
showAttrPath(state, env, attrPath));
}
@@ -1907,7 +1910,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{
- PathSet context;
+ NixStringContext context;
std::vector s;
size_t sSize = 0;
NixInt n = 0;
@@ -1990,7 +1993,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
else if (firstType == nPath) {
if (!context.empty())
state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow();
- v.mkPath(canonPath(str()));
+ v.mkPath(CanonPath(canonPath(str())));
} else
v.mkStringMove(c_str(), context);
}
@@ -2116,26 +2119,15 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
}
-void copyContext(const Value & v, PathSet & context)
+void copyContext(const Value & v, NixStringContext & context)
{
if (v.string.context)
for (const char * * p = v.string.context; *p; ++p)
- context.insert(*p);
+ context.insert(NixStringContextElem::parse(*p));
}
-NixStringContext Value::getContext(const Store & store)
-{
- NixStringContext res;
- assert(internalType == tString);
- if (string.context)
- for (const char * * p = string.context; *p; ++p)
- res.push_back(NixStringContextElem::parse(store, *p));
- return res;
-}
-
-
-std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx)
+std::string_view EvalState::forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx)
{
auto s = forceString(v, pos, errorCtx);
copyContext(v, context);
@@ -2165,7 +2157,7 @@ bool EvalState::isDerivation(Value & v)
std::optional EvalState::tryAttrsToString(const PosIdx pos, Value & v,
- PathSet & context, bool coerceMore, bool copyToStore)
+ NixStringContext & context, bool coerceMore, bool copyToStore)
{
auto i = v.attrs->find(sToString);
if (i != v.attrs->end()) {
@@ -2179,8 +2171,14 @@ std::optional EvalState::tryAttrsToString(const PosIdx pos, Value &
return {};
}
-BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &context,
- std::string_view errorCtx, bool coerceMore, bool copyToStore, bool canonicalizePath)
+BackedStringView EvalState::coerceToString(
+ const PosIdx pos,
+ Value & v,
+ NixStringContext & context,
+ std::string_view errorCtx,
+ bool coerceMore,
+ bool copyToStore,
+ bool canonicalizePath)
{
forceValue(v, pos);
@@ -2190,12 +2188,14 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &
}
if (v.type() == nPath) {
- BackedStringView path(PathView(v.path));
- if (canonicalizePath)
- path = canonPath(*path);
- if (copyToStore)
- path = store->printStorePath(copyPathToStore(context, std::move(path).toOwned()));
- return path;
+ return
+ !canonicalizePath && !copyToStore
+ ? // FIXME: hack to preserve path literals that end in a
+ // slash, as in /foo/${x}.
+ v._path
+ : copyToStore
+ ? store->printStorePath(copyPathToStore(context, v.path()))
+ : std::string(v.path().path.abs());
}
if (v.type() == nAttrs) {
@@ -2256,40 +2256,40 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &
}
-StorePath EvalState::copyPathToStore(PathSet & context, const Path & path)
+StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePath & path)
{
- if (nix::isDerivation(path))
+ if (nix::isDerivation(path.path.abs()))
error("file names are not allowed to end in '%1%'", drvExtension).debugThrow();
- auto dstPath = [&]() -> StorePath
- {
- auto i = srcToStore.find(path);
- if (i != srcToStore.end()) return i->second;
+ auto i = srcToStore.find(path);
- auto dstPath = settings.readOnlyMode
- ? store->computeStorePathForPath(std::string(baseNameOf(path)), checkSourcePath(path)).first
- : store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair);
- allowPath(dstPath);
- srcToStore.insert_or_assign(path, dstPath);
- printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath));
- return dstPath;
- }();
+ auto dstPath = i != srcToStore.end()
+ ? i->second
+ : [&]() {
+ auto dstPath = path.fetchToStore(store, path.baseName(), nullptr, repair);
+ allowPath(dstPath);
+ srcToStore.insert_or_assign(path, dstPath);
+ printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath));
+ return dstPath;
+ }();
- context.insert(store->printStorePath(dstPath));
+ context.insert(NixStringContextElem::Opaque {
+ .path = dstPath
+ });
return dstPath;
}
-Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
+SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx)
{
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
if (path == "" || path[0] != '/')
error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow();
- return path;
+ return CanonPath(path);
}
-StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
+StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx)
{
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
if (auto storePath = store->maybeParseStorePath(path))
@@ -2328,7 +2328,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
return strcmp(v1.string.s, v2.string.s) == 0;
case nPath:
- return strcmp(v1.path, v2.path) == 0;
+ return strcmp(v1._path, v2._path) == 0;
case nNull:
return true;
@@ -2455,8 +2455,8 @@ void EvalState::printStats()
else
obj["name"] = nullptr;
if (auto pos = positions[fun->pos]) {
- if (auto path = std::get_if(&pos.origin))
- obj["file"] = *path;
+ if (auto path = std::get_if(&pos.origin))
+ obj["file"] = path->to_string();
obj["line"] = pos.line;
obj["column"] = pos.column;
}
@@ -2470,8 +2470,8 @@ void EvalState::printStats()
for (auto & i : attrSelects) {
json obj = json::object();
if (auto pos = positions[i.first]) {
- if (auto path = std::get_if(&pos.origin))
- obj["file"] = *path;
+ if (auto path = std::get_if(&pos.origin))
+ obj["file"] = path->to_string();
obj["line"] = pos.line;
obj["column"] = pos.column;
}
@@ -2496,7 +2496,7 @@ void EvalState::printStats()
}
-std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
+std::string ExternalValueBase::coerceToString(const Pos & pos, NixStringContext & context, bool copyMore, bool copyToStore) const
{
throw TypeError({
.msg = hintfmt("cannot coerce %1% to a string", showType())
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index b3b985683..bb3ac2b22 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -8,6 +8,7 @@
#include "symbol-table.hh"
#include "config.hh"
#include "experimental-features.hh"
+#include "input-accessor.hh"
#include