diff --git a/.gitignore b/.gitignore
index 4b290425a..58e7377fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,6 +90,7 @@ perl/Makefile.config
/misc/systemd/nix-daemon.service
/misc/systemd/nix-daemon.socket
+/misc/systemd/nix-daemon.conf
/misc/upstart/nix-daemon.conf
/src/resolve-system-dependencies/resolve-system-dependencies
diff --git a/.version b/.version
index 9aa34646d..6533b6687 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-2.7.0
\ No newline at end of file
+2.8.0
\ No newline at end of file
diff --git a/doc/manual/generate-builtins.nix b/doc/manual/generate-builtins.nix
index 92c7b1a31..6c8b88da2 100644
--- a/doc/manual/generate-builtins.nix
+++ b/doc/manual/generate-builtins.nix
@@ -6,9 +6,9 @@ builtins:
concatStrings (map
(name:
let builtin = builtins.${name}; in
- "
${name} "
+ "${name} "
+ concatStringsSep " " (map (s: "${s}") builtin.args)
- + "
"
+ + "
"
+ "\n\n"
+ builtin.doc
+ "\n\n"
diff --git a/doc/manual/generate-options.nix b/doc/manual/generate-options.nix
index 9a77f4d36..2d586fa1b 100644
--- a/doc/manual/generate-options.nix
+++ b/doc/manual/generate-options.nix
@@ -6,7 +6,8 @@ options:
concatStrings (map
(name:
let option = options.${name}; in
- " - `${name}` \n\n"
+ " - [`${name}`](#conf-${name})"
+ + "\n\n"
+ concatStrings (map (s: " ${s}\n") (splitLines option.description)) + "\n\n"
+ (if option.documentDefault
then " **Default:** " + (
@@ -20,7 +21,7 @@ concatStrings (map
# JSON, but that converts to "{ }" here.
(if isAttrs option.value then "`\"\"`"
else "`" + toString option.value + "`")) + "\n\n"
- else " **Default:** *machine-specific*")
+ else " **Default:** *machine-specific*\n")
+ (if option.aliases != []
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"
else "")
diff --git a/doc/manual/local.mk b/doc/manual/local.mk
index 6b232a736..c1ce8aaeb 100644
--- a/doc/manual/local.mk
+++ b/doc/manual/local.mk
@@ -72,6 +72,7 @@ $(d)/builtins.json: $(bindir)/nix
@mv $@.tmp $@
# Generate the HTML manual.
+html: $(docdir)/manual/index.html
install: $(docdir)/manual/index.html
# Generate 'nix' manpages.
diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in
index 4e2afa20e..f0f9457d2 100644
--- a/doc/manual/src/SUMMARY.md.in
+++ b/doc/manual/src/SUMMARY.md.in
@@ -72,6 +72,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.7 (2022-03-07)](release-notes/rl-2.7.md)
- [Release 2.6 (2022-01-24)](release-notes/rl-2.6.md)
- [Release 2.5 (2021-12-13)](release-notes/rl-2.5.md)
- [Release 2.4 (2021-11-01)](release-notes/rl-2.4.md)
diff --git a/doc/manual/src/advanced-topics/distributed-builds.md b/doc/manual/src/advanced-topics/distributed-builds.md
index c4c60db15..b0d7fbf1a 100644
--- a/doc/manual/src/advanced-topics/distributed-builds.md
+++ b/doc/manual/src/advanced-topics/distributed-builds.md
@@ -110,7 +110,7 @@ default, set it to `-`.
7. A comma-separated list of *mandatory features*. A machine will only
be used to build a derivation if all of the machine’s mandatory
features appear in the derivation’s `requiredSystemFeatures`
- attribute..
+ attribute.
8. The (base64-encoded) public host key of the remote machine. If omitted, SSH
will use its regular known-hosts file. Specifically, the field is calculated
diff --git a/doc/manual/src/command-ref/nix-store.md b/doc/manual/src/command-ref/nix-store.md
index 26292f1bb..7db9f0c1c 100644
--- a/doc/manual/src/command-ref/nix-store.md
+++ b/doc/manual/src/command-ref/nix-store.md
@@ -321,8 +321,8 @@ symlink.
This query has one option:
- `--include-outputs`
- Also include the output path of store derivations, and their
- closures.
+ Also include the existing output paths of store derivations,
+ and their closures.
This query can be used to implement various kinds of deployment. A
*source deployment* is obtained by distributing the closure of a
diff --git a/doc/manual/src/installation/installing-binary.md b/doc/manual/src/installation/installing-binary.md
index 4367654a2..e5fb50088 100644
--- a/doc/manual/src/installation/installing-binary.md
+++ b/doc/manual/src/installation/installing-binary.md
@@ -84,7 +84,9 @@ 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`.
-You can uninstall Nix with the following commands:
+## Uninstalling
+
+### Linux
```console
sudo rm -rf /etc/profile/nix.sh /etc/nix /nix ~root/.nix-profile ~root/.nix-defexpr ~root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
@@ -95,15 +97,95 @@ sudo systemctl stop nix-daemon.service
sudo systemctl disable nix-daemon.socket
sudo systemctl disable nix-daemon.service
sudo systemctl daemon-reload
-
-# If you are on macOS, you will need to run:
-sudo launchctl unload /Library/LaunchDaemons/org.nixos.nix-daemon.plist
-sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
```
There may also be references to Nix in `/etc/profile`, `/etc/bashrc`,
and `/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 this,
+ `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.
+
+> **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
diff --git a/doc/manual/src/release-notes/rl-2.7.md b/doc/manual/src/release-notes/rl-2.7.md
new file mode 100644
index 000000000..2f3879422
--- /dev/null
+++ b/doc/manual/src/release-notes/rl-2.7.md
@@ -0,0 +1,33 @@
+# Release 2.7 (2022-03-07)
+
+* Nix will now make some helpful suggestions when you mistype
+ something on the command line. For instance, if you type `nix build
+ nixpkgs#thunderbrd`, it will suggest `thunderbird`.
+
+* A number of "default" flake output attributes have been
+ renamed. These are:
+
+ * `defaultPackage.` → `packages..default`
+ * `defaultApps.` → `apps..default`
+ * `defaultTemplate` → `templates.default`
+ * `defaultBundler.` → `bundlers..default`
+ * `overlay` → `overlays.default`
+ * `devShell.` → `devShells..default`
+
+ The old flake output attributes still work, but `nix flake check`
+ will warn about them.
+
+* Breaking API change: `nix bundle` now supports bundlers of the form
+ `bundler..= derivation: another-derivation;`. This
+ supports additional functionality to inspect evaluation information
+ during bundling. A new
+ [repository](https://github.com/NixOS/bundlers) has various bundlers
+ implemented.
+
+* `nix store ping` now reports the version of the remote Nix daemon.
+
+* `nix flake {init,new}` now display information about which files have been
+ created.
+
+* Templates can now define a `welcomeText` attribute, which is printed out by
+ `nix flake {init,new} --template `.
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index ad8c27dbc..8c8c0fd41 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -1,9 +1,42 @@
# Release X.Y (202?-??-??)
-* `nix bundle` breaking API change now supports bundlers of the form
- `bundler..= derivation: another-derivation;`. This supports
- additional functionality to inspect evaluation information during bundling. A
- new [repository](https://github.com/NixOS/bundlers) has various bundlers
- implemented.
+* Various nix commands can now read expressions from stdin with `--file -`.
-* `nix store ping` now reports the version of the remote Nix daemon.
+* `nix store make-content-addressable` has been renamed to `nix store
+ make-content-addressed`.
+
+* New experimental builtin function `builtins.fetchClosure` that
+ copies a closure from a binary cache at evaluation time and rewrites
+ it to content-addressed form (if it isn't already). Like
+ `builtins.storePath`, this allows importing pre-built store paths;
+ the difference is that it doesn't require the user to configure
+ binary caches and trusted public keys.
+
+ This function is only available if you enable the experimental
+ feature `fetch-closure`.
+
+* New experimental feature: *impure derivations*. These are
+ derivations that can produce a different result every time they're
+ built. Here is an example:
+
+ ```nix
+ stdenv.mkDerivation {
+ name = "impure";
+ __impure = true; # marks this derivation as impure
+ buildCommand = "date > $out";
+ }
+ ```
+
+ Running `nix build` twice on this expression will build the
+ derivation twice, producing two different content-addressed store
+ paths. Like fixed-output derivations, impure derivations have access
+ to the network. Only fixed-output derivations and impure derivations
+ can depend on an impure derivation.
+
+* The `nixosModule` flake output attribute has been renamed consistent
+ with the `.default` renames in nix 2.7.
+
+ * `nixosModule` → `nixosModules.default`
+
+ As before, the old output will continue to work, but `nix flake check` will
+ issue a warning about it.
diff --git a/flake.lock b/flake.lock
index 61eccb73c..cd79fa85e 100644
--- a/flake.lock
+++ b/flake.lock
@@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1632864508,
- "narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=",
+ "lastModified": 1645296114,
+ "narHash": "sha256-y53N7TyIkXsjMpOG7RhvqJFGDacLs9HlyHeSTBioqYU=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "82891b5e2c2359d7e58d08849e4c89511ab94234",
+ "rev": "530a53dcbc9437363471167a5e4762c5fcfa34a1",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index 5b1e13a16..87b00edf4 100644
--- a/flake.nix
+++ b/flake.nix
@@ -501,6 +501,12 @@
inherit (self) overlay;
});
+ tests.sourcehutFlakes = (import ./tests/sourcehut-flakes.nix rec {
+ system = "x86_64-linux";
+ inherit nixpkgs;
+ inherit (self) overlay;
+ });
+
tests.setuid = nixpkgs.lib.genAttrs
["i686-linux" "x86_64-linux"]
(system:
@@ -648,11 +654,10 @@
installCheckFlags = "sysconfdir=$(out)/etc";
};
}) crossSystems)) // (builtins.listToAttrs (map (stdenvName:
- nixpkgsFor.${system}.lib.nameValuePair
- "nix-${stdenvName}"
- nixpkgsFor.${system}."${stdenvName}Packages".nix
- ) stdenvs))
- );
+ nixpkgsFor.${system}.lib.nameValuePair
+ "nix-${stdenvName}"
+ nixpkgsFor.${system}."${stdenvName}Packages".nix
+ ) stdenvs)));
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl
index 18ab33424..d3ef63db8 100755
--- a/maintainers/upload-release.pl
+++ b/maintainers/upload-release.pl
@@ -55,6 +55,11 @@ my $releaseDir = "nix/$releaseName";
my $tmpDir = "$TMPDIR/nix-release/$releaseName";
File::Path::make_path($tmpDir);
+my $narCache = "$TMPDIR/nar-cache";
+File::Path::make_path($narCache);
+
+my $binaryCache = "https://cache.nixos.org/?local-nar-cache=$narCache";
+
# S3 setup.
my $aws_access_key_id = $ENV{'AWS_ACCESS_KEY_ID'} or die "No AWS_ACCESS_KEY_ID given.";
my $aws_secret_access_key = $ENV{'AWS_SECRET_ACCESS_KEY'} or die "No AWS_SECRET_ACCESS_KEY given.";
@@ -80,6 +85,7 @@ sub downloadFile {
my ($jobName, $productNr, $dstName) = @_;
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
+ #print STDERR "$jobName: ", Dumper($buildInfo), "\n";
my $srcFile = $buildInfo->{buildproducts}->{$productNr}->{path} or die "job '$jobName' lacks product $productNr\n";
$dstName //= basename($srcFile);
@@ -87,19 +93,27 @@ sub downloadFile {
if (!-e $tmpFile) {
print STDERR "downloading $srcFile to $tmpFile...\n";
- system("NIX_REMOTE=https://cache.nixos.org/ nix store cat '$srcFile' > '$tmpFile'") == 0
+
+ my $fileInfo = decode_json(`NIX_REMOTE=$binaryCache nix store ls --json '$srcFile'`);
+
+ $srcFile = $fileInfo->{target} if $fileInfo->{type} eq 'symlink';
+
+ #print STDERR $srcFile, " ", Dumper($fileInfo), "\n";
+
+ system("NIX_REMOTE=$binaryCache nix store cat '$srcFile' > '$tmpFile'.tmp") == 0
or die "unable to fetch $srcFile\n";
+ rename("$tmpFile.tmp", $tmpFile) or die;
}
- my $sha256_expected = $buildInfo->{buildproducts}->{$productNr}->{sha256hash} or die;
+ my $sha256_expected = $buildInfo->{buildproducts}->{$productNr}->{sha256hash};
my $sha256_actual = `nix hash file --base16 --type sha256 '$tmpFile'`;
chomp $sha256_actual;
- if ($sha256_expected ne $sha256_actual) {
+ if (defined($sha256_expected) && $sha256_expected ne $sha256_actual) {
print STDERR "file $tmpFile is corrupt, got $sha256_actual, expected $sha256_expected\n";
exit 1;
}
- write_file("$tmpFile.sha256", $sha256_expected);
+ write_file("$tmpFile.sha256", $sha256_actual);
if (! -e "$tmpFile.asc") {
system("gpg2 --detach-sign --armor $tmpFile") == 0 or die "unable to sign $tmpFile\n";
@@ -117,6 +131,60 @@ downloadFile("binaryTarballCross.x86_64-linux.armv6l-linux", "1");
downloadFile("binaryTarballCross.x86_64-linux.armv7l-linux", "1");
downloadFile("installerScript", "1");
+# Upload docker images to dockerhub.
+my $dockerManifest = "";
+my $dockerManifestLatest = "";
+
+for my $platforms (["x86_64-linux", "amd64"], ["aarch64-linux", "arm64"]) {
+ my $system = $platforms->[0];
+ my $dockerPlatform = $platforms->[1];
+ my $fn = "nix-$version-docker-image-$dockerPlatform.tar.gz";
+ downloadFile("dockerImage.$system", "1", $fn);
+
+ print STDERR "loading docker image for $dockerPlatform...\n";
+ system("docker load -i $tmpDir/$fn") == 0 or die;
+
+ my $tag = "nixos/nix:$version-$dockerPlatform";
+ my $latestTag = "nixos/nix:latest-$dockerPlatform";
+
+ print STDERR "tagging $version docker image for $dockerPlatform...\n";
+ system("docker tag nix:$version $tag") == 0 or die;
+
+ if ($isLatest) {
+ print STDERR "tagging latest docker image for $dockerPlatform...\n";
+ system("docker tag nix:$version $latestTag") == 0 or die;
+ }
+
+ print STDERR "pushing $version docker image for $dockerPlatform...\n";
+ system("docker push -q $tag") == 0 or die;
+
+ if ($isLatest) {
+ print STDERR "pushing latest docker image for $dockerPlatform...\n";
+ system("docker push -q $latestTag") == 0 or die;
+ }
+
+ $dockerManifest .= " --amend $tag";
+ $dockerManifestLatest .= " --amend $latestTag"
+}
+
+print STDERR "creating multi-platform docker manifest...\n";
+system("docker manifest rm nixos/nix:$version");
+system("docker manifest create nixos/nix:$version $dockerManifest") == 0 or die;
+if ($isLatest) {
+ print STDERR "creating latest multi-platform docker manifest...\n";
+ system("docker manifest rm nixos/nix:latest");
+ system("docker manifest create nixos/nix:latest $dockerManifestLatest") == 0 or die;
+}
+
+print STDERR "pushing multi-platform docker manifest...\n";
+system("docker manifest push nixos/nix:$version") == 0 or die;
+
+if ($isLatest) {
+ print STDERR "pushing latest multi-platform docker manifest...\n";
+ system("docker manifest push nixos/nix:latest") == 0 or die;
+}
+
+# Upload release files to S3.
for my $fn (glob "$tmpDir/*") {
my $name = basename($fn);
my $dstKey = "$releaseDir/" . $name;
diff --git a/misc/bash/completion.sh b/misc/bash/completion.sh
index 8fc224792..045053dee 100644
--- a/misc/bash/completion.sh
+++ b/misc/bash/completion.sh
@@ -15,7 +15,7 @@ function _complete_nix {
else
COMPREPLY+=("$completion")
fi
- done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}")
+ done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}" 2>/dev/null)
__ltrim_colon_completions "$cur"
}
diff --git a/misc/systemd/local.mk b/misc/systemd/local.mk
index 1fa037485..76121a0f9 100644
--- a/misc/systemd/local.mk
+++ b/misc/systemd/local.mk
@@ -1,7 +1,8 @@
ifdef HOST_LINUX
$(foreach n, nix-daemon.socket nix-daemon.service, $(eval $(call install-file-in, $(d)/$(n), $(prefix)/lib/systemd/system, 0644)))
+ $(foreach n, nix-daemon.conf, $(eval $(call install-file-in, $(d)/$(n), $(prefix)/lib/tmpfiles.d, 0644)))
- clean-files += $(d)/nix-daemon.socket $(d)/nix-daemon.service
+ clean-files += $(d)/nix-daemon.socket $(d)/nix-daemon.service $(d)/nix-daemon.conf
endif
diff --git a/misc/systemd/nix-daemon.conf.in b/misc/systemd/nix-daemon.conf.in
new file mode 100644
index 000000000..e7b264234
--- /dev/null
+++ b/misc/systemd/nix-daemon.conf.in
@@ -0,0 +1 @@
+d @localstatedir@/nix/daemon-socket 0755 root root - -
diff --git a/misc/systemd/nix-daemon.service.in b/misc/systemd/nix-daemon.service.in
index 25655204d..24d894898 100644
--- a/misc/systemd/nix-daemon.service.in
+++ b/misc/systemd/nix-daemon.service.in
@@ -1,7 +1,9 @@
[Unit]
Description=Nix Daemon
+Documentation=man:nix-daemon https://nixos.org/manual
RequiresMountsFor=@storedir@
RequiresMountsFor=@localstatedir@
+RequiresMountsFor=@localstatedir@/nix/db
ConditionPathIsReadWrite=@localstatedir@/nix/daemon-socket
[Service]
diff --git a/misc/zsh/completion.zsh b/misc/zsh/completion.zsh
index a902e37dc..e702c721e 100644
--- a/misc/zsh/completion.zsh
+++ b/misc/zsh/completion.zsh
@@ -4,7 +4,7 @@ function _nix() {
local ifs_bk="$IFS"
local input=("${(Q)words[@]}")
IFS=$'\n'
- local res=($(NIX_GET_COMPLETIONS=$((CURRENT - 1)) "$input[@]"))
+ local res=($(NIX_GET_COMPLETIONS=$((CURRENT - 1)) "$input[@]" 2>/dev/null))
IFS="$ifs_bk"
local tpe="${${res[1]}%%> *}"
local -a suggestions
diff --git a/mk/run_test.sh b/mk/run_test.sh
index 3783d3bf7..7e95df2ac 100755
--- a/mk/run_test.sh
+++ b/mk/run_test.sh
@@ -14,9 +14,27 @@ if [ -t 1 ]; then
yellow="[33;1m"
normal="[m"
fi
-(cd tests && env ${TESTS_ENVIRONMENT} init.sh 2>/dev/null > /dev/null)
-log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)"
-status=$?
+
+run_test () {
+ (cd tests && env ${TESTS_ENVIRONMENT} init.sh 2>/dev/null > /dev/null)
+ log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)"
+ status=$?
+}
+
+run_test "$1"
+
+# Hack: Retry the test if it fails with “unexpected EOF reading a line” as these
+# appear randomly without anyone knowing why.
+# See https://github.com/NixOS/nix/issues/3605 for more info
+if [[ $status -ne 0 && $status -ne 99 && \
+ "$(uname)" == "Darwin" && \
+ "$log" =~ "unexpected EOF reading a line" \
+]]; then
+ echo "$post_run_msg [${yellow}FAIL$normal] (possibly flaky, so will be retried)"
+ echo "$log" | sed 's/^/ /'
+ run_test "$1"
+fi
+
if [ $status -eq 0 ]; then
echo "$post_run_msg [${green}PASS$normal]"
elif [ $status -eq 99 ]; then
diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs
index edbf12f7c..54ad1680c 100644
--- a/perl/lib/Nix/Store.xs
+++ b/perl/lib/Nix/Store.xs
@@ -240,7 +240,7 @@ SV * convertHash(char * algo, char * s, int toBase32)
PPCODE:
try {
auto h = Hash::parseAny(s, parseHashType(algo));
- string s = h.to_string(toBase32 ? Base32 : Base16, false);
+ auto s = h.to_string(toBase32 ? Base32 : Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
diff --git a/scripts/check-hydra-status.sh b/scripts/check-hydra-status.sh
index c1d2d7c40..e62705e94 100644
--- a/scripts/check-hydra-status.sh
+++ b/scripts/check-hydra-status.sh
@@ -14,14 +14,19 @@ curl -sS -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master
someBuildFailed=0
for buildId in $BUILDS_FOR_LATEST_EVAL; do
- buildInfo=$(curl -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
+ buildInfo=$(curl --fail -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
- buildStatus=$(echo "$buildInfo" | \
- jq -r '.buildstatus')
+ finished=$(echo "$buildInfo" | jq -r '.finished')
- if [[ "$buildStatus" -ne 0 ]]; then
+ if [[ $finished = 0 ]]; then
+ continue
+ fi
+
+ buildStatus=$(echo "$buildInfo" | jq -r '.buildstatus')
+
+ if [[ $buildStatus != 0 ]]; then
someBuildFailed=1
- echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra"
+ echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra: $buildInfo"
fi
done
diff --git a/scripts/create-darwin-volume.sh b/scripts/create-darwin-volume.sh
index bd8a7ee3a..4bac4b7ba 100755
--- a/scripts/create-darwin-volume.sh
+++ b/scripts/create-darwin-volume.sh
@@ -246,7 +246,8 @@ get_volume_pass() {
verify_volume_pass() {
local volume_special="$1" # (i.e., disk1s7)
local volume_uuid="$2"
- /usr/sbin/diskutil apfs unlockVolume "$volume_special" -verify -stdinpassphrase -user "$volume_uuid"
+ _sudo "to confirm the password actually unlocks the volume" \
+ /usr/sbin/diskutil apfs unlockVolume "$volume_special" -verify -stdinpassphrase -user "$volume_uuid"
}
volume_pass_works() {
@@ -685,22 +686,27 @@ encrypt_volume() {
local volume_uuid="$1"
local volume_label="$2"
local password
+
+ task "Encrypt the Nix volume" >&2
+
# Note: mount/unmount are late additions to support the right order
# of operations for creating the volume and then baking its uuid into
# other artifacts; not as well-trod wrt to potential errors, race
# conditions, etc.
- /usr/sbin/diskutil mount "$volume_label"
+ _sudo "to mount your Nix volume for encrypting" \
+ /usr/sbin/diskutil mount "$volume_label"
password="$(/usr/bin/xxd -l 32 -p -c 256 /dev/random)"
_sudo "to add your Nix volume's password to Keychain" \
/usr/bin/security -i < /dev/null 2>&1; then
- fetch() { curl -L "$1" -o "$2"; }
+ fetch() { curl --fail -L "$1" -o "$2"; }
elif command -v wget > /dev/null 2>&1; then
fetch() { wget "$1" -O "$2"; }
else
diff --git a/scripts/nix-profile.sh.in b/scripts/nix-profile.sh.in
index 8cba1c522..45cbcbe74 100644
--- a/scripts/nix-profile.sh.in
+++ b/scripts/nix-profile.sh.in
@@ -24,6 +24,9 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ca-bundle.crt"
fi
+ # Only use MANPATH if it is already set. In general `man` will just simply
+ # pick up `.nix-profile/share/man` because is it close to `.nix-profile/bin`
+ # which is in the $PATH. For more info, run `manpath -d`.
if [ -n "${MANPATH-}" ]; then
export MANPATH="$NIX_LINK/share/man:$MANPATH"
fi
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc
index 9d541b45d..ff8ba2724 100644
--- a/src/build-remote/build-remote.cc
+++ b/src/build-remote/build-remote.cc
@@ -14,6 +14,7 @@
#include "pathlocks.hh"
#include "globals.hh"
#include "serialise.hh"
+#include "build-result.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "local-store.hh"
@@ -32,7 +33,7 @@ std::string escapeUri(std::string uri)
return uri;
}
-static string currentLoad;
+static std::string currentLoad;
static AutoCloseFD openSlotLock(const Machine & m, uint64_t slot)
{
@@ -97,7 +98,7 @@ static int main_build_remote(int argc, char * * argv)
}
std::optional drvPath;
- string storeUri;
+ std::string storeUri;
while (true) {
@@ -183,7 +184,7 @@ static int main_build_remote(int argc, char * * argv)
else
{
// build the hint template.
- string errorText =
+ std::string errorText =
"Failed to find a machine for remote build!\n"
"derivation: %s\nrequired (system, features): (%s, %s)";
errorText += "\n%s available machines:";
@@ -193,7 +194,7 @@ static int main_build_remote(int argc, char * * argv)
errorText += "\n(%s, %s, %s, %s)";
// add the template values.
- string drvstr;
+ std::string drvstr;
if (drvPath.has_value())
drvstr = drvPath->to_string();
else
@@ -208,7 +209,7 @@ static int main_build_remote(int argc, char * * argv)
for (auto & m : machines)
error
- % concatStringsSep>(", ", m.systemTypes)
+ % concatStringsSep>(", ", m.systemTypes)
% m.maxJobs
% concatStringsSep(", ", m.supportedFeatures)
% concatStringsSep(", ", m.mandatoryFeatures);
@@ -299,7 +300,7 @@ connected:
std::set missingRealisations;
StorePathSet missingPaths;
- if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) {
+ if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
for (auto & outputName : wantedOutputs) {
auto thisOutputHash = outputHashes.at(outputName);
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc
index e3c8fb29f..5cb8728e9 100644
--- a/src/libcmd/command.cc
+++ b/src/libcmd/command.cc
@@ -209,7 +209,7 @@ void BuiltPathsCommand::run(ref store)
for (auto & p : store->queryAllValidPaths())
paths.push_back(BuiltPath::Opaque{p});
} else {
- paths = toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
+ paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
if (recursive) {
// XXX: This only computes the store path closure, ignoring
// intermediate realisations
@@ -260,7 +260,8 @@ Strings editorFor(const Pos & pos)
if (pos.line > 0 && (
editor.find("emacs") != std::string::npos ||
editor.find("nano") != std::string::npos ||
- editor.find("vim") != std::string::npos))
+ editor.find("vim") != std::string::npos ||
+ editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", pos.line));
args.push_back(pos.file);
return args;
diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh
index 146a08ed6..94ad80210 100644
--- a/src/libcmd/command.hh
+++ b/src/libcmd/command.hh
@@ -5,7 +5,6 @@
#include "common-eval-args.hh"
#include "path.hh"
#include "flake/lockfile.hh"
-#include "store-api.hh"
#include
@@ -84,14 +83,6 @@ struct MixFlakeOptions : virtual Args, EvalCommand
{ return {}; }
};
-/* How to handle derivations in commands that operate on store paths. */
-enum class OperateOn {
- /* Operate on the output path. */
- Output,
- /* Operate on the .drv path. */
- Derivation
-};
-
struct SourceExprCommand : virtual Args, MixFlakeOptions
{
std::optional file;
@@ -115,19 +106,6 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
void completeInstallable(std::string_view prefix);
};
-enum class Realise {
- /* Build the derivation. Postcondition: the
- derivation outputs exist. */
- Outputs,
- /* Don't build the derivation. Postcondition: the store derivation
- exists. */
- Derivation,
- /* Evaluate in dry-run mode. Postcondition: nothing. */
- // FIXME: currently unused, but could be revived if we can
- // evaluate derivations in-memory.
- Nothing
-};
-
/* A command that operates on a list of "installables", which can be
store paths, attribute paths, Nix expressions, etc. */
struct InstallablesCommand : virtual Args, SourceExprCommand
@@ -240,38 +218,6 @@ static RegisterCommand registerCommand2(std::vector && name)
return RegisterCommand(std::move(name), [](){ return make_ref(); });
}
-BuiltPaths build(
- ref evalStore,
- ref store, Realise mode,
- const std::vector> & installables,
- BuildMode bMode = bmNormal);
-
-std::set toStorePaths(
- ref evalStore,
- ref store,
- Realise mode,
- OperateOn operateOn,
- const std::vector> & installables);
-
-StorePath toStorePath(
- ref evalStore,
- ref store,
- Realise mode,
- OperateOn operateOn,
- std::shared_ptr installable);
-
-std::set toDerivations(
- ref store,
- const std::vector> & installables,
- bool useDeriver = false);
-
-BuiltPaths toBuiltPaths(
- ref evalStore,
- ref store,
- Realise mode,
- OperateOn operateOn,
- const std::vector> & installables);
-
/* Helper function to generate args that invoke $EDITOR on
filename:lineno. */
Strings editorFor(const Pos & pos);
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 38a177f80..955bbe6fb 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -12,6 +12,7 @@
#include "eval-cache.hh"
#include "url.hh"
#include "registry.hh"
+#include "build-result.hh"
#include
#include
@@ -97,7 +98,7 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.writeLockFile = false;
lockFlags.inputOverrides.insert_or_assign(
flake::parseInputPath(inputPath),
- parseFlakeRef(flakeRef, absPath(".")));
+ parseFlakeRef(flakeRef, absPath("."), true));
}}
});
@@ -133,7 +134,9 @@ SourceExprCommand::SourceExprCommand()
addFlag({
.longName = "file",
.shortName = 'f',
- .description = "Interpret installables as attribute paths relative to the Nix expression stored in *file*.",
+ .description =
+ "Interpret installables as attribute paths relative to the Nix expression stored in *file*. "
+ "If *file* is the character -, then a Nix expression will be read from standard input.",
.category = installablesCategory,
.labels = {"file"},
.handler = {&file},
@@ -158,7 +161,10 @@ SourceExprCommand::SourceExprCommand()
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
{
- return {"defaultPackage." + settings.thisSystem.get()};
+ return {
+ "packages." + settings.thisSystem.get() + ".default",
+ "defaultPackage." + settings.thisSystem.get()
+ };
}
Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
@@ -269,9 +275,9 @@ void completeFlakeRefWithFragment(
auto attr = root->findAlongAttrPath(attrPath);
if (!attr) continue;
- for (auto & attr2 : attr->getAttrs()) {
+ for (auto & attr2 : (*attr)->getAttrs()) {
if (hasPrefix(attr2, lastAttr)) {
- auto attrPath2 = attr->getAttrPath(attr2);
+ auto attrPath2 = (*attr)->getAttrPath(attr2);
/* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
completions->add(flakeRefS + "#" + concatStringsSep(".", attrPath2));
@@ -465,11 +471,10 @@ std::vector InstallableAttrPath::toDerivations
std::vector res;
for (auto & drvInfo : drvInfos) {
- res.push_back({
- state->store->parseStorePath(drvInfo.queryDrvPath()),
- state->store->maybeParseStorePath(drvInfo.queryOutPath()),
- drvInfo.queryOutputName()
- });
+ auto drvPath = drvInfo.queryDrvPath();
+ if (!drvPath)
+ throw Error("'%s' is not a derivation", what());
+ res.push_back({ *drvPath, drvInfo.queryOutputName() });
}
return res;
@@ -545,13 +550,14 @@ InstallableFlake::InstallableFlake(
SourceExprCommand * cmd,
ref state,
FlakeRef && flakeRef,
- Strings && attrPaths,
- Strings && prefixes,
+ std::string_view fragment,
+ Strings attrPaths,
+ Strings prefixes,
const flake::LockFlags & lockFlags)
: InstallableValue(state),
flakeRef(flakeRef),
- attrPaths(attrPaths),
- prefixes(prefixes),
+ attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
+ prefixes(fragment == "" ? Strings{} : prefixes),
lockFlags(lockFlags)
{
if (cmd && cmd->getAutoArgs(*state)->size())
@@ -565,29 +571,37 @@ std::tuple InstallableF
auto cache = openEvalCache(*state, lockedFlake);
auto root = cache->getRoot();
+ Suggestions suggestions;
+
for (auto & attrPath : getActualAttrPaths()) {
- auto attr = root->findAlongAttrPath(
+ debug("trying flake output attribute '%s'", attrPath);
+
+ auto attrOrSuggestions = root->findAlongAttrPath(
parseAttrPath(*state, attrPath),
true
);
- if (!attr) continue;
+ if (!attrOrSuggestions) {
+ suggestions += attrOrSuggestions.getSuggestions();
+ continue;
+ }
+
+ auto attr = *attrOrSuggestions;
if (!attr->isDerivation())
throw Error("flake output attribute '%s' is not a derivation", attrPath);
auto drvPath = attr->forceDerivation();
- auto drvInfo = DerivationInfo{
+ auto drvInfo = DerivationInfo {
std::move(drvPath),
- state->store->maybeParseStorePath(attr->getAttr(state->sOutPath)->getString()),
attr->getAttr(state->sOutputName)->getString()
};
return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
}
- throw Error("flake '%s' does not provide attribute %s",
+ throw Error(suggestions, "flake '%s' does not provide attribute %s",
flakeRef, showAttrPaths(getActualAttrPaths()));
}
@@ -606,17 +620,24 @@ std::pair InstallableFlake::toValue(EvalState & state)
auto emptyArgs = state.allocBindings(0);
+ Suggestions suggestions;
+
for (auto & attrPath : getActualAttrPaths()) {
try {
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
state.forceValue(*v, pos);
return {v, pos};
} catch (AttrPathNotFound & e) {
+ suggestions += e.info().suggestions;
}
}
- throw Error("flake '%s' does not provide attribute %s",
- flakeRef, showAttrPaths(getActualAttrPaths()));
+ throw Error(
+ suggestions,
+ "flake '%s' does not provide attribute %s",
+ flakeRef,
+ showAttrPaths(getActualAttrPaths())
+ );
}
std::vector, std::string>>
@@ -631,7 +652,7 @@ InstallableFlake::getCursors(EvalState & state)
for (auto & attrPath : getActualAttrPaths()) {
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
- if (attr) res.push_back({attr, attrPath});
+ if (attr) res.push_back({*attr, attrPath});
}
return res;
@@ -676,7 +697,10 @@ std::vector> SourceExprCommand::parseInstallables(
auto state = getEvalState();
auto vFile = state->allocValue();
- if (file)
+ if (file == "-") {
+ auto e = state->parseStdin();
+ state->eval(e, *vFile);
+ } else if (file)
state->evalFile(lookupFileArg(*state, *file), *vFile);
else {
auto e = state->parseExprFromString(*expr, absPath("."));
@@ -708,7 +732,8 @@ std::vector> SourceExprCommand::parseInstallables(
this,
getEvalState(),
std::move(flakeRef),
- fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment},
+ fragment,
+ getDefaultFlakeAttrPaths(),
getDefaultFlakeAttrPathPrefixes(),
lockFlags));
continue;
@@ -731,56 +756,20 @@ std::shared_ptr SourceExprCommand::parseInstallable(
return installables.front();
}
-BuiltPaths getBuiltPaths(ref evalStore, ref store, const DerivedPaths & hopefullyBuiltPaths)
+BuiltPaths Installable::build(
+ ref evalStore,
+ ref store,
+ Realise mode,
+ const std::vector> & installables,
+ BuildMode bMode)
{
BuiltPaths res;
- for (const auto & b : hopefullyBuiltPaths)
- std::visit(
- overloaded{
- [&](const DerivedPath::Opaque & bo) {
- res.push_back(BuiltPath::Opaque{bo.path});
- },
- [&](const DerivedPath::Built & bfd) {
- OutputPathMap outputs;
- auto drv = evalStore->readDerivation(bfd.drvPath);
- auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
- auto drvOutputs = drv.outputsAndOptPaths(*store);
- for (auto & output : bfd.outputs) {
- if (!outputHashes.count(output))
- throw Error(
- "the derivation '%s' doesn't have an output named '%s'",
- store->printStorePath(bfd.drvPath), output);
- if (settings.isExperimentalFeatureEnabled(
- Xp::CaDerivations)) {
- auto outputId =
- DrvOutput{outputHashes.at(output), output};
- auto realisation =
- store->queryRealisation(outputId);
- if (!realisation)
- throw Error(
- "cannot operate on an output of unbuilt "
- "content-addressed derivation '%s'",
- outputId.to_string());
- outputs.insert_or_assign(
- output, realisation->outPath);
- } else {
- // If ca-derivations isn't enabled, assume that
- // the output path is statically known.
- assert(drvOutputs.count(output));
- assert(drvOutputs.at(output).second);
- outputs.insert_or_assign(
- output, *drvOutputs.at(output).second);
- }
- }
- res.push_back(BuiltPath::Built{bfd.drvPath, outputs});
- },
- },
- b.raw());
-
+ for (auto & [_, builtPath] : build2(evalStore, store, mode, installables, bMode))
+ res.push_back(builtPath);
return res;
}
-BuiltPaths build(
+std::vector, BuiltPath>> Installable::build2(
ref evalStore,
ref store,
Realise mode,
@@ -791,21 +780,96 @@ BuiltPaths build(
settings.readOnlyMode = true;
std::vector pathsToBuild;
+ std::map>> backmap;
for (auto & i : installables) {
- auto b = i->toDerivedPaths();
- pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end());
+ for (auto b : i->toDerivedPaths()) {
+ pathsToBuild.push_back(b);
+ backmap[b].push_back(i);
+ }
}
- if (mode == Realise::Nothing || mode == Realise::Derivation)
- printMissing(store, pathsToBuild, lvlError);
- else if (mode == Realise::Outputs)
- store->buildPaths(pathsToBuild, bMode, evalStore);
+ std::vector, BuiltPath>> res;
- return getBuiltPaths(evalStore, store, pathsToBuild);
+ switch (mode) {
+
+ case Realise::Nothing:
+ case Realise::Derivation:
+ printMissing(store, pathsToBuild, lvlError);
+
+ for (auto & path : pathsToBuild) {
+ for (auto & installable : backmap[path]) {
+ std::visit(overloaded {
+ [&](const DerivedPath::Built & bfd) {
+ OutputPathMap outputs;
+ auto drv = evalStore->readDerivation(bfd.drvPath);
+ auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
+ auto drvOutputs = drv.outputsAndOptPaths(*store);
+ for (auto & output : bfd.outputs) {
+ if (!outputHashes.count(output))
+ throw Error(
+ "the derivation '%s' doesn't have an output named '%s'",
+ store->printStorePath(bfd.drvPath), output);
+ if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
+ DrvOutput outputId { outputHashes.at(output), output };
+ auto realisation = store->queryRealisation(outputId);
+ if (!realisation)
+ throw Error(
+ "cannot operate on an output of unbuilt "
+ "content-addressed derivation '%s'",
+ outputId.to_string());
+ outputs.insert_or_assign(output, realisation->outPath);
+ } else {
+ // If ca-derivations isn't enabled, assume that
+ // the output path is statically known.
+ assert(drvOutputs.count(output));
+ assert(drvOutputs.at(output).second);
+ outputs.insert_or_assign(
+ output, *drvOutputs.at(output).second);
+ }
+ }
+ res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }});
+ },
+ [&](const DerivedPath::Opaque & bo) {
+ res.push_back({installable, BuiltPath::Opaque { bo.path }});
+ },
+ }, path.raw());
+ }
+ }
+
+ break;
+
+ case Realise::Outputs: {
+ for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) {
+ if (!buildResult.success())
+ buildResult.rethrow();
+
+ for (auto & installable : backmap[buildResult.path]) {
+ std::visit(overloaded {
+ [&](const DerivedPath::Built & bfd) {
+ std::map outputs;
+ for (auto & path : buildResult.builtOutputs)
+ outputs.emplace(path.first.outputName, path.second.outPath);
+ res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }});
+ },
+ [&](const DerivedPath::Opaque & bo) {
+ res.push_back({installable, BuiltPath::Opaque { bo.path }});
+ },
+ }, buildResult.path.raw());
+ }
+ }
+
+ break;
+ }
+
+ default:
+ assert(false);
+ }
+
+ return res;
}
-BuiltPaths toBuiltPaths(
+BuiltPaths Installable::toBuiltPaths(
ref evalStore,
ref store,
Realise mode,
@@ -813,19 +877,19 @@ BuiltPaths toBuiltPaths(
const std::vector> & installables)
{
if (operateOn == OperateOn::Output)
- return build(evalStore, store, mode, installables);
+ return Installable::build(evalStore, store, mode, installables);
else {
if (mode == Realise::Nothing)
settings.readOnlyMode = true;
BuiltPaths res;
- for (auto & drvPath : toDerivations(store, installables, true))
+ for (auto & drvPath : Installable::toDerivations(store, installables, true))
res.push_back(BuiltPath::Opaque{drvPath});
return res;
}
}
-StorePathSet toStorePaths(
+StorePathSet Installable::toStorePaths(
ref evalStore,
ref store,
Realise mode, OperateOn operateOn,
@@ -839,7 +903,7 @@ StorePathSet toStorePaths(
return outPaths;
}
-StorePath toStorePath(
+StorePath Installable::toStorePath(
ref evalStore,
ref store,
Realise mode, OperateOn operateOn,
@@ -853,7 +917,7 @@ StorePath toStorePath(
return *paths.begin();
}
-StorePathSet toDerivations(
+StorePathSet Installable::toDerivations(
ref store,
const std::vector> & installables,
bool useDeriver)
diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh
index ced6b3f10..f4bf0d406 100644
--- a/src/libcmd/installables.hh
+++ b/src/libcmd/installables.hh
@@ -5,6 +5,7 @@
#include "path-with-outputs.hh"
#include "derived-path.hh"
#include "eval.hh"
+#include "store-api.hh"
#include "flake/flake.hh"
#include
@@ -29,6 +30,27 @@ struct UnresolvedApp
App resolve(ref evalStore, ref store);
};
+enum class Realise {
+ /* Build the derivation. Postcondition: the
+ derivation outputs exist. */
+ Outputs,
+ /* Don't build the derivation. Postcondition: the store derivation
+ exists. */
+ Derivation,
+ /* Evaluate in dry-run mode. Postcondition: nothing. */
+ // FIXME: currently unused, but could be revived if we can
+ // evaluate derivations in-memory.
+ Nothing
+};
+
+/* How to handle derivations in commands that operate on store paths. */
+enum class OperateOn {
+ /* Operate on the output path. */
+ Output,
+ /* Operate on the .drv path. */
+ Derivation
+};
+
struct Installable
{
virtual ~Installable() { }
@@ -68,6 +90,46 @@ struct Installable
{
return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}});
}
+
+ static BuiltPaths build(
+ ref evalStore,
+ ref store,
+ Realise mode,
+ const std::vector> & installables,
+ BuildMode bMode = bmNormal);
+
+ static std::vector, BuiltPath>> build2(
+ ref evalStore,
+ ref store,
+ Realise mode,
+ const std::vector> & installables,
+ BuildMode bMode = bmNormal);
+
+ static std::set toStorePaths(
+ ref evalStore,
+ ref store,
+ Realise mode,
+ OperateOn operateOn,
+ const std::vector> & installables);
+
+ static StorePath toStorePath(
+ ref evalStore,
+ ref store,
+ Realise mode,
+ OperateOn operateOn,
+ std::shared_ptr installable);
+
+ static std::set toDerivations(
+ ref store,
+ const std::vector> & installables,
+ bool useDeriver = false);
+
+ static BuiltPaths toBuiltPaths(
+ ref evalStore,
+ ref store,
+ Realise mode,
+ OperateOn operateOn,
+ const std::vector> & installables);
};
struct InstallableValue : Installable
@@ -79,7 +141,6 @@ struct InstallableValue : Installable
struct DerivationInfo
{
StorePath drvPath;
- std::optional outPath;
std::string outputName;
};
@@ -102,8 +163,9 @@ struct InstallableFlake : InstallableValue
SourceExprCommand * cmd,
ref state,
FlakeRef && flakeRef,
- Strings && attrPaths,
- Strings && prefixes,
+ std::string_view fragment,
+ Strings attrPaths,
+ Strings prefixes,
const flake::LockFlags & lockFlags);
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc
index 31d0019d4..3dd55e104 100644
--- a/src/libcmd/repl.cc
+++ b/src/libcmd/repl.cc
@@ -25,6 +25,7 @@ extern "C" {
#include "eval-inline.hh"
#include "attr-path.hh"
#include "store-api.hh"
+#include "log-store.hh"
#include "common-eval-args.hh"
#include "get-drvs.hh"
#include "derivations.hh"
@@ -45,7 +46,7 @@ struct NixRepl
: gc
#endif
{
- string curDir;
+ std::string curDir;
ref state;
Bindings * autoArgs;
@@ -65,10 +66,10 @@ struct NixRepl
NixRepl(ref state);
~NixRepl();
void mainLoop(const std::vector & files);
- StringSet completePrefix(string prefix);
- bool getLine(string & input, const std::string &prompt);
+ StringSet completePrefix(const std::string & prefix);
+ bool getLine(std::string & input, const std::string &prompt);
StorePath getDerivationPath(Value & v);
- bool processLine(string line);
+ bool processLine(std::string line);
void loadFile(const Path & path);
void loadFlake(const std::string & flakeRef);
void initEnv();
@@ -76,22 +77,21 @@ struct NixRepl
void reloadFiles();
void addAttrsToScope(Value & attrs);
void addVarToScope(const Symbol & name, Value & v);
- Expr * parseString(string s);
- void evalString(string s, Value & v);
+ Expr * parseString(std::string s);
+ void evalString(std::string s, Value & v);
void loadDebugTraceEnv(DebugTrace &dt);
- typedef set ValuesSeen;
+ typedef std::set ValuesSeen;
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth);
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
};
-string removeWhitespace(string s)
+std::string removeWhitespace(std::string s)
{
s = chomp(s);
size_t n = s.find_first_not_of(" \n\r\t");
- if (n != string::npos) s = string(s, n);
-
+ if (n != std::string::npos) s = std::string(s, n);
return s;
}
@@ -111,7 +111,7 @@ NixRepl::~NixRepl()
write_history(historyFile.c_str());
}
-string runNix(Path program, const Strings & args,
+std::string runNix(Path program, const Strings & args,
const std::optional & input = {})
{
auto subprocessEnv = getEnv();
@@ -229,7 +229,7 @@ std::ostream& showDebugTrace(std::ostream &out, const DebugTrace &dt)
void NixRepl::mainLoop(const std::vector & files)
{
- string error = ANSI_RED "error:" ANSI_NORMAL " ";
+ std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
notice("Welcome to Nix " + nixVersion + ". Type :? for help.\n");
if (!files.empty()) {
@@ -297,7 +297,7 @@ void NixRepl::mainLoop(const std::vector & files)
}
-bool NixRepl::getLine(string & input, const std::string &prompt)
+bool NixRepl::getLine(std::string & input, const std::string & prompt)
{
struct sigaction act, old;
sigset_t savedSignalMask, set;
@@ -342,7 +342,7 @@ bool NixRepl::getLine(string & input, const std::string &prompt)
}
-StringSet NixRepl::completePrefix(string prefix)
+StringSet NixRepl::completePrefix(const std::string & prefix)
{
StringSet completions;
@@ -358,7 +358,7 @@ StringSet NixRepl::completePrefix(string prefix)
size_t slash, dot;
- if ((slash = cur.rfind('/')) != string::npos) {
+ if ((slash = cur.rfind('/')) != std::string::npos) {
try {
auto dir = std::string(cur, 0, slash);
auto prefix2 = std::string(cur, slash + 1);
@@ -368,11 +368,11 @@ StringSet NixRepl::completePrefix(string prefix)
}
} catch (Error &) {
}
- } else if ((dot = cur.rfind('.')) == string::npos) {
+ } else if ((dot = cur.rfind('.')) == std::string::npos) {
/* This is a variable name; look it up in the current scope. */
StringSet::iterator i = varNames.lower_bound(cur);
while (i != varNames.end()) {
- if (string(*i, 0, cur.size()) != cur) break;
+ if (i->substr(0, cur.size()) != cur) break;
completions.insert(prev + *i);
i++;
}
@@ -381,8 +381,8 @@ StringSet NixRepl::completePrefix(string prefix)
/* This is an expression that should evaluate to an
attribute set. Evaluate it to get the names of the
attributes. */
- string expr(cur, 0, dot);
- string cur2 = string(cur, dot + 1);
+ auto expr = cur.substr(0, dot);
+ auto cur2 = cur.substr(dot + 1);
Expr * e = parseString(expr);
Value v;
@@ -390,8 +390,8 @@ StringSet NixRepl::completePrefix(string prefix)
state->forceAttrs(v, noPos);
for (auto & i : *v.attrs) {
- string name = i.name;
- if (string(name, 0, cur2.size()) != cur2) continue;
+ std::string name = i.name;
+ if (name.substr(0, cur2.size()) != cur2) continue;
completions.insert(prev + expr + "." + name);
}
@@ -410,7 +410,7 @@ StringSet NixRepl::completePrefix(string prefix)
}
-bool isVarName(const string & s)
+static bool isVarName(std::string_view s)
{
if (s.size() == 0) return false;
char c = s[0];
@@ -429,13 +429,12 @@ StorePath NixRepl::getDerivationPath(Value & v) {
auto drvInfo = getDerivation(*state, v, false);
if (!drvInfo)
throw Error("expression does not evaluate to a derivation, so I can't build it");
- Path drvPathRaw = drvInfo->queryDrvPath();
- if (drvPathRaw == "")
- throw Error("expression did not evaluate to a valid derivation (no drv path)");
- StorePath drvPath = state->store->parseStorePath(drvPathRaw);
- if (!state->store->isValidPath(drvPath))
- throw Error("expression did not evaluate to a valid derivation (invalid drv path)");
- return drvPath;
+ auto drvPath = drvInfo->queryDrvPath();
+ if (!drvPath)
+ throw Error("expression did not evaluate to a valid derivation (no 'drvPath' attribute)");
+ if (!state->store->isValidPath(*drvPath))
+ throw Error("expression evaluated to invalid derivation '%s'", state->store->printStorePath(*drvPath));
+ return *drvPath;
}
void NixRepl::loadDebugTraceEnv(DebugTrace &dt)
@@ -457,18 +456,19 @@ void NixRepl::loadDebugTraceEnv(DebugTrace &dt)
}
}
-bool NixRepl::processLine(string line)
+bool NixRepl::processLine(std::string line)
{
+ line = trim(line);
if (line == "") return true;
_isInterrupted = false;
- string command, arg;
+ std::string command, arg;
if (line[0] == ':') {
size_t p = line.find_first_of(" \n\r\t");
- command = string(line, 0, p);
- if (p != string::npos) arg = removeWhitespace(string(line, p));
+ command = line.substr(0, p);
+ if (p != std::string::npos) arg = removeWhitespace(line.substr(p));
} else {
arg = line;
}
@@ -667,9 +667,16 @@ bool NixRepl::processLine(string line)
bool foundLog = false;
RunPager pager;
for (auto & sub : subs) {
- auto log = sub->getBuildLog(drvPath);
+ auto * logSubP = dynamic_cast(&*sub);
+ if (!logSubP) {
+ printInfo("Skipped '%s' which does not support retrieving build logs", sub->getUri());
+ continue;
+ }
+ auto & logSub = *logSubP;
+
+ auto log = logSub.getBuildLog(drvPath);
if (log) {
- printInfo("got build log for '%s' from '%s'", drvPathRaw, sub->getUri());
+ printInfo("got build log for '%s' from '%s'", drvPathRaw, logSub.getUri());
logger->writeToStdout(*log);
foundLog = true;
break;
@@ -733,13 +740,13 @@ bool NixRepl::processLine(string line)
else {
size_t p = line.find('=');
- string name;
- if (p != string::npos &&
+ std::string name;
+ if (p != std::string::npos &&
p < line.size() &&
line[p + 1] != '=' &&
- isVarName(name = removeWhitespace(string(line, 0, p))))
+ isVarName(name = removeWhitespace(line.substr(0, p))))
{
- Expr * e = parseString(string(line, p + 1));
+ Expr * e = parseString(line.substr(p + 1));
Value & v(*state->allocValue());
v.mkThunk(env, e);
addVarToScope(state->symbols.create(name), v);
@@ -766,9 +773,12 @@ void NixRepl::loadFile(const Path & path)
void NixRepl::loadFlake(const std::string & flakeRefS)
{
+ if (flakeRefS.empty())
+ throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)");
+
auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true);
- if (evalSettings.pureEval && !flakeRef.input.isImmutable())
- throw Error("cannot use ':load-flake' on mutable flake reference '%s' (use --impure to override)", flakeRefS);
+ if (evalSettings.pureEval && !flakeRef.input.isLocked())
+ throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS);
Value v;
@@ -829,7 +839,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
for (auto & i : *attrs.attrs) {
staticEnv->vars.emplace_back(i.name, displ);
env->values[displ++] = i.value;
- varNames.insert((string) i.name);
+ varNames.insert((std::string) i.name);
}
staticEnv->sort();
staticEnv->deduplicate();
@@ -846,17 +856,18 @@ void NixRepl::addVarToScope(const Symbol & name, Value & v)
staticEnv->vars.emplace_back(name, displ);
staticEnv->sort();
env->values[displ++] = &v;
- varNames.insert((string) name);
+ varNames.insert((std::string) name);
}
-Expr * NixRepl::parseString(string s)
+
+Expr * NixRepl::parseString(std::string s)
{
Expr * e = state->parseExprFromString(std::move(s), curDir, staticEnv);
return e;
}
-void NixRepl::evalString(string s, Value & v)
+void NixRepl::evalString(std::string s, Value & v)
{
Expr * e = parseString(s);
e->eval(*state, *env, v);
@@ -925,14 +936,17 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
str << "«derivation ";
Bindings::iterator i = v.attrs->find(state->sDrvPath);
PathSet context;
- Path drvPath = i != v.attrs->end() ? state->coerceToPath(*i->pos, *i->value, context) : "???";
- str << drvPath << "»";
+ if (i != v.attrs->end())
+ str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context));
+ else
+ str << "???";
+ str << "»";
}
else if (maxDepth > 0) {
str << "{ ";
- typedef std::map Sorted;
+ typedef std::map Sorted;
Sorted sorted;
for (auto & i : *v.attrs)
sorted[i.name] = i.value;
@@ -943,7 +957,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
else
printStringValue(str, i.first.c_str());
str << " = ";
- if (seen.find(i.second) != seen.end())
+ if (seen.count(i.second))
str << "«repeated»";
else
try {
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index bf0c1dabc..32deecfae 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -9,7 +9,7 @@ namespace nix {
static Strings parseAttrPath(std::string_view s)
{
Strings res;
- string cur;
+ std::string cur;
auto i = s.begin();
while (i != s.end()) {
if (*i == '.') {
@@ -41,7 +41,7 @@ std::vector parseAttrPath(EvalState & state, std::string_view s)
}
-std::pair findAlongAttrPath(EvalState & state, const string & attrPath,
+std::pair findAlongAttrPath(EvalState & state, const std::string & attrPath,
Bindings & autoArgs, Value & vIn)
{
Strings tokens = parseAttrPath(attrPath);
@@ -74,8 +74,14 @@ std::pair findAlongAttrPath(EvalState & state, const string & attr
throw Error("empty attribute name in selection path '%1%'", attrPath);
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
- if (a == v->attrs->end())
- throw AttrPathNotFound("attribute '%1%' in selection path '%2%' not found", attr, attrPath);
+ if (a == v->attrs->end()) {
+ std::set attrNames;
+ for (auto & attr : *v->attrs)
+ attrNames.insert(attr.name);
+
+ auto suggestions = Suggestions::bestMatches(attrNames, attr);
+ throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
+ }
v = &*a->value;
pos = *a->pos;
}
@@ -121,7 +127,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
std::string filename(pos, 0, colon);
unsigned int lineno;
try {
- lineno = std::stoi(std::string(pos, colon + 1, string::npos));
+ lineno = std::stoi(std::string(pos, colon + 1, std::string::npos));
} catch (std::invalid_argument & e) {
throw ParseError("cannot parse line number '%s'", pos);
}
diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh
index 2ee3ea089..ff1135a06 100644
--- a/src/libexpr/attr-path.hh
+++ b/src/libexpr/attr-path.hh
@@ -10,8 +10,11 @@ namespace nix {
MakeError(AttrPathNotFound, Error);
MakeError(NoPositionInfo, Error);
-std::pair findAlongAttrPath(EvalState & state, const string & attrPath,
- Bindings & autoArgs, Value & vIn);
+std::pair findAlongAttrPath(
+ EvalState & state,
+ const std::string & attrPath,
+ Bindings & autoArgs,
+ Value & vIn);
/* Heuristic to find the filename and lineno or a nix value. */
Pos findPackageFilename(EvalState & state, Value & v, std::string what);
diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh
index 3e4899efc..cad9743ea 100644
--- a/src/libexpr/attr-set.hh
+++ b/src/libexpr/attr-set.hh
@@ -105,7 +105,7 @@ public:
for (size_t n = 0; n < size_; n++)
res.emplace_back(&attrs[n]);
std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) {
- return (const string &) a->name < (const string &) b->name;
+ return (const std::string &) a->name < (const std::string &) b->name;
});
return res;
}
diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc
index fffca4ac5..e50ff244c 100644
--- a/src/libexpr/common-eval-args.cc
+++ b/src/libexpr/common-eval-args.cc
@@ -77,7 +77,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
for (auto & i : autoArgs) {
auto v = state.allocValue();
if (i.second[0] == 'E')
- state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
+ state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), absPath(".")));
else
v->mkString(((std::string_view) i.second).substr(1));
res.insert(state.symbols.create(i.first), v);
@@ -85,17 +85,17 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
return res.finish();
}
-Path lookupFileArg(EvalState & state, string s)
+Path lookupFileArg(EvalState & state, std::string_view s)
{
if (isUri(s)) {
return state.store->toRealPath(
fetchers::downloadTarball(
state.store, resolveUri(s), "source", false).first.storePath);
} else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
- Path p = s.substr(1, s.size() - 2);
+ Path p(s.substr(1, s.size() - 2));
return state.findFile(p);
} else
- return absPath(s);
+ return absPath(std::string(s));
}
}
diff --git a/src/libexpr/common-eval-args.hh b/src/libexpr/common-eval-args.hh
index 0e113fff1..03fa226aa 100644
--- a/src/libexpr/common-eval-args.hh
+++ b/src/libexpr/common-eval-args.hh
@@ -22,6 +22,6 @@ private:
std::map autoArgs;
};
-Path lookupFileArg(EvalState & state, string s);
+Path lookupFileArg(EvalState & state, std::string_view s);
}
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index b102684ec..9f6152561 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -21,6 +21,8 @@ struct AttrDb
{
std::atomic_bool failed{false};
+ const Store & cfg;
+
struct State
{
SQLite db;
@@ -33,8 +35,9 @@ struct AttrDb
std::unique_ptr> _state;
- AttrDb(const Hash & fingerprint)
- : _state(std::make_unique>())
+ AttrDb(const Store & cfg, const Hash & fingerprint)
+ : cfg(cfg)
+ , _state(std::make_unique>())
{
auto state(_state->lock());
@@ -254,10 +257,10 @@ struct AttrDb
return {{rowId, attrs}};
}
case AttrType::String: {
- std::vector> context;
+ NixStringContext context;
if (!queryAttribute.isNull(3))
for (auto & s : tokenizeString>(queryAttribute.getStr(3), ";"))
- context.push_back(decodeContext(s));
+ context.push_back(decodeContext(cfg, s));
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
}
case AttrType::Bool:
@@ -274,10 +277,10 @@ struct AttrDb
}
};
-static std::shared_ptr makeAttrDb(const Hash & fingerprint)
+static std::shared_ptr makeAttrDb(const Store & cfg, const Hash & fingerprint)
{
try {
- return std::make_shared(fingerprint);
+ return std::make_shared(cfg, fingerprint);
} catch (SQLiteError &) {
ignoreException();
return nullptr;
@@ -288,7 +291,7 @@ EvalCache::EvalCache(
std::optional> useCache,
EvalState & state,
RootLoader rootLoader)
- : db(useCache ? makeAttrDb(*useCache) : nullptr)
+ : db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr)
, state(state)
, rootLoader(rootLoader)
{
@@ -406,6 +409,16 @@ Value & AttrCursor::forceValue()
return v;
}
+Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
+{
+ auto attrNames = getAttrs();
+ std::set strAttrNames;
+ for (auto & name : attrNames)
+ strAttrNames.insert(std::string(name));
+
+ return Suggestions::bestMatches(strAttrNames, name);
+}
+
std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
{
if (root->db) {
@@ -446,6 +459,11 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro
return nullptr;
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
+ for (auto & attr : *v.attrs) {
+ if (root->db)
+ root->db->setPlaceholder({cachedValue->first, attr.name});
+ }
+
auto attr = v.attrs->get(name);
if (!attr) {
@@ -464,7 +482,7 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro
cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), placeholder_t()};
}
- return std::make_shared(
+ return make_ref(
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
}
@@ -473,27 +491,31 @@ std::shared_ptr AttrCursor::maybeGetAttr(std::string_view name)
return maybeGetAttr(root->state.symbols.create(name));
}
-std::shared_ptr AttrCursor::getAttr(Symbol name, bool forceErrors)
+ref AttrCursor::getAttr(Symbol name, bool forceErrors)
{
auto p = maybeGetAttr(name, forceErrors);
if (!p)
throw Error("attribute '%s' does not exist", getAttrPathStr(name));
- return p;
+ return ref(p);
}
-std::shared_ptr AttrCursor::getAttr(std::string_view name)
+ref AttrCursor::getAttr(std::string_view name)
{
return getAttr(root->state.symbols.create(name));
}
-std::shared_ptr AttrCursor::findAlongAttrPath(const std::vector & attrPath, bool force)
+OrSuggestions[> AttrCursor::findAlongAttrPath(const std::vector & attrPath, bool force)
{
auto res = shared_from_this();
for (auto & attr : attrPath) {
- res = res->maybeGetAttr(attr, force);
- if (!res) return {};
+ auto child = res->maybeGetAttr(attr, force);
+ if (!child) {
+ auto suggestions = res->getSuggestionsForAttr(attr);
+ return OrSuggestions][>::failed(suggestions);
+ }
+ res = child;
}
- return res;
+ return ref(res);
}
std::string AttrCursor::getString()
@@ -527,7 +549,7 @@ string_t AttrCursor::getStringWithContext()
if (auto s = std::get_if(&cachedValue->second)) {
bool valid = true;
for (auto & c : s->second) {
- if (!root->state.store->isValidPath(root->state.store->parseStorePath(c.first))) {
+ if (!root->state.store->isValidPath(c.first)) {
valid = false;
break;
}
@@ -544,7 +566,7 @@ string_t AttrCursor::getStringWithContext()
auto & v = forceValue();
if (v.type() == nString)
- return {v.string.s, v.getContext()};
+ return {v.string.s, v.getContext(*root->state.store)};
else if (v.type() == nPath)
return {v.path, {}};
else
@@ -599,7 +621,7 @@ std::vector AttrCursor::getAttrs()
for (auto & attr : *getValue().attrs)
attrs.push_back(attr.name);
std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
- return (const string &) a < (const string &) b;
+ return (const std::string &) a < (const std::string &) b;
});
if (root->db)
diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh
index 43b34ebcb..c9a9bf471 100644
--- a/src/libexpr/eval-cache.hh
+++ b/src/libexpr/eval-cache.hh
@@ -52,7 +52,7 @@ struct misc_t {};
struct failed_t {};
typedef uint64_t AttrId;
typedef std::pair AttrKey;
-typedef std::pair>> string_t;
+typedef std::pair string_t;
typedef std::variant<
std::vector,
@@ -94,15 +94,17 @@ public:
std::string getAttrPathStr(Symbol name) const;
+ Suggestions getSuggestionsForAttr(Symbol name);
+
std::shared_ptr maybeGetAttr(Symbol name, bool forceErrors = false);
std::shared_ptr maybeGetAttr(std::string_view name);
- std::shared_ptr getAttr(Symbol name, bool forceErrors = false);
+ ref getAttr(Symbol name, bool forceErrors = false);
- std::shared_ptr getAttr(std::string_view name);
+ ref getAttr(std::string_view name);
- std::shared_ptr findAlongAttrPath(const std::vector & attrPath, bool force = false);
+ OrSuggestions][> findAlongAttrPath(const std::vector & attrPath, bool force = false);
std::string getString();
diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh
index 7ed05950a..9b0073822 100644
--- a/src/libexpr/eval-inline.hh
+++ b/src/libexpr/eval-inline.hh
@@ -38,6 +38,81 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
}
+/* Note: Various places expect the allocated memory to be zeroed. */
+[[gnu::always_inline]]
+inline void * allocBytes(size_t n)
+{
+ void * p;
+#if HAVE_BOEHMGC
+ p = GC_MALLOC(n);
+#else
+ p = calloc(n, 1);
+#endif
+ if (!p) throw std::bad_alloc();
+ return p;
+}
+
+
+[[gnu::always_inline]]
+Value * EvalState::allocValue()
+{
+#if HAVE_BOEHMGC
+ /* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
+ GC_malloc_many returns a linked list of objects of the given size, where the first word
+ of each object is also the pointer to the next object in the list. This also means that we
+ have to explicitly clear the first word of every object we take. */
+ if (!*valueAllocCache) {
+ *valueAllocCache = GC_malloc_many(sizeof(Value));
+ if (!*valueAllocCache) throw std::bad_alloc();
+ }
+
+ /* GC_NEXT is a convenience macro for accessing the first word of an object.
+ Take the first list item, advance the list to the next item, and clear the next pointer. */
+ void * p = *valueAllocCache;
+ *valueAllocCache = GC_NEXT(p);
+ GC_NEXT(p) = nullptr;
+#else
+ void * p = allocBytes(sizeof(Value));
+#endif
+
+ nrValues++;
+ return (Value *) p;
+}
+
+
+[[gnu::always_inline]]
+Env & EvalState::allocEnv(size_t size)
+{
+ nrEnvs++;
+ nrValuesInEnvs += size;
+
+ Env * env;
+
+#if HAVE_BOEHMGC
+ if (size == 1) {
+ /* see allocValue for explanations. */
+ if (!*env1AllocCache) {
+ *env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *));
+ if (!*env1AllocCache) throw std::bad_alloc();
+ }
+
+ void * p = *env1AllocCache;
+ *env1AllocCache = GC_NEXT(p);
+ GC_NEXT(p) = nullptr;
+ env = (Env *) p;
+ } else
+#endif
+ env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
+
+ env->type = Env::Plain;
+
+ /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
+
+ return *env;
+}
+
+
+[[gnu::always_inline]]
void EvalState::forceValue(Value & v, const Pos & pos)
{
forceValue(v, [&]() { return pos; });
@@ -66,6 +141,7 @@ void EvalState::forceValue(Value & v, Callable getPos)
}
+[[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
{
forceAttrs(v, [&]() { return pos; });
@@ -73,6 +149,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
template
+[[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, Callable getPos)
{
forceValue(v, getPos);
@@ -81,6 +158,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos)
}
+[[gnu::always_inline]]
inline void EvalState::forceList(Value & v, const Pos & pos)
{
forceValue(v, pos);
@@ -88,18 +166,5 @@ inline void EvalState::forceList(Value & v, const Pos & pos)
throwTypeError(pos, "value is %1% while a list was expected", v, *this);
}
-/* Note: Various places expect the allocated memory to be zeroed. */
-inline void * allocBytes(size_t n)
-{
- void * p;
-#if HAVE_BOEHMGC
- p = GC_MALLOC(n);
-#else
- p = calloc(n, 1);
-#endif
- if (!p) throw std::bad_alloc();
- return p;
-}
-
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 4211d72dd..5ad7e546c 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -63,9 +63,15 @@ static char * dupString(const char * s)
}
-static char * dupStringWithLen(const char * s, size_t size)
+// When there's no need to write to the string, we can optimize away empty
+// string allocations.
+// This function handles makeImmutableStringWithLen(null, 0) by returning the
+// empty string.
+static const char * makeImmutableStringWithLen(const char * s, size_t size)
{
char * t;
+ if (size == 0)
+ return "";
#if HAVE_BOEHMGC
t = GC_STRNDUP(s, size);
#else
@@ -75,6 +81,10 @@ static char * dupStringWithLen(const char * s, size_t size)
return t;
}
+static inline const char * makeImmutableString(std::string_view s) {
+ return makeImmutableStringWithLen(s.data(), s.size());
+}
+
RootValue allocRootValue(Value * v)
{
@@ -86,25 +96,20 @@ RootValue allocRootValue(Value * v)
}
-void printValue(std::ostream & str, std::set & active, const Value & v)
+void Value::print(std::ostream & str, std::set * seen) const
{
checkInterrupt();
- if (!active.insert(&v).second) {
- str << "";
- return;
- }
-
- switch (v.internalType) {
+ switch (internalType) {
case tInt:
- str << v.integer;
+ str << integer;
break;
case tBool:
- str << (v.boolean ? "true" : "false");
+ str << (boolean ? "true" : "false");
break;
case tString:
str << "\"";
- for (const char * i = v.string.s; *i; i++)
+ 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";
@@ -114,30 +119,38 @@ void printValue(std::ostream & str, std::set & active, const Valu
str << "\"";
break;
case tPath:
- str << v.path; // !!! escaping?
+ str << path; // !!! escaping?
break;
case tNull:
str << "null";
break;
case tAttrs: {
- str << "{ ";
- for (auto & i : v.attrs->lexicographicOrder()) {
- str << i->name << " = ";
- printValue(str, active, *i->value);
- str << "; ";
+ if (seen && !attrs->empty() && !seen->insert(attrs).second)
+ str << "«repeated»";
+ else {
+ str << "{ ";
+ for (auto & i : attrs->lexicographicOrder()) {
+ str << i->name << " = ";
+ i->value->print(str, seen);
+ str << "; ";
+ }
+ str << "}";
}
- str << "}";
break;
}
case tList1:
case tList2:
case tListN:
- str << "[ ";
- for (auto v2 : v.listItems()) {
- printValue(str, active, *v2);
- str << " ";
+ if (seen && listSize() && !seen->insert(listElems()).second)
+ str << "«repeated»";
+ else {
+ str << "[ ";
+ for (auto v2 : listItems()) {
+ v2->print(str, seen);
+ str << " ";
+ }
+ str << "]";
}
- str << "]";
break;
case tThunk:
case tApp:
@@ -153,28 +166,32 @@ void printValue(std::ostream & str, std::set & active, const Valu
str << "";
break;
case tExternal:
- str << *v.external;
+ str << *external;
break;
case tFloat:
- str << v.fpoint;
+ str << fpoint;
break;
default:
abort();
}
+}
- active.erase(&v);
+
+void Value::print(std::ostream & str, bool showRepeated) const
+{
+ std::set seen;
+ print(str, showRepeated ? nullptr : &seen);
}
std::ostream & operator << (std::ostream & str, const Value & v)
{
- std::set active;
- printValue(str, active, v);
+ v.print(str, false);
return str;
}
-const Value *getPrimOp(const Value &v) {
+const Value * getPrimOp(const Value &v) {
const Value * primOp = &v;
while (primOp->isPrimOpApp()) {
primOp = primOp->primOpApp.left;
@@ -183,7 +200,7 @@ const Value *getPrimOp(const Value &v) {
return primOp;
}
-string showType(ValueType type)
+std::string_view showType(ValueType type)
{
switch (type) {
case nInt: return "an integer";
@@ -202,20 +219,20 @@ string showType(ValueType type)
}
-string showType(const Value & v)
+std::string showType(const Value & v)
{
switch (v.internalType) {
case tString: return v.string.context ? "a string with context" : "a string";
case tPrimOp:
- return fmt("the built-in function '%s'", string(v.primOp->name));
+ return fmt("the built-in function '%s'", std::string(v.primOp->name));
case tPrimOpApp:
- return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
+ return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name));
case tExternal: return v.external->showType();
case tThunk: return "a thunk";
case tApp: return "a function application";
case tBlackhole: return "a black hole";
default:
- return showType(v.type());
+ return std::string(showType(v.type()));
}
}
@@ -356,7 +373,7 @@ void initGC()
/* Very hacky way to parse $NIX_PATH, which is colon-separated, but
can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */
-static Strings parseNixPath(const string & s)
+static Strings parseNixPath(const std::string & s)
{
Strings res;
@@ -419,6 +436,7 @@ EvalState::EvalState(
, sBuilder(symbols.create("builder"))
, sArgs(symbols.create("args"))
, sContentAddressed(symbols.create("__contentAddressed"))
+ , sImpure(symbols.create("__impure"))
, sOutputHash(symbols.create("outputHash"))
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
, sOutputHashMode(symbols.create("outputHashMode"))
@@ -440,8 +458,10 @@ EvalState::EvalState(
, regexCache(makeRegexCache())
#if HAVE_BOEHMGC
, valueAllocCache(std::allocate_shared(traceable_allocator(), nullptr))
+ , env1AllocCache(std::allocate_shared(traceable_allocator(), nullptr))
#else
, valueAllocCache(std::make_shared(nullptr))
+ , env1AllocCache(std::make_shared(nullptr))
#endif
, baseEnv(allocEnv(128))
, staticBaseEnv(new StaticEnv(false, 0))
@@ -490,23 +510,6 @@ EvalState::~EvalState()
}
-void EvalState::requireExperimentalFeatureOnEvaluation(
- const ExperimentalFeature & feature,
- const std::string_view fName,
- const Pos & pos)
-{
- if (!settings.isExperimentalFeatureEnabled(feature)) {
- throw EvalError({
- .msg = hintfmt(
- "Cannot call '%2%' because experimental Nix feature '%1%' is disabled. You can enable it via '--extra-experimental-features %1%'.",
- feature,
- fName
- ),
- .errPos = pos
- });
- }
-}
-
void EvalState::allowPath(const Path & path)
{
if (allowedPaths)
@@ -519,6 +522,14 @@ void EvalState::allowPath(const StorePath & storePath)
allowedPaths->insert(store->toRealPath(storePath));
}
+void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value & v)
+{
+ allowPath(storePath);
+
+ auto path = store->printStorePath(storePath);
+ v.mkString(path, PathSet({path}));
+}
+
Path EvalState::checkSourcePath(const Path & path_)
{
if (!allowedPaths) return path_;
@@ -608,7 +619,7 @@ Path EvalState::toRealPath(const Path & path, const PathSet & context)
}
-Value * EvalState::addConstant(const string & name, Value & v)
+Value * EvalState::addConstant(const std::string & name, Value & v)
{
Value * v2 = allocValue();
*v2 = v;
@@ -617,19 +628,19 @@ Value * EvalState::addConstant(const string & name, Value & v)
}
-void EvalState::addConstant(const string & name, Value * v)
+void EvalState::addConstant(const std::string & name, Value * v)
{
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
- string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
+ auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
}
-Value * EvalState::addPrimOp(const string & name,
+Value * EvalState::addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp)
{
- auto name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
+ auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
Symbol sym = symbols.create(name2);
/* Hack to make constants lazy: turn them into a application of
@@ -677,7 +688,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
}
-Value & EvalState::getBuiltin(const string & name)
+Value & EvalState::getBuiltin(const std::string & name)
{
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
}
@@ -746,7 +757,7 @@ void printEnvBindings(const StaticEnv &se, const Env &env, int lvl)
// for the top level, don't print the double underscore ones; they are in builtins.
for (auto i = se.vars.begin(); i != se.vars.end(); ++i)
{
- if (((string)i->first).substr(0,2) != "__")
+ if (((std::string)i->first).substr(0,2) != "__")
std::cout << i->first << " ";
}
std::cout << ANSI_NORMAL;
@@ -810,7 +821,7 @@ valmap * mapStaticEnvBindings(const StaticEnv &se, const Env &env)
evaluator. So here are some helper functions for throwing
exceptions. */
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, EvalState &evalState))
+LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, EvalState &evalState))
{
auto error = EvalError(s, s2);
@@ -833,11 +844,12 @@ void EvalState::debug_throw(Error e) {
throw e;
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, Env & env, Expr &expr))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2, Env & env, Expr &expr))
{
auto error = EvalError({
- .msg = hintfmt(s),
- .errPos = pos
+ .msg = hintfmt(s, s2),
+ .errPos = pos,
+ .suggestions = suggestions,
});
if (debuggerHook)
@@ -846,7 +858,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, Env &
throw error;
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, EvalState &evalState))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, EvalState &evalState))
{
auto error = EvalError({
.msg = hintfmt(s, s2),
@@ -861,7 +873,20 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
throw error;
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, Env & env, Expr &expr))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, Env & env, Expr &expr))
+{
+ auto error = EvalError({
+ .msg = hintfmt(s),
+ .errPos = pos
+ });
+
+ if (debuggerHook)
+ debuggerHook(&error, env, expr);
+
+ throw error;
+}
+
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, Env & env, Expr &expr))
{
auto error = EvalError({
.msg = hintfmt(s, s2),
@@ -874,7 +899,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
throw error;
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3, EvalState &evalState))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3, EvalState &evalState))
{
auto error = EvalError({
.msg = hintfmt(s, s2, s3),
@@ -889,7 +914,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
throw error;
}
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3, EvalState &evalState))
+LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, const std::string & s3, EvalState &evalState))
{
auto error = EvalError({
.msg = hintfmt(s, s2, s3),
@@ -947,11 +972,13 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
throw error;
}
+// LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v));
+
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2, Env & env, Expr &expr))
{
auto error = TypeError({
.msg = hintfmt(s, fun.showNamePos(), s2),
- .errPos = pos
+ .errPos = pos,
});
if (debuggerHook)
@@ -960,7 +987,21 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
throw error;
}
-LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1, Env & env, Expr &expr))
+LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2, Env & env, Expr &expr))
+{
+ auto error = TypeError({
+ .msg = hintfmt(s, fun.showNamePos(), s2),
+ .errPos = pos,
+ .suggestions = suggestions,
+ });
+
+ if (debuggerHook)
+ debuggerHook(&error, env, expr);
+
+ throw error;
+}
+
+LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1, Env & env, Expr &expr))
{
auto error = AssertionError({
.msg = hintfmt(s, s1),
@@ -973,7 +1014,7 @@ LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s,
throw error;
}
-LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1, Env & env, const Expr &expr))
+LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1, Env & env, const Expr &expr))
{
auto error = UndefinedVarError({
.msg = hintfmt(s, s1),
@@ -986,7 +1027,7 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char *
throw error;
}
-LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1, Env & env, Expr &expr))
+LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1, Env & env, Expr &expr))
{
auto error = MissingArgumentError({
.msg = hintfmt(s, s1),
@@ -999,18 +1040,18 @@ LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char
throw error;
}
-LocalNoInline(void addErrorTrace(Error & e, const char * s, const string & s2))
+LocalNoInline(void addErrorTrace(Error & e, const char * s, const std::string & s2))
{
e.addTrace(std::nullopt, s, s2);
}
-LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const string & s2))
+LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2))
{
e.addTrace(pos, s, s2);
}
LocalNoInline(std::unique_ptr
- makeDebugTraceStacker(EvalState &state, Expr &expr, Env &env, std::optional pos, const char * s, const string & s2))
+ makeDebugTraceStacker(EvalState &state, Expr &expr, Env &env, std::optional pos, const char * s, const std::string & s2))
{
return std::unique_ptr(
new DebugTraceStacker(
@@ -1034,7 +1075,7 @@ DebugTraceStacker::DebugTraceStacker(EvalState &evalState, DebugTrace t)
void Value::mkString(std::string_view s)
{
- mkString(dupStringWithLen(s.data(), s.size()));
+ mkString(makeImmutableString(s));
}
@@ -1065,7 +1106,7 @@ void Value::mkStringMove(const char * s, const PathSet & context)
void Value::mkPath(std::string_view s)
{
- mkPath(dupStringWithLen(s.data(), s.size()));
+ mkPath(makeImmutableString(s));
}
@@ -1095,53 +1136,6 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
}
}
-
-Value * EvalState::allocValue()
-{
- /* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
- GC_malloc_many returns a linked list of objects of the given size, where the first word
- of each object is also the pointer to the next object in the list. This also means that we
- have to explicitly clear the first word of every object we take. */
- if (!*valueAllocCache) {
- *valueAllocCache = GC_malloc_many(sizeof(Value));
- if (!*valueAllocCache) throw std::bad_alloc();
- }
-
- /* GC_NEXT is a convenience macro for accessing the first word of an object.
- Take the first list item, advance the list to the next item, and clear the next pointer. */
- void * p = *valueAllocCache;
- GC_PTR_STORE_AND_DIRTY(&*valueAllocCache, GC_NEXT(p));
- GC_NEXT(p) = nullptr;
-
- nrValues++;
- auto v = (Value *) p;
- return v;
-}
-
-
-Env & EvalState::allocEnv(size_t size)
-{
-
- nrEnvs++;
- nrValuesInEnvs += size;
- Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
- env->type = Env::Plain;
-
- /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
-
- return *env;
-}
-
-Env & fakeEnv(size_t size)
-{
- // making a fake Env so we'll have one to pass to exception ftns.
- // a placeholder until we can pass real envs everywhere they're needed.
- Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
- env->type = Env::Plain;
-
- return *env;
-}
-
void EvalState::mkList(Value & v, size_t size)
{
v.mkList(size);
@@ -1480,7 +1474,7 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v)
}
-static string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath)
+static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
@@ -1531,8 +1525,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
}
} else {
state.forceAttrs(*vAttrs, pos);
- if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
- throwEvalError(pos, "attribute '%1%' missing", name, env, *this);
+ if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
+ std::set allAttrNames;
+ for (auto & attr : *vAttrs->attrs)
+ allAttrNames.insert(attr.name);
+ throwEvalError(
+ pos,
+ Suggestions::bestMatches(allAttrNames, name),
+ "attribute '%1%' missing", name, env, *this);
+ }
}
vAttrs = j->value;
pos2 = j->pos;
@@ -1647,9 +1648,16 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Nope, so show the first unexpected argument to the
user. */
for (auto & i : *args[0]->attrs)
- if (!lambda.formals->has(i.name))
- throwTypeError(pos, "%1% called with unexpected argument '%2%'",
+ if (!lambda.formals->has(i.name)) {
+ std::set formalNames;
+ for (auto & formal : lambda.formals->formals)
+ formalNames.insert(formal.name);
+ throwTypeError(
+ pos,
+ Suggestions::bestMatches(formalNames, i.name),
+ "%1% called with unexpected argument '%2%'",
lambda, i.name, *fun.lambda.env, lambda);
+ }
abort(); // can't happen
}
}
@@ -1664,7 +1672,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
makeDebugTraceStacker(*this, *lambda.body, env2, lambda.pos,
"while evaluating %s",
(lambda.name.set()
- ? "'" + (string) lambda.name + "'"
+ ? "'" + (std::string) lambda.name + "'"
: "anonymous lambda"))
: nullptr;
@@ -1673,7 +1681,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (loggerSettings.showTrace.get()) {
addErrorTrace(e, lambda.pos, "while evaluating %s",
(lambda.name.set()
- ? "'" + (string) lambda.name + "'"
+ ? "'" + (const std::string &) lambda.name + "'"
: "anonymous lambda"));
addErrorTrace(e, pos, "from call site%s", "");
}
@@ -2175,13 +2183,22 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos)
/* Decode a context string ‘!!’ into a pair . */
-std::pair decodeContext(std::string_view s)
+NixStringContextElem decodeContext(const Store & store, std::string_view s)
{
if (s.at(0) == '!') {
size_t index = s.find("!", 1);
- return {std::string(s.substr(index + 1)), std::string(s.substr(1, index - 1))};
+ return {
+ store.parseStorePath(s.substr(index + 1)),
+ std::string(s.substr(1, index - 1)),
+ };
} else
- return {s.at(0) == '/' ? std::string(s) : std::string(s.substr(1)), ""};
+ return {
+ store.parseStorePath(
+ s.at(0) == '/'
+ ? s
+ : s.substr(1)),
+ "",
+ };
}
@@ -2193,13 +2210,13 @@ void copyContext(const Value & v, PathSet & context)
}
-std::vector> Value::getContext()
+NixStringContext Value::getContext(const Store & store)
{
- std::vector> res;
+ NixStringContext res;
assert(internalType == tString);
if (string.context)
for (const char * * p = string.context; *p; ++p)
- res.push_back(decodeContext(*p));
+ res.push_back(decodeContext(store, *p));
return res;
}
@@ -2238,7 +2255,7 @@ bool EvalState::isDerivation(Value & v)
}
-std::optional EvalState::tryAttrsToString(const Pos & pos, Value & v,
+std::optional EvalState::tryAttrsToString(const Pos & pos, Value & v,
PathSet & context, bool coerceMore, bool copyToStore)
{
auto i = v.attrs->find(sToString);
@@ -2293,7 +2310,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
if (v.type() == nNull) return "";
if (v.isList()) {
- string result;
+ std::string result;
for (auto [n, v2] : enumerate(v.listItems())) {
result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
if (n < v.listSize() - 1
@@ -2309,7 +2326,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
}
-string EvalState::copyPathToStore(PathSet & context, const Path & path)
+std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
{
if (nix::isDerivation(path))
throwEvalError("file names are not allowed to end in '%1%'", drvExtension, *this);
@@ -2335,13 +2352,25 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
{
- string path = coerceToString(pos, v, context, false, false).toOwned();
+ auto path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/')
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path, *this);
return path;
}
+StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context)
+{
+ auto path = coerceToString(pos, v, context, false, false).toOwned();
+ if (auto storePath = store->maybeParseStorePath(path))
+ return *storePath;
+ throw EvalError({
+ .msg = hintfmt("path '%1%' is not in the Nix store", path),
+ .errPos = pos
+ });
+}
+
+
bool EvalState::eqValues(Value & v1, Value & v2)
{
forceValue(v1, noPos);
@@ -2508,11 +2537,11 @@ void EvalState::printStats()
for (auto & i : functionCalls) {
auto obj = list.object();
if (i.first->name.set())
- obj.attr("name", (const string &) i.first->name);
+ obj.attr("name", (const std::string &) i.first->name);
else
obj.attr("name", nullptr);
if (i.first->pos) {
- obj.attr("file", (const string &) i.first->pos.file);
+ obj.attr("file", (const std::string &) i.first->pos.file);
obj.attr("line", i.first->pos.line);
obj.attr("column", i.first->pos.column);
}
@@ -2524,7 +2553,7 @@ void EvalState::printStats()
for (auto & i : attrSelects) {
auto obj = list.object();
if (i.first) {
- obj.attr("file", (const string &) i.first.file);
+ obj.attr("file", (const std::string &) i.first.file);
obj.attr("line", i.first.line);
obj.attr("column", i.first.column);
}
@@ -2541,7 +2570,7 @@ void EvalState::printStats()
}
-string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
+std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & 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 1c569fc36..2ced5bea9 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -89,7 +89,7 @@ public:
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString,
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
- sContentAddressed,
+ sContentAddressed, sImpure,
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
@@ -150,9 +150,14 @@ private:
/* Cache used by prim_match(). */
std::shared_ptr regexCache;
+#if HAVE_BOEHMGC
/* Allocation cache for GC'd Value objects. */
std::shared_ptr valueAllocCache;
+ /* Allocation cache for size-1 Env objects. */
+ std::shared_ptr env1AllocCache;
+#endif
+
public:
EvalState(
@@ -161,13 +166,7 @@ public:
std::shared_ptr buildStore = nullptr);
~EvalState();
- void requireExperimentalFeatureOnEvaluation(
- const ExperimentalFeature &,
- const std::string_view fName,
- const Pos & pos
- );
-
- void addToSearchPath(const string & s);
+ void addToSearchPath(const std::string & s);
SearchPath getSearchPath() { return searchPath; }
@@ -178,6 +177,9 @@ public:
the real store path if `store` is a chroot store. */
void allowPath(const StorePath & storePath);
+ /* Allow access to a store path and return it as a string. */
+ void allowAndSetStorePathString(const StorePath & storePath, Value & v);
+
/* Check whether access to a path is allowed and throw an error if
not. Otherwise return the canonicalised path. */
Path checkSourcePath(const Path & path);
@@ -268,7 +270,7 @@ public:
set with attribute `type = "derivation"'). */
bool isDerivation(Value & v);
- std::optional tryAttrsToString(const Pos & pos, Value & v,
+ std::optional tryAttrsToString(const Pos & pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true);
/* String coercion. Converts strings, paths and derivations to a
@@ -279,13 +281,16 @@ public:
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
- string copyPathToStore(PathSet & context, const Path & path);
+ std::string copyPathToStore(PathSet & context, const Path & path);
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
Path coerceToPath(const Pos & pos, Value & v, PathSet & context);
+ /* Like coerceToPath, but the result must be a store path. */
+ StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context);
+
public:
/* The base environment, containing the builtin functions and
@@ -301,18 +306,18 @@ private:
void createBaseEnv();
- Value * addConstant(const string & name, Value & v);
+ Value * addConstant(const std::string & name, Value & v);
- void addConstant(const string & name, Value * v);
+ void addConstant(const std::string & name, Value * v);
- Value * addPrimOp(const string & name,
+ Value * addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp);
Value * addPrimOp(PrimOp && primOp);
public:
- Value & getBuiltin(const string & name);
+ Value & getBuiltin(const std::string & name);
struct Doc
{
@@ -358,8 +363,8 @@ public:
void autoCallFunction(Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */
- Value * allocValue();
- Env & allocEnv(size_t size);
+ inline Value * allocValue();
+ inline Env & allocEnv(size_t size);
Value * allocAttr(Value & vAttrs, const Symbol & name);
Value * allocAttr(Value & vAttrs, std::string_view name);
@@ -442,12 +447,12 @@ class DebugTraceStacker {
};
/* Return a string representing the type of the value `v'. */
-string showType(ValueType type);
-string showType(const Value & v);
+std::string_view showType(ValueType type);
+std::string showType(const Value & v);
/* Decode a context string ‘!!’ into a pair . */
-std::pair decodeContext(std::string_view s);
+NixStringContextElem decodeContext(const Store & store, std::string_view s);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
@@ -531,3 +536,5 @@ extern EvalSettings evalSettings;
static const std::string corepkgsPrefix{"/__corepkgs__/"};
}
+
+#include "eval-inline.hh"
diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc
index 7ecd61816..a811e59a1 100644
--- a/src/libexpr/flake/config.cc
+++ b/src/libexpr/flake/config.cc
@@ -1,5 +1,6 @@
#include "flake.hh"
#include "globals.hh"
+#include "fetch-settings.hh"
#include
@@ -53,7 +54,7 @@ void ConfigFile::apply()
auto trustedList = readTrustedList();
bool trusted = false;
- if (nix::settings.acceptFlakeConfig){
+ if (nix::fetchSettings.acceptFlakeConfig){
trusted = true;
} else if (auto saved = get(get(trustedList, name).value_or(std::map()), valueS)) {
trusted = *saved;
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 9f3b58909..22257c6b3 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -6,6 +6,7 @@
#include "store-api.hh"
#include "fetchers.hh"
#include "finally.hh"
+#include "fetch-settings.hh"
namespace nix {
@@ -254,7 +255,7 @@ static Flake getFlake(
for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos);
if (setting.value->type() == nString)
- flake.config.settings.insert({setting.name, string(state.forceStringNoCtx(*setting.value, *setting.pos))});
+ flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, *setting.pos))});
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
flake.config.settings.emplace(
@@ -315,7 +316,7 @@ LockedFlake lockFlake(
FlakeCache flakeCache;
- auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
+ auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries);
auto flake = getFlake(state, topRef, useRegistries, flakeCache);
@@ -501,7 +502,7 @@ LockedFlake lockFlake(
this input. */
debug("creating new input '%s'", inputPathS);
- if (!lockFlags.allowMutable && !input.ref->input.isImmutable())
+ if (!lockFlags.allowMutable && !input.ref->input.isLocked())
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
if (input.isFlake) {
@@ -591,7 +592,7 @@ LockedFlake lockFlake(
if (lockFlags.writeLockFile) {
if (auto sourcePath = topRef.input.getSourcePath()) {
if (!newLockFile.isImmutable()) {
- if (settings.warnDirty)
+ if (fetchSettings.warnDirty)
warn("will not write lock file of flake '%s' because it has a mutable input", topRef);
} else {
if (!lockFlags.updateLockFile)
@@ -618,7 +619,7 @@ LockedFlake lockFlake(
if (lockFlags.commitLockFile) {
std::string cm;
- cm = settings.commitLockFileSummary.get();
+ cm = fetchSettings.commitLockFileSummary.get();
if (cm == "") {
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
@@ -650,7 +651,7 @@ LockedFlake lockFlake(
now. Corner case: we could have reverted from a
dirty to a clean tree! */
if (flake.lockedRef.input == prevLockedRef.input
- && !flake.lockedRef.input.isImmutable())
+ && !flake.lockedRef.input.isLocked())
throw Error("'%s' did not change after I updated its 'flake.lock' file; is 'flake.lock' under version control?", flake.originalRef);
}
} else
@@ -705,24 +706,45 @@ void callFlake(EvalState & state,
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
-
- string flakeRefS(state.forceStringNoCtx(*args[0], pos));
+ std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
- if (evalSettings.pureEval && !flakeRef.input.isImmutable())
- throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
+ if (evalSettings.pureEval && !flakeRef.input.isLocked())
+ throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
callFlake(state,
lockFlake(state, flakeRef,
LockFlags {
.updateLockFile = false,
- .useRegistries = !evalSettings.pureEval && settings.useRegistries,
+ .useRegistries = !evalSettings.pureEval && fetchSettings.useRegistries,
.allowMutable = !evalSettings.pureEval,
}),
v);
}
-static RegisterPrimOp r2("__getFlake", 1, prim_getFlake);
+static RegisterPrimOp r2({
+ .name = "__getFlake",
+ .args = {"args"},
+ .doc = R"(
+ Fetch a flake from a flake reference, and return its output attributes and some metadata. For example:
+
+ ```nix
+ (builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix
+ ```
+
+ Unless impure evaluation is allowed (`--impure`), the flake reference
+ must be "locked", e.g. contain a Git revision or content hash. An
+ example of an unlocked usage is:
+
+ ```nix
+ (builtins.getFlake "github:edolstra/dwarffs").rev
+ ```
+
+ This function is only available if you enable the experimental feature
+ `flakes`.
+ )",
+ .fun = prim_getFlake,
+ .experimentalFeature = Xp::Flakes,
+});
}
diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc
index 930ed9ccd..c1eae413f 100644
--- a/src/libexpr/flake/flakeref.cc
+++ b/src/libexpr/flake/flakeref.cc
@@ -98,7 +98,7 @@ std::pair parseFlakeRefWithFragment(
if (std::regex_match(url, match, flakeRegex)) {
auto parsedURL = ParsedURL{
.url = url,
- .base = "flake:" + std::string(match[1]),
+ .base = "flake:" + match.str(1),
.scheme = "flake",
.authority = "",
.path = match[1],
@@ -106,12 +106,12 @@ std::pair parseFlakeRefWithFragment(
return std::make_pair(
FlakeRef(Input::fromURL(parsedURL), ""),
- percentDecode(std::string(match[6])));
+ percentDecode(match.str(6)));
}
else if (std::regex_match(url, match, pathUrlRegex)) {
std::string path = match[1];
- std::string fragment = percentDecode(std::string(match[3]));
+ std::string fragment = percentDecode(match.str(3));
if (baseDir) {
/* Check if 'url' is a path (either absolute or relative
diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc
index fda340789..60b52d578 100644
--- a/src/libexpr/flake/lockfile.cc
+++ b/src/libexpr/flake/lockfile.cc
@@ -35,7 +35,7 @@ LockedNode::LockedNode(const nlohmann::json & json)
, originalRef(getFlakeRef(json, "original", nullptr))
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
{
- if (!lockedRef.input.isImmutable())
+ if (!lockedRef.input.isLocked())
throw Error("lockfile contains mutable lock '%s'",
fetchers::attrsToJSON(lockedRef.input.toAttrs()));
}
@@ -220,7 +220,7 @@ bool LockFile::isImmutable() const
for (auto & i : nodes) {
if (i == root) continue;
auto lockedNode = std::dynamic_pointer_cast(i);
- if (lockedNode && !lockedNode->lockedRef.input.isImmutable()) return false;
+ if (lockedNode && !lockedNode->lockedRef.input.isLocked()) return false;
}
return true;
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 5995a857b..bb7e77b61 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -1,6 +1,7 @@
#include "get-drvs.hh"
#include "util.hh"
#include "eval-inline.hh"
+#include "derivations.hh"
#include "store-api.hh"
#include "path-with-outputs.hh"
@@ -11,8 +12,8 @@
namespace nix {
-DrvInfo::DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs)
- : state(&state), attrs(attrs), attrPath(attrPath)
+DrvInfo::DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs)
+ : state(&state), attrs(attrs), attrPath(std::move(attrPath))
{
}
@@ -22,7 +23,7 @@ DrvInfo::DrvInfo(EvalState & state, ref store, const std::string & drvPat
{
auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs);
- this->drvPath = store->printStorePath(drvPath);
+ this->drvPath = drvPath;
auto drv = store->derivationFromPath(drvPath);
@@ -41,13 +42,11 @@ DrvInfo::DrvInfo(EvalState & state, ref store, const std::string & drvPat
throw Error("derivation '%s' does not have output '%s'", store->printStorePath(drvPath), outputName);
auto & [outputName, output] = *i;
- auto optStorePath = output.path(*store, drv.name, outputName);
- if (optStorePath)
- outPath = store->printStorePath(*optStorePath);
+ outPath = {output.path(*store, drv.name, outputName)};
}
-string DrvInfo::queryName() const
+std::string DrvInfo::queryName() const
{
if (name == "" && attrs) {
auto i = attrs->find(state->sName);
@@ -58,7 +57,7 @@ string DrvInfo::queryName() const
}
-string DrvInfo::querySystem() const
+std::string DrvInfo::querySystem() const
{
if (system == "" && attrs) {
auto i = attrs->find(state->sSystem);
@@ -68,24 +67,35 @@ string DrvInfo::querySystem() const
}
-string DrvInfo::queryDrvPath() const
+std::optional DrvInfo::queryDrvPath() const
{
- if (drvPath == "" && attrs) {
+ if (!drvPath && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath);
PathSet context;
- drvPath = i != attrs->end() ? state->coerceToPath(*i->pos, *i->value, context) : "";
+ if (i == attrs->end())
+ drvPath = {std::nullopt};
+ else
+ drvPath = {state->coerceToStorePath(*i->pos, *i->value, context)};
}
- return drvPath;
+ return drvPath.value_or(std::nullopt);
}
-string DrvInfo::queryOutPath() const
+StorePath DrvInfo::requireDrvPath() const
+{
+ if (auto drvPath = queryDrvPath())
+ return *drvPath;
+ throw Error("derivation does not contain a 'drvPath' attribute");
+}
+
+
+StorePath DrvInfo::queryOutPath() const
{
if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context;
if (i != attrs->end())
- outPath = state->coerceToPath(*i->pos, *i->value, context);
+ outPath = state->coerceToStorePath(*i->pos, *i->value, context);
}
if (!outPath)
throw UnimplementedError("CA derivations are not yet supported");
@@ -93,7 +103,7 @@ string DrvInfo::queryOutPath() const
}
-DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
+DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall)
{
if (outputs.empty()) {
/* Get the ‘outputs’ list. */
@@ -103,20 +113,24 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
/* For each output... */
for (auto elem : i->value->listItems()) {
- /* Evaluate the corresponding set. */
- string name(state->forceStringNoCtx(*elem, *i->pos));
- Bindings::iterator out = attrs->find(state->symbols.create(name));
- if (out == attrs->end()) continue; // FIXME: throw error?
- state->forceAttrs(*out->value, *i->pos);
+ std::string output(state->forceStringNoCtx(*elem, *i->pos));
- /* And evaluate its ‘outPath’ attribute. */
- Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
- if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
- PathSet context;
- outputs[name] = state->coerceToPath(*outPath->pos, *outPath->value, context);
+ if (withPaths) {
+ /* Evaluate the corresponding set. */
+ Bindings::iterator out = attrs->find(state->symbols.create(output));
+ if (out == attrs->end()) continue; // FIXME: throw error?
+ state->forceAttrs(*out->value, *i->pos);
+
+ /* And evaluate its ‘outPath’ attribute. */
+ Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
+ if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
+ PathSet context;
+ outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context));
+ } else
+ outputs.emplace(output, std::nullopt);
}
} else
- outputs["out"] = queryOutPath();
+ outputs.emplace("out", withPaths ? std::optional{queryOutPath()} : std::nullopt);
}
if (!onlyOutputsToInstall || !attrs)
return outputs;
@@ -138,7 +152,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
}
-string DrvInfo::queryOutputName() const
+std::string DrvInfo::queryOutputName() const
{
if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutputName);
@@ -190,7 +204,7 @@ bool DrvInfo::checkMeta(Value & v)
}
-Value * DrvInfo::queryMeta(const string & name)
+Value * DrvInfo::queryMeta(const std::string & name)
{
if (!getMeta()) return 0;
Bindings::iterator a = meta->find(state->symbols.create(name));
@@ -199,7 +213,7 @@ Value * DrvInfo::queryMeta(const string & name)
}
-string DrvInfo::queryMetaString(const string & name)
+std::string DrvInfo::queryMetaString(const std::string & name)
{
Value * v = queryMeta(name);
if (!v || v->type() != nString) return "";
@@ -207,7 +221,7 @@ string DrvInfo::queryMetaString(const string & name)
}
-NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
+NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def)
{
Value * v = queryMeta(name);
if (!v) return def;
@@ -221,7 +235,7 @@ NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
return def;
}
-NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
+NixFloat DrvInfo::queryMetaFloat(const std::string & name, NixFloat def)
{
Value * v = queryMeta(name);
if (!v) return def;
@@ -236,7 +250,7 @@ NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
}
-bool DrvInfo::queryMetaBool(const string & name, bool def)
+bool DrvInfo::queryMetaBool(const std::string & name, bool def)
{
Value * v = queryMeta(name);
if (!v) return def;
@@ -251,7 +265,7 @@ bool DrvInfo::queryMetaBool(const string & name, bool def)
}
-void DrvInfo::setMeta(const string & name, Value * v)
+void DrvInfo::setMeta(const std::string & name, Value * v)
{
getMeta();
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
@@ -266,7 +280,7 @@ void DrvInfo::setMeta(const string & name, Value * v)
/* Cache for already considered attrsets. */
-typedef set Done;
+typedef std::set Done;
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
@@ -274,7 +288,7 @@ typedef set Done;
The result boolean indicates whether it makes sense
for the caller to recursively search for derivations in `v'. */
static bool getDerivation(EvalState & state, Value & v,
- const string & attrPath, DrvInfos & drvs, Done & done,
+ const std::string & attrPath, DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures)
{
try {
@@ -311,7 +325,7 @@ std::optional getDerivation(EvalState & state, Value & v,
}
-static string addToPath(const string & s1, const string & s2)
+static std::string addToPath(const std::string & s1, const std::string & s2)
{
return s1.empty() ? s2 : s1 + "." + s2;
}
@@ -321,7 +335,7 @@ static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
static void getDerivations(EvalState & state, Value & vIn,
- const string & pathPrefix, Bindings & autoArgs,
+ const std::string & pathPrefix, Bindings & autoArgs,
DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures)
{
@@ -346,7 +360,7 @@ static void getDerivations(EvalState & state, Value & vIn,
debug("evaluating attribute '%1%'", i->name);
if (!std::regex_match(std::string(i->name), attrRegex))
continue;
- string pathPrefix2 = addToPath(pathPrefix, i->name);
+ std::string pathPrefix2 = addToPath(pathPrefix, i->name);
if (combineChannels)
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
@@ -364,7 +378,7 @@ static void getDerivations(EvalState & state, Value & vIn,
else if (v.type() == nList) {
for (auto [n, elem] : enumerate(v.listItems())) {
- string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
+ std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
@@ -374,7 +388,7 @@ static void getDerivations(EvalState & state, Value & vIn,
}
-void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
+void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix,
Bindings & autoArgs, DrvInfos & drvs, bool ignoreAssertionFailures)
{
Done done;
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index 29bb6a660..7cc1abef2 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -1,6 +1,7 @@
#pragma once
#include "eval.hh"
+#include "path.hh"
#include
#include ]