Compare commits

..
6 Commits
Author SHA1 Message Date
regnat a06cd93324 Split installTests
That way the CI will run the different ones on different runners
2022-02-25 08:33:44 +01:00
regnat 38edf8a0b4 Split the CI in several steps 2022-02-24 20:49:09 +01:00
regnat 55b46a5060 testS: poll more eagerly for the daemon start/stop
Polling every 1 second means that even the simplest test takes at least
2 seconds. We can reasonably poll 1/10 of that to make things much
  quicker (esp. given that most of the time 0.1s is enough for the
  daemon to be started or stopped)
2022-02-24 15:30:05 +01:00
regnat 8442d587ce Allow tracking the start and end time of tests 2022-02-24 15:30:05 +01:00
regnat 1e622145b2 Sort the tests by wall time
The tests are scheduled in the order they appear, so running the long
ones first slightly improves the scheduling.

On my machine, this decreases the time of `make install` from 40s to 36s
2022-02-24 15:30:05 +01:00
regnat 76c36742ee Show the elapsed time for each test
To have an idea of what’s taking time
2022-02-24 15:30:05 +01:00
265 changed files with 2812 additions and 5533 deletions

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
# required to find all branches
+39 -12
View File
@@ -6,18 +6,20 @@ on:
jobs:
tests:
build:
needs: [check_cachix]
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 60
outputs:
flake-outputs-json: ${{ steps.list-outputs.outputs.json }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2.4.0
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v17
- uses: cachix/install-nix-action@v16
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v10
if: needs.check_cachix.outputs.secret == 'true'
@@ -25,7 +27,32 @@ jobs:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix --experimental-features 'nix-command flakes' flake check -L
- run: nix --experimental-features 'nix-command flakes' build -L
- name: List all the tests to run
id: list-outputs
run: scripts/list-tests-flake-outptus-for-gha
test:
needs: [build, check_cachix]
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
outputName: ${{ fromJson(needs.build.outputs.flake-outputs-json) }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2.4.0
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v16
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v10
if: needs.check_cachix.outputs.secret == 'true'
with:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix --experimental-features 'nix-command flakes' build .#checks.$(nix eval --raw --impure --expr builtins.currentSystem).${{ matrix.outputName }} -L
check_cachix:
name: Cachix secret present for installer tests
@@ -40,17 +67,17 @@ jobs:
run: echo "::set-output name=secret::${{ env._CACHIX_SECRETS != '' }}"
installer:
needs: [tests, check_cachix]
needs: [test, check_cachix]
if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true'
runs-on: ubuntu-latest
outputs:
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2.4.0
with:
fetch-depth: 0
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v17
- uses: cachix/install-nix-action@v16
- uses: cachix/cachix-action@v10
with:
name: '${{ env.CACHIX_NAME }}'
@@ -67,26 +94,26 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2.4.0
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v17
- uses: cachix/install-nix-action@v16
with:
install_url: '${{needs.installer.outputs.installerURL}}'
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
- run: nix-instantiate -E 'builtins.currentTime' --eval
docker_push_image:
needs: [check_cachix, tests]
needs: [check_cachix, build]
if: >-
github.event_name == 'push' &&
github.ref_name == 'master' &&
needs.check_cachix.outputs.secret == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2.4.0
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v17
- uses: cachix/install-nix-action@v16
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- run: echo NIX_VERSION="$(nix-instantiate --eval -E '(import ./default.nix).defaultPackage.${builtins.currentSystem}.version' | tr -d \")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v10
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
if: github.repository_owner == 'NixOS'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2.4.0
with:
fetch-depth: 0
- run: bash scripts/check-hydra-status.sh
-1
View File
@@ -90,7 +90,6 @@ 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
+1 -1
View File
@@ -1 +1 @@
2.8.0
2.7.0
+2 -2
View File
@@ -6,9 +6,9 @@ builtins:
concatStrings (map
(name:
let builtin = builtins.${name}; in
"<dt id=\"builtins-${name}\"><a href=\"#builtins-${name}\"><code>${name} "
"<dt><code>${name} "
+ concatStringsSep " " (map (s: "<var>${s}</var>") builtin.args)
+ "</code></a></dt>"
+ "</code></dt>"
+ "<dd>\n\n"
+ builtin.doc
+ "\n\n</dd>"
+1 -2
View File
@@ -6,8 +6,7 @@ options:
concatStrings (map
(name:
let option = options.${name}; in
" - [`${name}`](#conf-${name})"
+ "<p id=\"conf-${name}\"></p>\n\n"
" - `${name}` \n\n"
+ concatStrings (map (s: " ${s}\n") (splitLines option.description)) + "\n\n"
+ (if option.documentDefault
then " **Default:** " + (
-1
View File
@@ -72,7 +72,6 @@ $(d)/builtins.json: $(bindir)/nix
@mv $@.tmp $@
# Generate the HTML manual.
html: $(docdir)/manual/index.html
install: $(docdir)/manual/index.html
# Generate 'nix' manpages.
+1 -2
View File
@@ -71,8 +71,7 @@
- [Hacking](contributing/hacking.md)
- [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md)
- [Release 2.8 (2022-04-19)](release-notes/rl-2.8.md)
- [Release 2.7 (2022-03-07)](release-notes/rl-2.7.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.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)
@@ -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
@@ -84,9 +84,7 @@ The installer will modify `/etc/bashrc`, and `/etc/zshrc` if they exist.
The installer will first back up these files with a `.backup-before-nix`
extension. The installer will also create `/etc/profile.d/nix.sh`.
## Uninstalling
### Linux
You can uninstall Nix with the following commands:
```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
@@ -97,95 +95,15 @@ 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 <a name="sect-macos-installation-change-store-prefix"></a><a name="sect-macos-installation-encrypted-volume"></a><a name="sect-macos-installation-symlink"></a><a name="sect-macos-installation-recommended-notes"></a>
<!-- Note: anchors above to catch permalinks to old explanations -->
-33
View File
@@ -1,33 +0,0 @@
# 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.<system>` → `packages.<system>.default`
* `defaultApps.<system>` → `apps.<system>.default`
* `defaultTemplate` → `templates.default`
* `defaultBundler.<system>` → `bundlers.<system>.default`
* `overlay` → `overlays.default`
* `devShell.<system>` → `devShells.<system>.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.<system>.<name>= 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 <template>`.
-53
View File
@@ -1,53 +0,0 @@
# Release 2.8 (2022-04-19)
* New experimental command: `nix fmt`, which applies a formatter
defined by the `formatter.<system>` flake output to the Nix
expressions in a flake.
* Various Nix commands can now read expressions from standard input
using `--file -`.
* 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.
* `nix store make-content-addressable` has been renamed to `nix store
make-content-addressed`.
* 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.
* `nix run` is now stricter in what it accepts: members of the `apps`
flake output are now required to be apps (as defined in [the
manual](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-run.html#apps)),
and members of `packages` or `legacyPackages` must be derivations
(not apps).
+14
View File
@@ -1 +1,15 @@
# Release X.Y (202?-??-??)
* `nix bundle` breaking API change now supports bundlers of the form
`bundler.<system>.<name>= 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 <template>`.
Generated
+3 -3
View File
@@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1645296114,
"narHash": "sha256-y53N7TyIkXsjMpOG7RhvqJFGDacLs9HlyHeSTBioqYU=",
"lastModified": 1632864508,
"narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "530a53dcbc9437363471167a5e4762c5fcfa34a1",
"rev": "82891b5e2c2359d7e58d08849e4c89511ab94234",
"type": "github"
},
"original": {
+16 -14
View File
@@ -15,7 +15,7 @@
then ""
else "pre${builtins.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")}_${self.shortRev or "dirty"}";
officialRelease = true;
officialRelease = false;
linux64BitSystems = [ "x86_64-linux" "aarch64-linux" ];
linuxSystems = linux64BitSystems ++ [ "i686-linux" ];
@@ -533,26 +533,28 @@
nixpkgs = nixpkgs-regression;
};
installTests = forAllSystems (system:
installTestsAgainstSelf = forAllSystems (system:
let pkgs = nixpkgsFor.${system}; in
pkgs.runCommand "install-tests" {
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
againstCurrentUnstable =
# FIXME: temporarily disable this on macOS because of #3605.
if system == "x86_64-linux"
then testNixVersions pkgs pkgs.nix pkgs.nixUnstable
else null;
# Disabled because the latest stable version doesn't handle
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
} "touch $out");
testNixVersions pkgs pkgs.nix pkgs.pkgs.nix
);
installTestsAgainstCurrentUnstable = forAllSystems (system:
let pkgs = nixpkgsFor.${system}; in
# FIXME: temporarily disable this on macOS because of #3605.
if system == "x86_64-linux"
then testNixVersions pkgs pkgs.nix pkgs.nixUnstable
else pkgs.writeText "dummy" "dummy"
);
# Disabled because the latest stable version doesn't handle
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
};
checks = forAllSystems (system: {
binaryTarball = self.hydraJobs.binaryTarball.${system};
perlBindings = self.hydraJobs.perlBindings.${system};
installTests = self.hydraJobs.installTests.${system};
installTestsAgainstCurrentUnstable = self.hydraJobs.installTestsAgainstCurrentUnstable.${system};
installTestsAgainstSelf = self.hydraJobs.installTestsAgainstSelf.${system};
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
dockerImage = self.hydraJobs.dockerImage.${system};
});
+1 -1
View File
@@ -15,7 +15,7 @@ function _complete_nix {
else
COMPREPLY+=("$completion")
fi
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}" 2>/dev/null)
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}" 2>/dev/null)
__ltrim_colon_completions "$cur"
}
+1 -2
View File
@@ -1,8 +1,7 @@
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 $(d)/nix-daemon.conf
clean-files += $(d)/nix-daemon.socket $(d)/nix-daemon.service
endif
-1
View File
@@ -1 +0,0 @@
d @localstatedir@/nix/daemon-socket 0755 root root - -
-2
View File
@@ -1,9 +1,7 @@
[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]
+13 -22
View File
@@ -7,6 +7,8 @@ green=""
yellow=""
normal=""
TESTS_TIMER_LOG=${TESTS_TIMER_LOG:-/dev/null}
post_run_msg="ran test $1..."
if [ -t 1 ]; then
red=""
@@ -14,33 +16,22 @@ if [ -t 1 ]; then
yellow=""
normal=""
fi
(cd tests && env ${TESTS_ENVIRONMENT} init.sh 2>/dev/null > /dev/null)
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
start_time=$(date -u +%s)
echo "$(date -u +%s) $1 start" >> "$TESTS_TIMER_LOG"
log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)"
status=$?
echo "$(date -u +%s) $1 stop" >> "$TESTS_TIMER_LOG"
stop_time=$(date -u +%s)
elapsed_time=$(($stop_time-$start_time))
if [ $status -eq 0 ]; then
echo "$post_run_msg [${green}PASS$normal]"
echo "$post_run_msg [${green}PASS$normal] in ${elapsed_time}s"
elif [ $status -eq 99 ]; then
echo "$post_run_msg [${yellow}SKIP$normal]"
echo "$post_run_msg [${yellow}SKIP$normal] after ${elapsed_time}s"
else
echo "$post_run_msg [${red}FAIL$normal]"
echo "$post_run_msg [${red}FAIL$normal] in ${elapsed_time}s"
echo "$log" | sed 's/^/ /'
exit "$status"
fi
+1 -1
View File
@@ -240,7 +240,7 @@ SV * convertHash(char * algo, char * s, int toBase32)
PPCODE:
try {
auto h = Hash::parseAny(s, parseHashType(algo));
auto s = h.to_string(toBase32 ? Base32 : Base16, false);
string s = h.to_string(toBase32 ? Base32 : Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
+1 -1
View File
@@ -14,7 +14,7 @@ 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 --fail -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
buildInfo=$(curl -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
finished=$(echo "$buildInfo" | jq -r '.finished')
+4 -10
View File
@@ -246,8 +246,7 @@ get_volume_pass() {
verify_volume_pass() {
local volume_special="$1" # (i.e., disk1s7)
local volume_uuid="$2"
_sudo "to confirm the password actually unlocks the volume" \
/usr/sbin/diskutil apfs unlockVolume "$volume_special" -verify -stdinpassphrase -user "$volume_uuid"
/usr/sbin/diskutil apfs unlockVolume "$volume_special" -verify -stdinpassphrase -user "$volume_uuid"
}
volume_pass_works() {
@@ -686,27 +685,22 @@ 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.
_sudo "to mount your Nix volume for encrypting" \
/usr/sbin/diskutil mount "$volume_label"
/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 <<EOF
add-generic-password -a "$volume_label" -s "$volume_uuid" -l "$volume_label encryption password" -D "Encrypted volume password" -j "Added automatically by the Nix installer for use by $NIX_VOLUME_MOUNTD_DEST" -w "$password" -T /System/Library/CoreServices/APFSUserAgent -T /System/Library/CoreServices/CSUserAgent -T /usr/bin/security "/Library/Keychains/System.keychain"
EOF
builtin printf "%s" "$password" | _sudo "to actually encrypt your Nix volume" \
builtin printf "%s" "$password" | _sudo "to encrypt your Nix volume" \
/usr/sbin/diskutil apfs encryptVolume "$volume_label" -user disk -stdinpassphrase
_sudo "to unmount the encrypted volume" \
/usr/sbin/diskutil unmount force "$volume_label"
/usr/sbin/diskutil unmount force "$volume_label"
}
create_volume() {
+3 -15
View File
@@ -23,10 +23,10 @@ readonly RED='\033[31m'
# installer allows overriding build user count to speed up installation
# as creating each user takes non-trivial amount of time on macos
readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32}
readonly NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}"
readonly NIX_BUILD_GROUP_ID="30000"
readonly NIX_BUILD_GROUP_NAME="nixbld"
# darwin installer needs to override these
NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
NIX_FIRST_BUILD_UID="30001"
NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
# Please don't change this. We don't support it, because the
# default shell profile that comes with Nix doesn't support it.
@@ -423,18 +423,6 @@ EOF
fi
done
if [ "$(uname -s)" = "Linux" ] && [ ! -e /run/systemd/system ]; then
warning <<EOF
We did not detect systemd on your system. With a multi-user install
without systemd you will have to manually configure your init system to
launch the Nix daemon after installation.
EOF
if ! ui_confirm "Do you want to proceed with a multi-user installation?"; then
failure <<EOF
You have aborted the installation.
EOF
fi
fi
}
setup_report() {
@@ -751,7 +739,7 @@ install_from_extracted_nix() {
cd "$EXTRACTED_NIX_PATH"
_sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \
cp -RPp ./store/* "$NIX_ROOT/store/"
cp -RLp ./store/* "$NIX_ROOT/store/"
_sudo "to make the new store non-writable at $NIX_ROOT/store" \
chmod -R ugo-w "$NIX_ROOT/store/"
-9
View File
@@ -9,8 +9,6 @@ readonly SERVICE_DEST=/etc/systemd/system/nix-daemon.service
readonly SOCKET_SRC=/lib/systemd/system/nix-daemon.socket
readonly SOCKET_DEST=/etc/systemd/system/nix-daemon.socket
readonly TMPFILES_SRC=/lib/tmpfiles.d/nix-daemon.conf
readonly TMPFILES_DEST=/etc/tmpfiles.d/nix-daemon.conf
# Path for the systemd override unit file to contain the proxy settings
readonly SERVICE_OVERRIDE=${SERVICE_DEST}.d/override.conf
@@ -85,13 +83,6 @@ EOF
poly_configure_nix_daemon_service() {
if [ -e /run/systemd/system ]; then
task "Setting up the nix-daemon systemd service"
_sudo "to create the nix-daemon tmpfiles config" \
ln -sfn /nix/var/nix/profiles/default/$TMPFILES_SRC $TMPFILES_DEST
_sudo "to run systemd-tmpfiles once to pick that path up" \
systemd-tmpfiles --create --prefix=/nix/var/nix
_sudo "to set up the nix-daemon service" \
systemctl link "/nix/var/nix/profiles/default$SERVICE_SRC"
+1 -1
View File
@@ -82,7 +82,7 @@ if [ "$(uname -s)" != "Darwin" ]; then
fi
if command -v curl > /dev/null 2>&1; then
fetch() { curl --fail -L "$1" -o "$2"; }
fetch() { curl -L "$1" -o "$2"; }
elif command -v wget > /dev/null 2>&1; then
fetch() { wget "$1" -O "$2"; }
else
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
set -x
nix build --inputs-from . nixpkgs#jq -o jq
TEST_NAMES=$(nix flake show . --json | ./jq-bin/bin/jq -c ".checks[\"$(nix eval --raw --impure --expr builtins.currentSystem)\"] | keys")
echo "::set-output name=json::$TEST_NAMES"
+6 -7
View File
@@ -14,7 +14,6 @@
#include "pathlocks.hh"
#include "globals.hh"
#include "serialise.hh"
#include "build-result.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "local-store.hh"
@@ -33,7 +32,7 @@ std::string escapeUri(std::string uri)
return uri;
}
static std::string currentLoad;
static string currentLoad;
static AutoCloseFD openSlotLock(const Machine & m, uint64_t slot)
{
@@ -98,7 +97,7 @@ static int main_build_remote(int argc, char * * argv)
}
std::optional<StorePath> drvPath;
std::string storeUri;
string storeUri;
while (true) {
@@ -184,7 +183,7 @@ static int main_build_remote(int argc, char * * argv)
else
{
// build the hint template.
std::string errorText =
string errorText =
"Failed to find a machine for remote build!\n"
"derivation: %s\nrequired (system, features): (%s, %s)";
errorText += "\n%s available machines:";
@@ -194,7 +193,7 @@ static int main_build_remote(int argc, char * * argv)
errorText += "\n(%s, %s, %s, %s)";
// add the template values.
std::string drvstr;
string drvstr;
if (drvPath.has_value())
drvstr = drvPath->to_string();
else
@@ -209,7 +208,7 @@ static int main_build_remote(int argc, char * * argv)
for (auto & m : machines)
error
% concatStringsSep<std::vector<std::string>>(", ", m.systemTypes)
% concatStringsSep<std::vector<string>>(", ", m.systemTypes)
% m.maxJobs
% concatStringsSep<StringSet>(", ", m.supportedFeatures)
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
@@ -300,7 +299,7 @@ connected:
std::set<Realisation> missingRealisations;
StorePathSet missingPaths;
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) {
for (auto & outputName : wantedOutputs) {
auto thisOutputHash = outputHashes.at(outputName);
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
+2 -3
View File
@@ -153,7 +153,7 @@ void BuiltPathsCommand::run(ref<Store> store)
for (auto & p : store->queryAllValidPaths())
paths.push_back(BuiltPath::Opaque{p});
} else {
paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
paths = toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
if (recursive) {
// XXX: This only computes the store path closure, ignoring
// intermediate realisations
@@ -204,8 +204,7 @@ 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("kak") != std::string::npos))
editor.find("vim") != std::string::npos))
args.push_back(fmt("+%d", pos.line));
args.push_back(pos.file);
return args;
+57 -4
View File
@@ -5,6 +5,7 @@
#include "common-eval-args.hh"
#include "path.hh"
#include "flake/lockfile.hh"
#include "store-api.hh"
#include <optional>
@@ -81,16 +82,23 @@ 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<Path> file;
std::optional<std::string> expr;
bool readOnlyMode = false;
// FIXME: move this; not all commands (e.g. 'nix run') use it.
OperateOn operateOn = OperateOn::Output;
SourceExprCommand(bool supportReadOnlyMode = false);
SourceExprCommand();
std::vector<std::shared_ptr<Installable>> parseInstallables(
ref<Store> store, std::vector<std::string> ss);
@@ -105,6 +113,19 @@ 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
@@ -129,13 +150,13 @@ struct InstallableCommand : virtual Args, SourceExprCommand
{
std::shared_ptr<Installable> installable;
InstallableCommand(bool supportReadOnlyMode = false);
InstallableCommand();
void prepare() override;
std::optional<FlakeRef> getFlakeRefForCompletion() override
{
return parseFlakeRefWithFragment(_installable, absPath(".")).first;
return parseFlakeRef(_installable, absPath("."));
}
private:
@@ -217,6 +238,38 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
}
BuiltPaths build(
ref<Store> evalStore,
ref<Store> store, Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode = bmNormal);
std::set<StorePath> toStorePaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
const std::vector<std::shared_ptr<Installable>> & installables);
StorePath toStorePath(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
std::shared_ptr<Installable> installable);
std::set<StorePath> toDerivations(
ref<Store> store,
const std::vector<std::shared_ptr<Installable>> & installables,
bool useDeriver = false);
BuiltPaths toBuiltPaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
const std::vector<std::shared_ptr<Installable>> & installables);
/* Helper function to generate args that invoke $EDITOR on
filename:lineno. */
Strings editorFor(const Pos & pos);
+130 -207
View File
@@ -1,6 +1,4 @@
#include "globals.hh"
#include "installables.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
@@ -14,7 +12,6 @@
#include "eval-cache.hh"
#include "url.hh"
#include "registry.hh"
#include "build-result.hh"
#include <regex>
#include <queue>
@@ -101,14 +98,6 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.inputOverrides.insert_or_assign(
flake::parseInputPath(inputPath),
parseFlakeRef(flakeRef, absPath("."), true));
}},
.completer = {[&](size_t n, std::string_view prefix) {
if (n == 0) {
if (auto flakeRef = getFlakeRefForCompletion())
completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
} else if (n == 1) {
completeFlakeRef(getEvalState()->store, prefix);
}
}}
});
@@ -139,14 +128,12 @@ MixFlakeOptions::MixFlakeOptions()
});
}
SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
SourceExprCommand::SourceExprCommand()
{
addFlag({
.longName = "file",
.shortName = 'f',
.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.",
.description = "Interpret installables as attribute paths relative to the Nix expression stored in *file*.",
.category = installablesCategory,
.labels = {"file"},
.handler = {&file},
@@ -167,25 +154,11 @@ SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
.category = installablesCategory,
.handler = {&operateOn, OperateOn::Derivation},
});
if (supportReadOnlyMode) {
addFlag({
.longName = "read-only",
.description =
"Do not instantiate each evaluated derivation. "
"This improves performance, but can cause errors when accessing "
"store paths of derivations during evaluation.",
.handler = {&readOnlyMode, true},
});
}
}
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
{
return {
"packages." + settings.thisSystem.get() + ".default",
"defaultPackage." + settings.thisSystem.get()
};
return {"defaultPackage." + settings.thisSystem.get()};
}
Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
@@ -203,8 +176,6 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
void SourceExprCommand::completeInstallable(std::string_view prefix)
{
if (file) {
completionType = ctAttrs;
evalSettings.pureEval = false;
auto state = getEvalState();
Expr *e = state->parseExprFromFile(
@@ -233,14 +204,13 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
Value v2;
state->autoCallFunction(*autoArgs, v1, v2);
completionType = ctAttrs;
if (v2.type() == nAttrs) {
for (auto & i : *v2.attrs) {
std::string name = i.name;
if (name.find(searchWord) == 0) {
if (prefix_ == "")
completions->add(name);
else
completions->add(prefix_ + "." + name);
completions->add(i.name);
}
}
}
@@ -268,11 +238,10 @@ void completeFlakeRefWithFragment(
if (hash == std::string::npos) {
completeFlakeRef(evalState->store, prefix);
} else {
completionType = ctAttrs;
auto fragment = prefix.substr(hash + 1);
auto flakeRefS = std::string(prefix.substr(0, hash));
auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
// FIXME: do tilde expansion.
auto flakeRef = parseFlakeRef(flakeRefS, absPath("."));
auto evalCache = openEvalCache(*evalState,
std::make_shared<flake::LockedFlake>(lockFlake(*evalState, flakeRef, lockFlags)));
@@ -284,6 +253,8 @@ void completeFlakeRefWithFragment(
flake. */
attrPathPrefixes.push_back("");
completionType = ctAttrs;
for (auto & attrPathPrefixS : attrPathPrefixes) {
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
auto attrPathS = attrPathPrefixS + std::string(fragment);
@@ -298,9 +269,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));
@@ -357,16 +328,16 @@ DerivedPath Installable::toDerivedPath()
return std::move(buildables[0]);
}
std::vector<ref<eval_cache::AttrCursor>>
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
Installable::getCursors(EvalState & state)
{
auto evalCache =
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state,
[&]() { return toValue(state).first; });
return {evalCache->getRoot()};
return {{evalCache->getRoot(), ""}};
}
ref<eval_cache::AttrCursor>
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
Installable::getCursor(EvalState & state)
{
auto cursors = getCursors(state);
@@ -494,10 +465,11 @@ std::vector<InstallableValue::DerivationInfo> InstallableAttrPath::toDerivations
std::vector<DerivationInfo> res;
for (auto & drvInfo : drvInfos) {
auto drvPath = drvInfo.queryDrvPath();
if (!drvPath)
throw Error("'%s' is not a derivation", what());
res.push_back({ *drvPath, drvInfo.queryOutputName() });
res.push_back({
state->store->parseStorePath(drvInfo.queryDrvPath()),
state->store->maybeParseStorePath(drvInfo.queryOutPath()),
drvInfo.queryOutputName()
});
}
return res;
@@ -589,21 +561,37 @@ InstallableFlake::InstallableFlake(
std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation()
{
auto attr = getCursor(*state);
auto lockedFlake = getLockedFlake();
auto attrPath = attr->getAttrPathStr();
auto cache = openEvalCache(*state, lockedFlake);
auto root = cache->getRoot();
if (!attr->isDerivation())
throw Error("flake output attribute '%s' is not a derivation", attrPath);
for (auto & attrPath : getActualAttrPaths()) {
debug("trying flake output attribute '%s'", attrPath);
auto drvPath = attr->forceDerivation();
auto attr = root->findAlongAttrPath(
parseAttrPath(*state, attrPath),
true
);
auto drvInfo = DerivationInfo {
std::move(drvPath),
attr->getAttr(state->sOutputName)->getString()
};
if (!attr) continue;
return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
if (!attr->isDerivation())
throw Error("flake output attribute '%s' is not a derivation", attrPath);
auto drvPath = attr->forceDerivation();
auto drvInfo = DerivationInfo{
std::move(drvPath),
state->store->maybeParseStorePath(attr->getAttr(state->sOutPath)->getString()),
attr->getAttr(state->sOutputName)->getString()
};
return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
}
throw Error("flake '%s' does not provide attribute %s",
flakeRef, showAttrPaths(getActualAttrPaths()));
}
std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
@@ -615,10 +603,26 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
{
return {&getCursor(state)->forceValue(), noPos};
auto lockedFlake = getLockedFlake();
auto vOutputs = getFlakeOutputs(state, *lockedFlake);
auto emptyArgs = state.allocBindings(0);
for (auto & attrPath : getActualAttrPaths()) {
try {
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
state.forceValue(*v, pos);
return {v, pos};
} catch (AttrPathNotFound & e) {
}
}
throw Error("flake '%s' does not provide attribute %s",
flakeRef, showAttrPaths(getActualAttrPaths()));
}
std::vector<ref<eval_cache::AttrCursor>>
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
InstallableFlake::getCursors(EvalState & state)
{
auto evalCache = openEvalCache(state,
@@ -626,55 +630,21 @@ InstallableFlake::getCursors(EvalState & state)
auto root = evalCache->getRoot();
std::vector<ref<eval_cache::AttrCursor>> res;
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> res;
for (auto & attrPath : getActualAttrPaths()) {
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
if (attr) res.push_back(ref(*attr));
if (attr) res.push_back({attr, attrPath});
}
return res;
}
ref<eval_cache::AttrCursor> InstallableFlake::getCursor(EvalState & state)
{
auto lockedFlake = getLockedFlake();
auto cache = openEvalCache(state, lockedFlake);
auto root = cache->getRoot();
Suggestions suggestions;
auto attrPaths = getActualAttrPaths();
for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath);
auto attrOrSuggestions = root->findAlongAttrPath(
parseAttrPath(state, attrPath),
true
);
if (!attrOrSuggestions) {
suggestions += attrOrSuggestions.getSuggestions();
continue;
}
return *attrOrSuggestions;
}
throw Error(
suggestions,
"flake '%s' does not provide attribute %s",
flakeRef,
showAttrPaths(attrPaths));
}
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
{
flake::LockFlags lockFlagsApplyConfig = lockFlags;
lockFlagsApplyConfig.applyNixConfig = true;
if (!_lockedFlake) {
flake::LockFlags lockFlagsApplyConfig = lockFlags;
lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
}
return _lockedFlake;
@@ -699,10 +669,6 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
{
std::vector<std::shared_ptr<Installable>> result;
if (readOnlyMode) {
settings.readOnlyMode = true;
}
if (file || expr) {
if (file && expr)
throw UsageError("'--file' and '--expr' are exclusive");
@@ -713,10 +679,7 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
auto state = getEvalState();
auto vFile = state->allocValue();
if (file == "-") {
auto e = state->parseStdin();
state->eval(e, *vFile);
} else if (file)
if (file)
state->evalFile(lookupFileArg(*state, *file), *vFile);
else {
auto e = state->parseExprFromString(*expr, absPath("."));
@@ -772,20 +735,56 @@ std::shared_ptr<Installable> SourceExprCommand::parseInstallable(
return installables.front();
}
BuiltPaths Installable::build(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode)
BuiltPaths getBuiltPaths(ref<Store> evalStore, ref<Store> store, const DerivedPaths & hopefullyBuiltPaths)
{
BuiltPaths res;
for (auto & [_, builtPath] : build2(evalStore, store, mode, installables, bMode))
res.push_back(builtPath);
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());
return res;
}
std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::build2(
BuiltPaths build(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -796,96 +795,21 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
settings.readOnlyMode = true;
std::vector<DerivedPath> pathsToBuild;
std::map<DerivedPath, std::vector<std::shared_ptr<Installable>>> backmap;
for (auto & i : installables) {
for (auto b : i->toDerivedPaths()) {
pathsToBuild.push_back(b);
backmap[b].push_back(i);
}
auto b = i->toDerivedPaths();
pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end());
}
std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> res;
switch (mode) {
case Realise::Nothing:
case Realise::Derivation:
if (mode == Realise::Nothing || mode == Realise::Derivation)
printMissing(store, pathsToBuild, lvlError);
else if (mode == Realise::Outputs)
store->buildPaths(pathsToBuild, bMode, evalStore);
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<std::string, StorePath> 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;
return getBuiltPaths(evalStore, store, pathsToBuild);
}
BuiltPaths Installable::toBuiltPaths(
BuiltPaths toBuiltPaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -893,19 +817,19 @@ BuiltPaths Installable::toBuiltPaths(
const std::vector<std::shared_ptr<Installable>> & installables)
{
if (operateOn == OperateOn::Output)
return Installable::build(evalStore, store, mode, installables);
return build(evalStore, store, mode, installables);
else {
if (mode == Realise::Nothing)
settings.readOnlyMode = true;
BuiltPaths res;
for (auto & drvPath : Installable::toDerivations(store, installables, true))
for (auto & drvPath : toDerivations(store, installables, true))
res.push_back(BuiltPath::Opaque{drvPath});
return res;
}
}
StorePathSet Installable::toStorePaths(
StorePathSet toStorePaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode, OperateOn operateOn,
@@ -919,7 +843,7 @@ StorePathSet Installable::toStorePaths(
return outPaths;
}
StorePath Installable::toStorePath(
StorePath toStorePath(
ref<Store> evalStore,
ref<Store> store,
Realise mode, OperateOn operateOn,
@@ -933,7 +857,7 @@ StorePath Installable::toStorePath(
return *paths.begin();
}
StorePathSet Installable::toDerivations(
StorePathSet toDerivations(
ref<Store> store,
const std::vector<std::shared_ptr<Installable>> & installables,
bool useDeriver)
@@ -970,7 +894,7 @@ InstallablesCommand::InstallablesCommand()
void InstallablesCommand::prepare()
{
if (_installables.empty() && useDefaultInstallables())
// FIXME: commands like "nix profile install" should not have a
// FIXME: commands like "nix install" should not have a
// default, probably.
_installables.push_back(".");
installables = parseInstallables(getStore(), _installables);
@@ -980,14 +904,13 @@ std::optional<FlakeRef> InstallablesCommand::getFlakeRefForCompletion()
{
if (_installables.empty()) {
if (useDefaultInstallables())
return parseFlakeRefWithFragment(".", absPath(".")).first;
return parseFlakeRef(".", absPath("."));
return {};
}
return parseFlakeRefWithFragment(_installables.front(), absPath(".")).first;
return parseFlakeRef(_installables.front(), absPath("."));
}
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
: SourceExprCommand(supportReadOnlyMode)
InstallableCommand::InstallableCommand()
{
expectArgs({
.label = "installable",
+4 -71
View File
@@ -5,7 +5,6 @@
#include "path-with-outputs.hh"
#include "derived-path.hh"
#include "eval.hh"
#include "store-api.hh"
#include "flake/flake.hh"
#include <optional>
@@ -30,27 +29,6 @@ struct UnresolvedApp
App resolve(ref<Store> evalStore, ref<Store> 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() { }
@@ -80,56 +58,16 @@ struct Installable
return {};
}
virtual std::vector<ref<eval_cache::AttrCursor>>
virtual std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
getCursors(EvalState & state);
virtual ref<eval_cache::AttrCursor>
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
getCursor(EvalState & state);
virtual FlakeRef nixpkgsFlakeRef() const
{
return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}});
}
static BuiltPaths build(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode = bmNormal);
static std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> build2(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode = bmNormal);
static std::set<StorePath> toStorePaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
const std::vector<std::shared_ptr<Installable>> & installables);
static StorePath toStorePath(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
std::shared_ptr<Installable> installable);
static std::set<StorePath> toDerivations(
ref<Store> store,
const std::vector<std::shared_ptr<Installable>> & installables,
bool useDeriver = false);
static BuiltPaths toBuiltPaths(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
OperateOn operateOn,
const std::vector<std::shared_ptr<Installable>> & installables);
};
struct InstallableValue : Installable
@@ -141,6 +79,7 @@ struct InstallableValue : Installable
struct DerivationInfo
{
StorePath drvPath;
std::optional<StorePath> outPath;
std::string outputName;
};
@@ -180,15 +119,9 @@ struct InstallableFlake : InstallableValue
std::pair<Value *, Pos> toValue(EvalState & state) override;
/* Get a cursor to every attrpath in getActualAttrPaths() that
exists. */
std::vector<ref<eval_cache::AttrCursor>>
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
getCursors(EvalState & state) override;
/* Get a cursor to the first attrpath in getActualAttrPaths() that
exists, or throw an exception with suggestions if none exists. */
ref<eval_cache::AttrCursor> getCursor(EvalState & state) override;
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
FlakeRef nixpkgsFlakeRef() const override;
+5 -11
View File
@@ -9,7 +9,7 @@ namespace nix {
static Strings parseAttrPath(std::string_view s)
{
Strings res;
std::string cur;
string cur;
auto i = s.begin();
while (i != s.end()) {
if (*i == '.') {
@@ -41,7 +41,7 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
}
std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string & attrPath,
std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attrPath,
Bindings & autoArgs, Value & vIn)
{
Strings tokens = parseAttrPath(attrPath);
@@ -74,14 +74,8 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string &
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()) {
std::set<std::string> 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);
}
if (a == v->attrs->end())
throw AttrPathNotFound("attribute '%1%' in selection path '%2%' not found", attr, attrPath);
v = &*a->value;
pos = *a->pos;
}
@@ -127,7 +121,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, std::string::npos));
lineno = std::stoi(std::string(pos, colon + 1, string::npos));
} catch (std::invalid_argument & e) {
throw ParseError("cannot parse line number '%s'", pos);
}
+2 -5
View File
@@ -10,11 +10,8 @@ namespace nix {
MakeError(AttrPathNotFound, Error);
MakeError(NoPositionInfo, Error);
std::pair<Value *, Pos> findAlongAttrPath(
EvalState & state,
const std::string & attrPath,
Bindings & autoArgs,
Value & vIn);
std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const 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);
+1 -1
View File
@@ -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 std::string &) a->name < (const std::string &) b->name;
return (const string &) a->name < (const string &) b->name;
});
return res;
}
@@ -7,7 +7,6 @@
#include "registry.hh"
#include "flake/flakeref.hh"
#include "store-api.hh"
#include "command.hh"
namespace nix {
@@ -60,9 +59,6 @@ MixEvalArgs::MixEvalArgs()
fetchers::Attrs extraAttrs;
if (to.subdir != "") extraAttrs["dir"] = to.subdir;
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
}},
.completer = {[&](size_t, std::string_view prefix) {
completeFlakeRef(openStore(), prefix);
}}
});
@@ -81,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(i.second.substr(1), absPath(".")));
state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
else
v->mkString(((std::string_view) i.second).substr(1));
res.insert(state.symbols.create(i.first), v);
@@ -89,17 +85,17 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
return res.finish();
}
Path lookupFileArg(EvalState & state, std::string_view s)
Path lookupFileArg(EvalState & state, string 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(std::string(s));
return absPath(s);
}
}
@@ -22,6 +22,6 @@ private:
std::map<std::string, std::string> autoArgs;
};
Path lookupFileArg(EvalState & state, std::string_view s);
Path lookupFileArg(EvalState & state, string s);
}
+20 -42
View File
@@ -21,8 +21,6 @@ struct AttrDb
{
std::atomic_bool failed{false};
const Store & cfg;
struct State
{
SQLite db;
@@ -35,9 +33,8 @@ struct AttrDb
std::unique_ptr<Sync<State>> _state;
AttrDb(const Store & cfg, const Hash & fingerprint)
: cfg(cfg)
, _state(std::make_unique<Sync<State>>())
AttrDb(const Hash & fingerprint)
: _state(std::make_unique<Sync<State>>())
{
auto state(_state->lock());
@@ -257,10 +254,10 @@ struct AttrDb
return {{rowId, attrs}};
}
case AttrType::String: {
NixStringContext context;
std::vector<std::pair<Path, std::string>> context;
if (!queryAttribute.isNull(3))
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
context.push_back(decodeContext(cfg, s));
context.push_back(decodeContext(s));
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
}
case AttrType::Bool:
@@ -277,10 +274,10 @@ struct AttrDb
}
};
static std::shared_ptr<AttrDb> makeAttrDb(const Store & cfg, const Hash & fingerprint)
static std::shared_ptr<AttrDb> makeAttrDb(const Hash & fingerprint)
{
try {
return std::make_shared<AttrDb>(cfg, fingerprint);
return std::make_shared<AttrDb>(fingerprint);
} catch (SQLiteError &) {
ignoreException();
return nullptr;
@@ -291,7 +288,7 @@ EvalCache::EvalCache(
std::optional<std::reference_wrapper<const Hash>> useCache,
EvalState & state,
RootLoader rootLoader)
: db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr)
: db(useCache ? makeAttrDb(*useCache) : nullptr)
, state(state)
, rootLoader(rootLoader)
{
@@ -306,9 +303,9 @@ Value * EvalCache::getRootValue()
return *value;
}
ref<AttrCursor> EvalCache::getRoot()
std::shared_ptr<AttrCursor> EvalCache::getRoot()
{
return make_ref<AttrCursor>(ref(shared_from_this()), std::nullopt);
return std::make_shared<AttrCursor>(ref(shared_from_this()), std::nullopt);
}
AttrCursor::AttrCursor(
@@ -409,16 +406,6 @@ Value & AttrCursor::forceValue()
return v;
}
Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
{
auto attrNames = getAttrs();
std::set<std::string> strAttrNames;
for (auto & name : attrNames)
strAttrNames.insert(std::string(name));
return Suggestions::bestMatches(strAttrNames, name);
}
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
{
if (root->db) {
@@ -459,11 +446,6 @@ std::shared_ptr<AttrCursor> 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) {
@@ -482,7 +464,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), placeholder_t()};
}
return make_ref<AttrCursor>(
return std::make_shared<AttrCursor>(
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
}
@@ -491,31 +473,27 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name)
return maybeGetAttr(root->state.symbols.create(name));
}
ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
std::shared_ptr<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
{
auto p = maybeGetAttr(name, forceErrors);
if (!p)
throw Error("attribute '%s' does not exist", getAttrPathStr(name));
return ref(p);
return p;
}
ref<AttrCursor> AttrCursor::getAttr(std::string_view name)
std::shared_ptr<AttrCursor> AttrCursor::getAttr(std::string_view name)
{
return getAttr(root->state.symbols.create(name));
}
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
std::shared_ptr<AttrCursor> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
{
auto res = shared_from_this();
for (auto & attr : attrPath) {
auto child = res->maybeGetAttr(attr, force);
if (!child) {
auto suggestions = res->getSuggestionsForAttr(attr);
return OrSuggestions<ref<AttrCursor>>::failed(suggestions);
}
res = child;
res = res->maybeGetAttr(attr, force);
if (!res) return {};
}
return ref(res);
return res;
}
std::string AttrCursor::getString()
@@ -549,7 +527,7 @@ string_t AttrCursor::getStringWithContext()
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
bool valid = true;
for (auto & c : s->second) {
if (!root->state.store->isValidPath(c.first)) {
if (!root->state.store->isValidPath(root->state.store->parseStorePath(c.first))) {
valid = false;
break;
}
@@ -566,7 +544,7 @@ string_t AttrCursor::getStringWithContext()
auto & v = forceValue();
if (v.type() == nString)
return {v.string.s, v.getContext(*root->state.store)};
return {v.string.s, v.getContext()};
else if (v.type() == nPath)
return {v.path, {}};
else
@@ -618,7 +596,7 @@ std::vector<Symbol> 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 std::string &) a < (const std::string &) b;
return (const string &) a < (const string &) b;
});
if (root->db)
+5 -9
View File
@@ -33,7 +33,7 @@ public:
EvalState & state,
RootLoader rootLoader);
ref<AttrCursor> getRoot();
std::shared_ptr<AttrCursor> getRoot();
};
enum AttrType {
@@ -52,7 +52,7 @@ struct misc_t {};
struct failed_t {};
typedef uint64_t AttrId;
typedef std::pair<AttrId, Symbol> AttrKey;
typedef std::pair<std::string, NixStringContext> string_t;
typedef std::pair<std::string, std::vector<std::pair<Path, std::string>>> string_t;
typedef std::variant<
std::vector<Symbol>,
@@ -94,19 +94,15 @@ public:
std::string getAttrPathStr(Symbol name) const;
Suggestions getSuggestionsForAttr(Symbol name);
std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name, bool forceErrors = false);
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name);
ref<AttrCursor> getAttr(Symbol name, bool forceErrors = false);
std::shared_ptr<AttrCursor> getAttr(Symbol name, bool forceErrors = false);
ref<AttrCursor> getAttr(std::string_view name);
std::shared_ptr<AttrCursor> getAttr(std::string_view name);
/* Get an attribute along a chain of attrsets. Note that this does
not auto-call functors or functions. */
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
std::shared_ptr<AttrCursor> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
std::string getString();
+13 -78
View File
@@ -24,81 +24,6 @@ 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; });
@@ -127,7 +52,6 @@ void EvalState::forceValue(Value & v, Callable getPos)
}
[[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
{
forceAttrs(v, [&]() { return pos; });
@@ -135,7 +59,6 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
template <typename Callable>
[[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, Callable getPos)
{
forceValue(v, getPos);
@@ -144,7 +67,6 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos)
}
[[gnu::always_inline]]
inline void EvalState::forceList(Value & v, const Pos & pos)
{
forceValue(v, pos);
@@ -152,5 +74,18 @@ inline void EvalState::forceList(Value & v, const Pos & pos)
throwTypeError(pos, "value is %1% while a list was expected", v);
}
/* 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;
}
}
+126 -157
View File
@@ -63,15 +63,9 @@ static char * dupString(const char * s)
}
// 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)
static char * dupStringWithLen(const char * s, size_t size)
{
char * t;
if (size == 0)
return "";
#if HAVE_BOEHMGC
t = GC_STRNDUP(s, size);
#else
@@ -81,10 +75,6 @@ static const char * makeImmutableStringWithLen(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)
{
@@ -96,20 +86,25 @@ RootValue allocRootValue(Value * v)
}
void Value::print(std::ostream & str, std::set<const void *> * seen) const
void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v)
{
checkInterrupt();
switch (internalType) {
if (!active.insert(&v).second) {
str << "<CYCLE>";
return;
}
switch (v.internalType) {
case tInt:
str << integer;
str << v.integer;
break;
case tBool:
str << (boolean ? "true" : "false");
str << (v.boolean ? "true" : "false");
break;
case tString:
str << "\"";
for (const char * i = string.s; *i; i++)
for (const char * i = v.string.s; *i; i++)
if (*i == '\"' || *i == '\\') str << "\\" << *i;
else if (*i == '\n') str << "\\n";
else if (*i == '\r') str << "\\r";
@@ -119,38 +114,30 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
str << "\"";
break;
case tPath:
str << path; // !!! escaping?
str << v.path; // !!! escaping?
break;
case tNull:
str << "null";
break;
case tAttrs: {
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 << "{ ";
for (auto & i : v.attrs->lexicographicOrder()) {
str << i->name << " = ";
printValue(str, active, *i->value);
str << "; ";
}
str << "}";
break;
}
case tList1:
case tList2:
case tListN:
if (seen && listSize() && !seen->insert(listElems()).second)
str << "«repeated»";
else {
str << "[ ";
for (auto v2 : listItems()) {
v2->print(str, seen);
str << " ";
}
str << "]";
str << "[ ";
for (auto v2 : v.listItems()) {
printValue(str, active, *v2);
str << " ";
}
str << "]";
break;
case tThunk:
case tApp:
@@ -166,32 +153,28 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
str << "<PRIMOP-APP>";
break;
case tExternal:
str << *external;
str << *v.external;
break;
case tFloat:
str << fpoint;
str << v.fpoint;
break;
default:
abort();
}
}
void Value::print(std::ostream & str, bool showRepeated) const
{
std::set<const void *> seen;
print(str, showRepeated ? nullptr : &seen);
active.erase(&v);
}
std::ostream & operator << (std::ostream & str, const Value & v)
{
v.print(str, false);
std::set<const Value *> active;
printValue(str, active, v);
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;
@@ -200,7 +183,7 @@ const Value * getPrimOp(const Value &v) {
return primOp;
}
std::string_view showType(ValueType type)
string showType(ValueType type)
{
switch (type) {
case nInt: return "an integer";
@@ -219,20 +202,20 @@ std::string_view showType(ValueType type)
}
std::string showType(const Value & v)
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'", std::string(v.primOp->name));
return fmt("the built-in function '%s'", string(v.primOp->name));
case tPrimOpApp:
return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name));
return fmt("the partially applied built-in function '%s'", 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 std::string(showType(v.type()));
return showType(v.type());
}
}
@@ -373,7 +356,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 std::string & s)
static Strings parseNixPath(const string & s)
{
Strings res;
@@ -436,7 +419,6 @@ 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"))
@@ -456,10 +438,8 @@ EvalState::EvalState(
, regexCache(makeRegexCache())
#if HAVE_BOEHMGC
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
, env1AllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
#else
, valueAllocCache(std::make_shared<void *>(nullptr))
, env1AllocCache(std::make_shared<void *>(nullptr))
#endif
, baseEnv(allocEnv(128))
, staticBaseEnv(false, 0)
@@ -508,6 +488,23 @@ 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)
@@ -520,14 +517,6 @@ 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_;
@@ -617,7 +606,7 @@ Path EvalState::toRealPath(const Path & path, const PathSet & context)
}
Value * EvalState::addConstant(const std::string & name, Value & v)
Value * EvalState::addConstant(const string & name, Value & v)
{
Value * v2 = allocValue();
*v2 = v;
@@ -626,19 +615,19 @@ Value * EvalState::addConstant(const std::string & name, Value & v)
}
void EvalState::addConstant(const std::string & name, Value * v)
void EvalState::addConstant(const string & name, Value * v)
{
staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
}
Value * EvalState::addPrimOp(const std::string & name,
Value * EvalState::addPrimOp(const string & name,
size_t arity, PrimOpFun primOp)
{
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
auto name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
Symbol sym = symbols.create(name2);
/* Hack to make constants lazy: turn them into a application of
@@ -686,7 +675,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
}
Value & EvalState::getBuiltin(const std::string & name)
Value & EvalState::getBuiltin(const string & name)
{
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
}
@@ -714,34 +703,25 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
evaluator. So here are some helper functions for throwing
exceptions. */
LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2))
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
{
throw EvalError(s, s2);
}
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2))
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2))
{
throw EvalError(ErrorInfo {
.msg = hintfmt(s, s2),
.errPos = pos,
.suggestions = suggestions,
});
}
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2))
{
throw EvalError(ErrorInfo {
throw EvalError({
.msg = hintfmt(s, s2),
.errPos = pos
});
}
LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, const std::string & s3))
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
{
throw EvalError(s, s2, s3);
}
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3))
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3))
{
throw EvalError({
.msg = hintfmt(s, s2, s3),
@@ -774,22 +754,12 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
});
}
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2))
{
throw TypeError(ErrorInfo {
.msg = hintfmt(s, fun.showNamePos(), s2),
.errPos = pos,
.suggestions = suggestions,
});
}
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
{
throw TypeError(s, showType(v));
}
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1))
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
{
throw AssertionError({
.msg = hintfmt(s, s1),
@@ -797,7 +767,7 @@ LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s,
});
}
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1))
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1))
{
throw UndefinedVarError({
.msg = hintfmt(s, s1),
@@ -805,7 +775,7 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char *
});
}
LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1))
LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1))
{
throw MissingArgumentError({
.msg = hintfmt(s, s1),
@@ -813,12 +783,12 @@ LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char
});
}
LocalNoInline(void addErrorTrace(Error & e, const char * s, const std::string & s2))
LocalNoInline(void addErrorTrace(Error & e, const char * s, const string & s2))
{
e.addTrace(std::nullopt, s, s2);
}
LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2))
LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const string & s2))
{
e.addTrace(pos, s, s2);
}
@@ -826,7 +796,7 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con
void Value::mkString(std::string_view s)
{
mkString(makeImmutableString(s));
mkString(dupStringWithLen(s.data(), s.size()));
}
@@ -857,7 +827,7 @@ void Value::mkStringMove(const char * s, const PathSet & context)
void Value::mkPath(std::string_view s)
{
mkPath(makeImmutableString(s));
mkPath(dupStringWithLen(s.data(), s.size()));
}
@@ -887,6 +857,42 @@ 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;
}
void EvalState::mkList(Value & v, size_t size)
{
v.mkList(size);
@@ -1215,7 +1221,7 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v)
}
static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath)
static string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
@@ -1256,15 +1262,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
}
} else {
state.forceAttrs(*vAttrs, pos);
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
std::set<std::string> allAttrNames;
for (auto & attr : *vAttrs->attrs)
allAttrNames.insert(attr.name);
throwEvalError(
pos,
Suggestions::bestMatches(allAttrNames, name),
"attribute '%1%' missing", name);
}
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
throwEvalError(pos, "attribute '%1%' missing", name);
}
vAttrs = j->value;
pos2 = j->pos;
@@ -1380,17 +1379,8 @@ 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)) {
std::set<std::string> 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);
}
if (!lambda.formals->has(i.name))
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
abort(); // can't happen
}
}
@@ -1405,7 +1395,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()
? "'" + (const std::string &) lambda.name + "'"
? "'" + (string) lambda.name + "'"
: "anonymous lambda"));
addErrorTrace(e, pos, "from call site%s", "");
}
@@ -1893,22 +1883,13 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos)
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
name>. */
NixStringContextElem decodeContext(const Store & store, std::string_view s)
std::pair<string, string> decodeContext(std::string_view s)
{
if (s.at(0) == '!') {
size_t index = s.find("!", 1);
return {
store.parseStorePath(s.substr(index + 1)),
std::string(s.substr(1, index - 1)),
};
return {std::string(s.substr(index + 1)), std::string(s.substr(1, index - 1))};
} else
return {
store.parseStorePath(
s.at(0) == '/'
? s
: s.substr(1)),
"",
};
return {s.at(0) == '/' ? std::string(s) : std::string(s.substr(1)), ""};
}
@@ -1920,13 +1901,13 @@ void copyContext(const Value & v, PathSet & context)
}
NixStringContext Value::getContext(const Store & store)
std::vector<std::pair<Path, std::string>> Value::getContext()
{
NixStringContext res;
std::vector<std::pair<Path, std::string>> res;
assert(internalType == tString);
if (string.context)
for (const char * * p = string.context; *p; ++p)
res.push_back(decodeContext(store, *p));
res.push_back(decodeContext(*p));
return res;
}
@@ -1965,7 +1946,7 @@ bool EvalState::isDerivation(Value & v)
}
std::optional<std::string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
std::optional<string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
PathSet & context, bool coerceMore, bool copyToStore)
{
auto i = v.attrs->find(sToString);
@@ -2020,7 +2001,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
if (v.type() == nNull) return "";
if (v.isList()) {
std::string result;
string result;
for (auto [n, v2] : enumerate(v.listItems())) {
result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
if (n < v.listSize() - 1
@@ -2036,7 +2017,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
}
std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
string EvalState::copyPathToStore(PathSet & context, const Path & path)
{
if (nix::isDerivation(path))
throwEvalError("file names are not allowed to end in '%1%'", drvExtension);
@@ -2062,25 +2043,13 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
{
auto path = coerceToString(pos, v, context, false, false).toOwned();
string path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/')
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
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);
@@ -2244,11 +2213,11 @@ void EvalState::printStats()
for (auto & i : functionCalls) {
auto obj = list.object();
if (i.first->name.set())
obj.attr("name", (const std::string &) i.first->name);
obj.attr("name", (const string &) i.first->name);
else
obj.attr("name", nullptr);
if (i.first->pos) {
obj.attr("file", (const std::string &) i.first->pos.file);
obj.attr("file", (const string &) i.first->pos.file);
obj.attr("line", i.first->pos.line);
obj.attr("column", i.first->pos.column);
}
@@ -2260,7 +2229,7 @@ void EvalState::printStats()
for (auto & i : attrSelects) {
auto obj = list.object();
if (i.first) {
obj.attr("file", (const std::string &) i.first.file);
obj.attr("file", (const string &) i.first.file);
obj.attr("line", i.first.line);
obj.attr("column", i.first.column);
}
@@ -2277,7 +2246,7 @@ void EvalState::printStats()
}
std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
{
throw TypeError({
.msg = hintfmt("cannot coerce %1% to a string", showType()),
+19 -26
View File
@@ -78,7 +78,7 @@ public:
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString,
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
sContentAddressed, sImpure,
sContentAddressed,
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
@@ -133,14 +133,9 @@ private:
/* Cache used by prim_match(). */
std::shared_ptr<RegexCache> regexCache;
#if HAVE_BOEHMGC
/* Allocation cache for GC'd Value objects. */
std::shared_ptr<void *> valueAllocCache;
/* Allocation cache for size-1 Env objects. */
std::shared_ptr<void *> env1AllocCache;
#endif
public:
EvalState(
@@ -149,7 +144,13 @@ public:
std::shared_ptr<Store> buildStore = nullptr);
~EvalState();
void addToSearchPath(const std::string & s);
void requireExperimentalFeatureOnEvaluation(
const ExperimentalFeature &,
const std::string_view fName,
const Pos & pos
);
void addToSearchPath(const string & s);
SearchPath getSearchPath() { return searchPath; }
@@ -160,9 +161,6 @@ 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);
@@ -253,7 +251,7 @@ public:
set with attribute `type = "derivation"'). */
bool isDerivation(Value & v);
std::optional<std::string> tryAttrsToString(const Pos & pos, Value & v,
std::optional<string> tryAttrsToString(const Pos & pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true);
/* String coercion. Converts strings, paths and derivations to a
@@ -264,16 +262,13 @@ public:
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
std::string copyPathToStore(PathSet & context, const Path & path);
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
@@ -289,18 +284,18 @@ private:
void createBaseEnv();
Value * addConstant(const std::string & name, Value & v);
Value * addConstant(const string & name, Value & v);
void addConstant(const std::string & name, Value * v);
void addConstant(const string & name, Value * v);
Value * addPrimOp(const std::string & name,
Value * addPrimOp(const string & name,
size_t arity, PrimOpFun primOp);
Value * addPrimOp(PrimOp && primOp);
public:
Value & getBuiltin(const std::string & name);
Value & getBuiltin(const string & name);
struct Doc
{
@@ -346,8 +341,8 @@ public:
void autoCallFunction(Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */
inline Value * allocValue();
inline Env & allocEnv(size_t size);
Value * allocValue();
Env & allocEnv(size_t size);
Value * allocAttr(Value & vAttrs, const Symbol & name);
Value * allocAttr(Value & vAttrs, std::string_view name);
@@ -419,12 +414,12 @@ private:
/* Return a string representing the type of the value `v'. */
std::string_view showType(ValueType type);
std::string showType(const Value & v);
string showType(ValueType type);
string showType(const Value & v);
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
name>. */
NixStringContextElem decodeContext(const Store & store, std::string_view s);
std::pair<string, string> decodeContext(std::string_view s);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
@@ -508,5 +503,3 @@ extern EvalSettings evalSettings;
static const std::string corepkgsPrefix{"/__corepkgs__/"};
}
#include "eval-inline.hh"
+1 -2
View File
@@ -1,6 +1,5 @@
#include "flake.hh"
#include "globals.hh"
#include "fetch-settings.hh"
#include <nlohmann/json.hpp>
@@ -54,7 +53,7 @@ void ConfigFile::apply()
auto trustedList = readTrustedList();
bool trusted = false;
if (nix::fetchSettings.acceptFlakeConfig){
if (nix::settings.acceptFlakeConfig){
trusted = true;
} else if (auto saved = get(get(trustedList, name).value_or(std::map<std::string, bool>()), valueS)) {
trusted = *saved;
+13 -35
View File
@@ -6,7 +6,6 @@
#include "store-api.hh"
#include "fetchers.hh"
#include "finally.hh"
#include "fetch-settings.hh"
namespace nix {
@@ -255,7 +254,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, std::string(state.forceStringNoCtx(*setting.value, *setting.pos))});
flake.config.settings.insert({setting.name, string(state.forceStringNoCtx(*setting.value, *setting.pos))});
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
flake.config.settings.emplace(
@@ -316,7 +315,7 @@ LockedFlake lockFlake(
FlakeCache flakeCache;
auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries);
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
auto flake = getFlake(state, topRef, useRegistries, flakeCache);
@@ -502,7 +501,7 @@ LockedFlake lockFlake(
this input. */
debug("creating new input '%s'", inputPathS);
if (!lockFlags.allowMutable && !input.ref->input.isLocked())
if (!lockFlags.allowMutable && !input.ref->input.isImmutable())
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
if (input.isFlake) {
@@ -592,7 +591,7 @@ LockedFlake lockFlake(
if (lockFlags.writeLockFile) {
if (auto sourcePath = topRef.input.getSourcePath()) {
if (!newLockFile.isImmutable()) {
if (fetchSettings.warnDirty)
if (settings.warnDirty)
warn("will not write lock file of flake '%s' because it has a mutable input", topRef);
} else {
if (!lockFlags.updateLockFile)
@@ -619,7 +618,7 @@ LockedFlake lockFlake(
if (lockFlags.commitLockFile) {
std::string cm;
cm = fetchSettings.commitLockFileSummary.get();
cm = settings.commitLockFileSummary.get();
if (cm == "") {
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
@@ -651,7 +650,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.isLocked())
&& !flake.lockedRef.input.isImmutable())
throw Error("'%s' did not change after I updated its 'flake.lock' file; is 'flake.lock' under version control?", flake.originalRef);
}
} else
@@ -706,45 +705,24 @@ void callFlake(EvalState & state,
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
if (evalSettings.pureEval && !flakeRef.input.isLocked())
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
if (evalSettings.pureEval && !flakeRef.input.isImmutable())
throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
callFlake(state,
lockFlake(state, flakeRef,
LockFlags {
.updateLockFile = false,
.useRegistries = !evalSettings.pureEval && fetchSettings.useRegistries,
.useRegistries = !evalSettings.pureEval && settings.useRegistries,
.allowMutable = !evalSettings.pureEval,
}),
v);
}
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,
});
static RegisterPrimOp r2("__getFlake", 1, prim_getFlake);
}
+3 -3
View File
@@ -98,7 +98,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
if (std::regex_match(url, match, flakeRegex)) {
auto parsedURL = ParsedURL{
.url = url,
.base = "flake:" + match.str(1),
.base = "flake:" + std::string(match[1]),
.scheme = "flake",
.authority = "",
.path = match[1],
@@ -106,12 +106,12 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
return std::make_pair(
FlakeRef(Input::fromURL(parsedURL), ""),
percentDecode(match.str(6)));
percentDecode(std::string(match[6])));
}
else if (std::regex_match(url, match, pathUrlRegex)) {
std::string path = match[1];
std::string fragment = percentDecode(match.str(3));
std::string fragment = percentDecode(std::string(match[3]));
if (baseDir) {
/* Check if 'url' is a path (either absolute or relative
+2 -2
View File
@@ -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.isLocked())
if (!lockedRef.input.isImmutable())
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<const LockedNode>(i);
if (lockedNode && !lockedNode->lockedRef.input.isLocked()) return false;
if (lockedNode && !lockedNode->lockedRef.input.isImmutable()) return false;
}
return true;
+39 -53
View File
@@ -1,7 +1,6 @@
#include "get-drvs.hh"
#include "util.hh"
#include "eval-inline.hh"
#include "derivations.hh"
#include "store-api.hh"
#include "path-with-outputs.hh"
@@ -12,8 +11,8 @@
namespace nix {
DrvInfo::DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs)
: state(&state), attrs(attrs), attrPath(std::move(attrPath))
DrvInfo::DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs)
: state(&state), attrs(attrs), attrPath(attrPath)
{
}
@@ -23,7 +22,7 @@ DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPat
{
auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs);
this->drvPath = drvPath;
this->drvPath = store->printStorePath(drvPath);
auto drv = store->derivationFromPath(drvPath);
@@ -42,11 +41,13 @@ DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPat
throw Error("derivation '%s' does not have output '%s'", store->printStorePath(drvPath), outputName);
auto & [outputName, output] = *i;
outPath = {output.path(*store, drv.name, outputName)};
auto optStorePath = output.path(*store, drv.name, outputName);
if (optStorePath)
outPath = store->printStorePath(*optStorePath);
}
std::string DrvInfo::queryName() const
string DrvInfo::queryName() const
{
if (name == "" && attrs) {
auto i = attrs->find(state->sName);
@@ -57,7 +58,7 @@ std::string DrvInfo::queryName() const
}
std::string DrvInfo::querySystem() const
string DrvInfo::querySystem() const
{
if (system == "" && attrs) {
auto i = attrs->find(state->sSystem);
@@ -67,35 +68,24 @@ std::string DrvInfo::querySystem() const
}
std::optional<StorePath> DrvInfo::queryDrvPath() const
string DrvInfo::queryDrvPath() const
{
if (!drvPath && attrs) {
if (drvPath == "" && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath);
PathSet context;
if (i == attrs->end())
drvPath = {std::nullopt};
else
drvPath = {state->coerceToStorePath(*i->pos, *i->value, context)};
drvPath = i != attrs->end() ? state->coerceToPath(*i->pos, *i->value, context) : "";
}
return drvPath.value_or(std::nullopt);
return drvPath;
}
StorePath DrvInfo::requireDrvPath() const
{
if (auto drvPath = queryDrvPath())
return *drvPath;
throw Error("derivation does not contain a 'drvPath' attribute");
}
StorePath DrvInfo::queryOutPath() const
string DrvInfo::queryOutPath() const
{
if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context;
if (i != attrs->end())
outPath = state->coerceToStorePath(*i->pos, *i->value, context);
outPath = state->coerceToPath(*i->pos, *i->value, context);
}
if (!outPath)
throw UnimplementedError("CA derivations are not yet supported");
@@ -103,7 +93,7 @@ StorePath DrvInfo::queryOutPath() const
}
DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall)
DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
{
if (outputs.empty()) {
/* Get the ‘outputs’ list. */
@@ -113,24 +103,20 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
/* For each output... */
for (auto elem : i->value->listItems()) {
std::string output(state->forceStringNoCtx(*elem, *i->pos));
/* 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);
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);
/* 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);
}
} else
outputs.emplace("out", withPaths ? std::optional{queryOutPath()} : std::nullopt);
outputs["out"] = queryOutPath();
}
if (!onlyOutputsToInstall || !attrs)
return outputs;
@@ -152,7 +138,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
}
std::string DrvInfo::queryOutputName() const
string DrvInfo::queryOutputName() const
{
if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutputName);
@@ -204,7 +190,7 @@ bool DrvInfo::checkMeta(Value & v)
}
Value * DrvInfo::queryMeta(const std::string & name)
Value * DrvInfo::queryMeta(const string & name)
{
if (!getMeta()) return 0;
Bindings::iterator a = meta->find(state->symbols.create(name));
@@ -213,7 +199,7 @@ Value * DrvInfo::queryMeta(const std::string & name)
}
std::string DrvInfo::queryMetaString(const std::string & name)
string DrvInfo::queryMetaString(const string & name)
{
Value * v = queryMeta(name);
if (!v || v->type() != nString) return "";
@@ -221,7 +207,7 @@ std::string DrvInfo::queryMetaString(const std::string & name)
}
NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def)
NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
{
Value * v = queryMeta(name);
if (!v) return def;
@@ -235,7 +221,7 @@ NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def)
return def;
}
NixFloat DrvInfo::queryMetaFloat(const std::string & name, NixFloat def)
NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
{
Value * v = queryMeta(name);
if (!v) return def;
@@ -250,7 +236,7 @@ NixFloat DrvInfo::queryMetaFloat(const std::string & name, NixFloat def)
}
bool DrvInfo::queryMetaBool(const std::string & name, bool def)
bool DrvInfo::queryMetaBool(const string & name, bool def)
{
Value * v = queryMeta(name);
if (!v) return def;
@@ -265,7 +251,7 @@ bool DrvInfo::queryMetaBool(const std::string & name, bool def)
}
void DrvInfo::setMeta(const std::string & name, Value * v)
void DrvInfo::setMeta(const string & name, Value * v)
{
getMeta();
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
@@ -288,7 +274,7 @@ typedef std::set<Bindings *> 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 std::string & attrPath, DrvInfos & drvs, Done & done,
const string & attrPath, DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures)
{
try {
@@ -325,7 +311,7 @@ std::optional<DrvInfo> getDerivation(EvalState & state, Value & v,
}
static std::string addToPath(const std::string & s1, const std::string & s2)
static string addToPath(const string & s1, const string & s2)
{
return s1.empty() ? s2 : s1 + "." + s2;
}
@@ -335,7 +321,7 @@ static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
static void getDerivations(EvalState & state, Value & vIn,
const std::string & pathPrefix, Bindings & autoArgs,
const string & pathPrefix, Bindings & autoArgs,
DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures)
{
@@ -360,7 +346,7 @@ static void getDerivations(EvalState & state, Value & vIn,
debug("evaluating attribute '%1%'", i->name);
if (!std::regex_match(std::string(i->name), attrRegex))
continue;
std::string pathPrefix2 = addToPath(pathPrefix, i->name);
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)) {
@@ -378,7 +364,7 @@ static void getDerivations(EvalState & state, Value & vIn,
else if (v.type() == nList) {
for (auto [n, elem] : enumerate(v.listItems())) {
std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
@@ -388,7 +374,7 @@ static void getDerivations(EvalState & state, Value & vIn,
}
void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix,
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
Bindings & autoArgs, DrvInfos & drvs, bool ignoreAssertionFailures)
{
Done done;
+25 -28
View File
@@ -1,7 +1,6 @@
#pragma once
#include "eval.hh"
#include "path.hh"
#include <string>
#include <map>
@@ -13,16 +12,16 @@ namespace nix {
struct DrvInfo
{
public:
typedef std::map<std::string, std::optional<StorePath>> Outputs;
typedef std::map<string, Path> Outputs;
private:
EvalState * state;
mutable std::string name;
mutable std::string system;
mutable std::optional<std::optional<StorePath>> drvPath;
mutable std::optional<StorePath> outPath;
mutable std::string outputName;
mutable string name;
mutable string system;
mutable string drvPath;
mutable std::optional<string> outPath;
mutable string outputName;
Outputs outputs;
bool failed = false; // set if we get an AssertionError
@@ -34,38 +33,36 @@ private:
bool checkMeta(Value & v);
public:
std::string attrPath; /* path towards the derivation */
string attrPath; /* path towards the derivation */
DrvInfo(EvalState & state) : state(&state) { };
DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs);
DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs);
DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
std::string queryName() const;
std::string querySystem() const;
std::optional<StorePath> queryDrvPath() const;
StorePath requireDrvPath() const;
StorePath queryOutPath() const;
std::string queryOutputName() const;
/** Return the unordered map of output names to (optional) output paths.
* The "outputs to install" are determined by `meta.outputsToInstall`. */
Outputs queryOutputs(bool withPaths = true, bool onlyOutputsToInstall = false);
string queryName() const;
string querySystem() const;
string queryDrvPath() const;
string queryOutPath() const;
string queryOutputName() const;
/** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */
Outputs queryOutputs(bool onlyOutputsToInstall = false);
StringSet queryMetaNames();
Value * queryMeta(const std::string & name);
std::string queryMetaString(const std::string & name);
NixInt queryMetaInt(const std::string & name, NixInt def);
NixFloat queryMetaFloat(const std::string & name, NixFloat def);
bool queryMetaBool(const std::string & name, bool def);
void setMeta(const std::string & name, Value * v);
Value * queryMeta(const string & name);
string queryMetaString(const string & name);
NixInt queryMetaInt(const string & name, NixInt def);
NixFloat queryMetaFloat(const string & name, NixFloat def);
bool queryMetaBool(const string & name, bool def);
void setMeta(const string & name, Value * v);
/*
MetaInfo queryMetaInfo(EvalState & state) const;
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
*/
void setName(const std::string & s) { name = s; }
void setDrvPath(StorePath path) { drvPath = {{std::move(path)}}; }
void setOutPath(StorePath path) { outPath = {{std::move(path)}}; }
void setName(const string & s) { name = s; }
void setDrvPath(const string & s) { drvPath = s; }
void setOutPath(const string & s) { outPath = s; }
void setFailed() { failed = true; };
bool hasFailed() { return failed; };
@@ -84,7 +81,7 @@ typedef std::list<DrvInfo> DrvInfos;
std::optional<DrvInfo> getDerivation(EvalState & state,
Value & v, bool ignoreAssertionFailures);
void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix,
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
Bindings & autoArgs, DrvInfos & drvs,
bool ignoreAssertionFailures);
+4 -19
View File
@@ -28,13 +28,6 @@ using namespace nix;
namespace nix {
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
{
return Pos(data->origin, data->file, loc.first_line, loc.first_column);
}
#define CUR_POS makeCurPos(*yylloc, data)
// backup to recover from yyless(0)
YYLTYPE prev_yylloc;
@@ -44,6 +37,7 @@ static void initLoc(YYLTYPE * loc)
loc->first_column = loc->last_column = 1;
}
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
{
prev_yylloc = *loc;
@@ -153,20 +147,14 @@ or { return OR_KW; }
try {
yylval->n = boost::lexical_cast<int64_t>(yytext);
} catch (const boost::bad_lexical_cast &) {
throw ParseError({
.msg = hintfmt("invalid integer '%1%'", yytext),
.errPos = CUR_POS,
});
throw ParseError("invalid integer '%1%'", yytext);
}
return INT;
}
{FLOAT} { errno = 0;
yylval->nf = strtod(yytext, 0);
if (errno != 0)
throw ParseError({
.msg = hintfmt("invalid float '%1%'", yytext),
.errPos = CUR_POS,
});
throw ParseError("invalid float '%1%'", yytext);
return FLOAT;
}
@@ -292,10 +280,7 @@ or { return OR_KW; }
<INPATH_SLASH>{ANY} |
<INPATH_SLASH><<EOF>> {
throw ParseError({
.msg = hintfmt("path has a trailing slash"),
.errPos = CUR_POS,
});
throw ParseError("path has a trailing slash");
}
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
+11 -18
View File
@@ -16,10 +16,10 @@ std::ostream & operator << (std::ostream & str, const Expr & e)
return str;
}
static void showString(std::ostream & str, std::string_view s)
static void showString(std::ostream & str, const string & s)
{
str << '"';
for (auto c : s)
for (auto c : (string) s)
if (c == '"' || c == '\\' || c == '$') str << "\\" << c;
else if (c == '\n') str << "\\n";
else if (c == '\r') str << "\\r";
@@ -28,7 +28,7 @@ static void showString(std::ostream & str, std::string_view s)
str << '"';
}
static void showId(std::ostream & str, std::string_view s)
static void showId(std::ostream & str, const string & s)
{
if (s.empty())
str << "\"\"";
@@ -103,18 +103,11 @@ void ExprAttrs::show(std::ostream & str) const
{
if (recursive) str << "rec ";
str << "{ ";
typedef const decltype(attrs)::value_type * Attr;
std::vector<Attr> sorted;
for (auto & i : attrs) sorted.push_back(&i);
std::sort(sorted.begin(), sorted.end(), [](Attr a, Attr b) {
return (const std::string &) a->first < (const std::string &) b->first;
});
for (auto & i : sorted) {
if (i->second.inherited)
str << "inherit " << i->first << " " << "; ";
for (auto & i : attrs)
if (i.second.inherited)
str << "inherit " << i.first << " " << "; ";
else
str << i->first << " = " << *i->second.e << "; ";
}
str << i.first << " = " << *i.second.e << "; ";
for (auto & i : dynamicAttrs)
str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
str << "}";
@@ -218,7 +211,7 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
auto f = format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%");
switch (pos.origin) {
case foFile:
f % (const std::string &) pos.file;
f % (string) pos.file;
break;
case foStdin:
case foString:
@@ -234,7 +227,7 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
}
std::string showAttrPath(const AttrPath & attrPath)
string showAttrPath(const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
@@ -468,9 +461,9 @@ void ExprLambda::setName(Symbol & name)
}
std::string ExprLambda::showNamePos() const
string ExprLambda::showNamePos() const
{
return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pos);
return (format("%1% at %2%") % (name.set() ? "'" + (string) name + "'" : "anonymous function") % pos).str();
}
+11 -13
View File
@@ -23,23 +23,21 @@ MakeError(RestrictedPathError, Error);
struct Pos
{
FileOrigin origin;
Symbol file;
uint32_t line;
FileOrigin origin:2;
uint32_t column:30;
Pos() : line(0), origin(foString), column(0) { };
Pos(FileOrigin origin, const Symbol & file, uint32_t line, uint32_t column)
: file(file), line(line), origin(origin), column(column) { };
unsigned int line, column;
Pos() : origin(foString), line(0), column(0) { };
Pos(FileOrigin origin, const Symbol & file, unsigned int line, unsigned int column)
: origin(origin), file(file), line(line), column(column) { };
operator bool() const
{
return line != 0;
}
bool operator < (const Pos & p2) const
{
if (!line) return p2.line;
if (!p2.line) return false;
int d = ((const std::string &) file).compare((const std::string &) p2.file);
int d = ((string) file).compare((string) p2.file);
if (d < 0) return true;
if (d > 0) return false;
if (line < p2.line) return true;
@@ -70,7 +68,7 @@ struct AttrName
typedef std::vector<AttrName> AttrPath;
std::string showAttrPath(const AttrPath & attrPath);
string showAttrPath(const AttrPath & attrPath);
/* Abstract syntax of Nix expressions. */
@@ -112,7 +110,7 @@ struct ExprFloat : Expr
struct ExprString : Expr
{
std::string s;
string s;
Value v;
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
COMMON_METHODS
@@ -121,9 +119,9 @@ struct ExprString : Expr
struct ExprPath : Expr
{
std::string s;
string s;
Value v;
ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); };
ExprPath(const string & s) : s(s) { v.mkPath(this->s.c_str()); };
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
@@ -251,7 +249,7 @@ struct ExprLambda : Expr
{
};
void setName(Symbol & name);
std::string showNamePos() const;
string showNamePos() const;
inline bool hasFormals() const { return formals != nullptr; }
COMMON_METHODS
};
+15 -15
View File
@@ -244,7 +244,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
es2->emplace_back(i->first, e);
};
const auto trimString = [&] (const StringToken & t) {
std::string s2;
string s2;
for (size_t j = 0; j < t.l; ++j) {
if (atStartOfLine) {
if (t.p[j] == ' ') {
@@ -268,9 +268,9 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
/* Remove the last line if it is empty and consists only of
spaces. */
if (n == 1) {
std::string::size_type p = s2.find_last_of('\n');
if (p != std::string::npos && s2.find_first_not_of(' ', p + 1) == std::string::npos)
s2 = std::string(s2, 0, p + 1);
string::size_type p = s2.find_last_of('\n');
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
s2 = string(s2, 0, p + 1);
}
es2->emplace_back(i->first, new ExprString(s2));
@@ -466,7 +466,7 @@ expr_simple
$$ = new ExprConcatStrings(CUR_POS, false, $2);
}
| SPATH {
std::string path($1.p + 1, $1.l - 2);
string path($1.p + 1, $1.l - 2);
$$ = new ExprCall(CUR_POS,
new ExprVar(data->symbols.create("__findFile")),
{new ExprVar(data->symbols.create("__nixPath")),
@@ -479,7 +479,7 @@ expr_simple
.msg = hintfmt("URL literals are disabled"),
.errPos = CUR_POS
});
$$ = new ExprString(std::string($1));
$$ = new ExprString(string($1));
}
| '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared
@@ -494,19 +494,19 @@ expr_simple
;
string_parts
: STR { $$ = new ExprString(std::string($1)); }
: STR { $$ = new ExprString(string($1)); }
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
| { $$ = new ExprString(""); }
;
string_parts_interpolated
: string_parts_interpolated STR
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); }
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(string($2))); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' {
$$ = new std::vector<std::pair<Pos, Expr *> >;
$$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1)));
$$->emplace_back(makeCurPos(@1, data), new ExprString(string($1)));
$$->emplace_back(makeCurPos(@2, data), $3);
}
;
@@ -520,7 +520,7 @@ path_start
$$ = new ExprPath(path);
}
| HPATH {
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
Path path(getHome() + string($1.p + 1, $1.l - 1));
$$ = new ExprPath(path);
}
;
@@ -738,16 +738,16 @@ Expr * EvalState::parseStdin()
}
void EvalState::addToSearchPath(const std::string & s)
void EvalState::addToSearchPath(const string & s)
{
size_t pos = s.find('=');
std::string prefix;
string prefix;
Path path;
if (pos == std::string::npos) {
if (pos == string::npos) {
path = s;
} else {
prefix = std::string(s, 0, pos);
path = std::string(s, pos + 1);
prefix = string(s, 0, pos);
path = string(s, pos + 1);
}
searchPath.emplace_back(prefix, path);
+104 -130
View File
@@ -43,8 +43,8 @@ StringMap EvalState::realiseContext(const PathSet & context)
StringMap res;
for (auto & i : context) {
auto [ctx, outputName] = decodeContext(*store, i);
auto ctxS = store->printStorePath(ctx);
auto [ctxS, outputName] = decodeContext(i);
auto ctx = store->parseStorePath(ctxS);
if (!store->isValidPath(ctx))
throw InvalidPathError(store->printStorePath(ctx));
if (!outputName.empty() && ctx.isDerivation()) {
@@ -141,7 +141,7 @@ static void mkOutputString(
BindingsBuilder & attrs,
const StorePath & drvPath,
const BasicDerivation & drv,
const std::pair<std::string, DerivationOutput> & o)
const std::pair<string, DerivationOutput> & o)
{
auto optOutputPath = o.second.path(*state.store, drv.name, o.first);
attrs.alloc(o.first).mkString(
@@ -314,7 +314,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
{
auto path = realisePath(state, pos, *args[0]);
std::string sym(state.forceStringNoCtx(*args[1], pos));
string sym(state.forceStringNoCtx(*args[1], pos));
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
@@ -386,7 +386,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
std::string t;
string t;
switch (args[0]->type()) {
case nInt: t = "int"; break;
case nBool: t = "bool"; break;
@@ -694,32 +694,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info {
.name = "__genericClosure",
.args = {"attrset"},
.arity = 1,
.doc = R"(
Take an *attrset* with values named `startSet` and `operator` in order to
return a *list of attrsets* by starting with the `startSet`, recursively
applying the `operator` function to each element. The *attrsets* in the
`startSet` and produced by the `operator` must each contain value named
`key` which are comparable to each other. The result is produced by
repeatedly calling the operator for each element encountered with a
unique key, terminating when no new elements are produced. For example,
```
builtins.genericClosure {
startSet = [ {key = 5;} ];
operator = item: [{
key = if (item.key / 2 ) * 2 == item.key
then item.key / 2
else 3 * item.key + 1;
}];
}
```
evaluates to
```
[ { key = 5; } { key = 16; } { key = 8; } { key = 4; } { key = 2; } { key = 1; } ]
```
)",
.fun = prim_genericClosure,
});
@@ -732,7 +707,7 @@ static RegisterPrimOp primop_abort({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context).toOwned();
string s = state.coerceToString(pos, *args[0], context).toOwned();
throw Abort("evaluation aborted with the following error message: '%1%'", s);
}
});
@@ -750,7 +725,7 @@ static RegisterPrimOp primop_throw({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context).toOwned();
string s = state.coerceToString(pos, *args[0], context).toOwned();
throw ThrownError(s);
}
});
@@ -851,7 +826,7 @@ static RegisterPrimOp primop_tryEval({
/* Return an environment variable. Use with care. */
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
std::string name(state.forceStringNoCtx(*args[0], pos));
string name(state.forceStringNoCtx(*args[0], pos));
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
}
@@ -960,7 +935,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
pos
);
std::string drvName;
string drvName;
Pos & posDrvName(*attr->pos);
try {
drvName = state.forceStringNoCtx(*attr->value, pos);
@@ -989,17 +964,16 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
PathSet context;
bool contentAddressed = false;
bool isImpure = false;
std::optional<std::string> outputHash;
std::string outputHashAlgo;
std::optional<FileIngestionMethod> ingestionMethod;
auto ingestionMethod = FileIngestionMethod::Flat;
StringSet outputs;
outputs.insert("out");
for (auto & i : args[0]->attrs->lexicographicOrder()) {
if (i->name == state.sIgnoreNulls) continue;
const std::string & key = i->name;
const string & key = i->name;
vomit("processing attribute '%1%'", key);
auto handleHashMode = [&](const std::string_view s) {
@@ -1052,18 +1026,12 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
settings.requireExperimentalFeature(Xp::CaDerivations);
}
else if (i->name == state.sImpure) {
isImpure = state.forceBool(*i->value, pos);
if (isImpure)
settings.requireExperimentalFeature(Xp::ImpureDerivations);
}
/* The `args' attribute is special: it supplies the
command-line arguments to the builder. */
else if (i->name == state.sArgs) {
state.forceList(*i->value, pos);
for (auto elem : i->value->listItems()) {
auto s = state.coerceToString(posDrvName, *elem, context, true).toOwned();
string s = state.coerceToString(posDrvName, *elem, context, true).toOwned();
drv.args.push_back(s);
}
}
@@ -1150,8 +1118,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Handle derivation outputs of the form ‘!<name>!<path>’. */
else if (path.at(0) == '!') {
auto ctx = decodeContext(*state.store, path);
drv.inputDrvs[ctx.first].insert(ctx.second);
std::pair<string, string> ctx = decodeContext(path);
drv.inputDrvs[state.store->parseStorePath(ctx.first)].insert(ctx.second);
}
/* Otherwise it's a source file. */
@@ -1190,44 +1158,31 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
.errPos = posDrvName
});
auto h = newHashAllowEmpty(*outputHash, parseHashTypeOpt(outputHashAlgo));
std::optional<HashType> ht = parseHashTypeOpt(outputHashAlgo);
Hash h = newHashAllowEmpty(*outputHash, ht);
auto method = ingestionMethod.value_or(FileIngestionMethod::Flat);
auto outPath = state.store->makeFixedOutputPath(method, h, drvName);
auto outPath = state.store->makeFixedOutputPath(ingestionMethod, h, drvName);
drv.env["out"] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign("out",
DerivationOutput::CAFixed {
.hash = FixedOutputHash {
.method = method,
.hash = std::move(h),
drv.outputs.insert_or_assign("out", DerivationOutput {
.output = DerivationOutputCAFixed {
.hash = FixedOutputHash {
.method = ingestionMethod,
.hash = std::move(h),
},
},
});
});
}
else if (contentAddressed || isImpure) {
if (contentAddressed && isImpure)
throw EvalError({
.msg = hintfmt("derivation cannot be both content-addressed and impure"),
.errPos = posDrvName
});
auto ht = parseHashTypeOpt(outputHashAlgo).value_or(htSHA256);
auto method = ingestionMethod.value_or(FileIngestionMethod::Recursive);
else if (contentAddressed) {
HashType ht = parseHashType(outputHashAlgo);
for (auto & i : outputs) {
drv.env[i] = hashPlaceholder(i);
if (isImpure)
drv.outputs.insert_or_assign(i,
DerivationOutput::Impure {
.method = method,
.hashType = ht,
});
else
drv.outputs.insert_or_assign(i,
DerivationOutput::CAFloating {
.method = method,
.hashType = ht,
});
drv.outputs.insert_or_assign(i, DerivationOutput {
.output = DerivationOutputCAFloating {
.method = ingestionMethod,
.hashType = ht,
},
});
}
}
@@ -1241,29 +1196,44 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
for (auto & i : outputs) {
drv.env[i] = "";
drv.outputs.insert_or_assign(i,
DerivationOutput::Deferred { });
DerivationOutput {
.output = DerivationOutputInputAddressed {
.path = StorePath::dummy,
},
});
}
// Regular, non-CA derivation should always return a single hash and not
// hash per output.
auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true);
switch (hashModulo.kind) {
case DrvHash::Kind::Regular:
for (auto & i : outputs) {
auto h = hashModulo.hashes.at(i);
auto outPath = state.store->makeOutputPath(i, h, drvName);
drv.env[i] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign(
i,
DerivationOutputInputAddressed {
.path = std::move(outPath),
});
}
break;
;
case DrvHash::Kind::Deferred:
for (auto & i : outputs) {
drv.outputs.insert_or_assign(i, DerivationOutputDeferred {});
}
}
std::visit(overloaded {
[&](Hash & h) {
for (auto & i : outputs) {
auto outPath = state.store->makeOutputPath(i, h, drvName);
drv.env[i] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign(i,
DerivationOutput {
.output = DerivationOutputInputAddressed {
.path = std::move(outPath),
},
});
}
},
[&](CaOutputHashes &) {
// Shouldn't happen as the toplevel derivation is not CA.
assert(false);
},
[&](DeferredHash &) {
for (auto & i : outputs) {
drv.outputs.insert_or_assign(i,
DerivationOutput {
.output = DerivationOutputDeferred{},
});
}
},
},
hashModulo);
}
/* Write the resulting term into the Nix store directory. */
@@ -1274,9 +1244,12 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Optimisation, but required in read-only mode! because in that
case we don't actually write store derivations, so we can't
read them later. */
{
auto h = hashDerivationModulo(*state.store, drv, false);
read them later.
However, we don't bother doing this for floating CA derivations because
their "hash modulo" is indeterminate until built. */
if (drv.type() != DerivationType::CAFloating) {
auto h = hashDerivationModulo(*state.store, Derivation(drv), false);
drvHashes.lock()->insert_or_assign(drvPath, h);
}
@@ -1467,8 +1440,8 @@ static RegisterPrimOp primop_dirOf({
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto path = realisePath(state, pos, *args[0]);
auto s = readFile(path);
if (s.find((char) 0) != std::string::npos)
string s = readFile(path);
if (s.find((char) 0) != string::npos)
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
StorePathSet refs;
if (state.store->isInStore(path)) {
@@ -1501,7 +1474,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
for (auto v2 : args[0]->listItems()) {
state.forceAttrs(*v2, pos);
std::string prefix;
string prefix;
Bindings::iterator i = v2->attrs->find(state.sPrefix);
if (i != v2->attrs->end())
prefix = state.forceStringNoCtx(*i->value, pos);
@@ -1515,7 +1488,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
);
PathSet context;
auto path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
string path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
try {
auto rewrites = state.realiseContext(context);
@@ -1781,8 +1754,8 @@ static RegisterPrimOp primop_fromJSON({
static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
std::string name(state.forceStringNoCtx(*args[0], pos));
std::string contents(state.forceString(*args[1], context, pos));
string name(state.forceStringNoCtx(*args[0], pos));
string contents(state.forceString(*args[1], context, pos));
StorePathSet refs;
@@ -1890,7 +1863,7 @@ static RegisterPrimOp primop_toFile({
static void addPath(
EvalState & state,
const Pos & pos,
const std::string & name,
const string & name,
Path path,
Value * filterFun,
FileIngestionMethod method,
@@ -1946,15 +1919,20 @@ static void addPath(
if (expectedHash)
expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name);
Path dstPath;
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
StorePath dstPath = settings.readOnlyMode
dstPath = state.store->printStorePath(settings.readOnlyMode
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs);
if (expectedHash && expectedStorePath != dstPath)
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs));
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
state.allowAndSetStorePathString(dstPath, v);
} else
state.allowAndSetStorePathString(*expectedStorePath, v);
dstPath = state.store->printStorePath(*expectedStorePath);
v.mkString(dstPath, {dstPath});
state.allowPath(dstPath);
} catch (Error & e) {
e.addTrace(pos, "while adding path '%s'", path);
throw;
@@ -2038,14 +2016,14 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
{
state.forceAttrs(*args[0], pos);
Path path;
std::string name;
string name;
Value * filterFun = nullptr;
auto method = FileIngestionMethod::Recursive;
std::optional<Hash> expectedHash;
PathSet context;
for (auto & attr : *args[0]->attrs) {
auto & n(attr.name);
const string & n(attr.name);
if (n == "path")
path = state.coerceToPath(*attr.pos, *attr.value, context);
else if (attr.name == state.sName)
@@ -3638,7 +3616,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * *
auto sep = state.forceString(*args[0], context, pos);
state.forceList(*args[1], pos);
std::string res;
string res;
res.reserve((args[1]->listSize() + 32) * sep.size());
bool first = true;
@@ -3671,12 +3649,12 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
.errPos = pos
});
std::vector<std::string> from;
std::vector<string> from;
from.reserve(args[0]->listSize());
for (auto elem : args[0]->listItems())
from.emplace_back(state.forceString(*elem, pos));
std::vector<std::pair<std::string, PathSet>> to;
std::vector<std::pair<string, PathSet>> to;
to.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) {
PathSet ctx;
@@ -3687,7 +3665,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
PathSet context;
auto s = state.forceString(*args[2], context, pos);
std::string res;
string res;
// Loops one past last character to handle the case where 'from' contains an empty string.
for (size_t p = 0; p <= s.size(); ) {
bool found = false;
@@ -3828,7 +3806,7 @@ RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun)
.name = name,
.args = {},
.arity = arity,
.fun = fun,
.fun = fun
});
}
@@ -3900,17 +3878,13 @@ void EvalState::createBaseEnv()
if (RegisterPrimOp::primOps)
for (auto & primOp : *RegisterPrimOp::primOps)
if (!primOp.experimentalFeature
|| settings.isExperimentalFeatureEnabled(*primOp.experimentalFeature))
{
addPrimOp({
.fun = primOp.fun,
.arity = std::max(primOp.args.size(), primOp.arity),
.name = symbols.create(primOp.name),
.args = primOp.args,
.doc = primOp.doc,
});
}
addPrimOp({
.fun = primOp.fun,
.arity = std::max(primOp.args.size(), primOp.arity),
.name = symbols.create(primOp.name),
.args = primOp.args,
.doc = primOp.doc,
});
/* Add a wrapper around the derivation primop that computes the
`drvPath' and `outPath' attributes lazily. */
-2
View File
@@ -16,7 +16,6 @@ struct RegisterPrimOp
size_t arity = 0;
const char * doc;
PrimOpFun fun;
std::optional<ExperimentalFeature> experimentalFeature;
};
typedef std::vector<Info> PrimOps;
@@ -36,7 +35,6 @@ struct RegisterPrimOp
/* These primops are disabled without enableNativeCode, but plugins
may wish to use them in limited contexts without globally enabling
them. */
/* Load a ValueInitializer from a DSO and return whatever it initializes */
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v);
+6 -7
View File
@@ -1,6 +1,5 @@
#include "primops.hh"
#include "eval-inline.hh"
#include "derivations.hh"
#include "store-api.hh"
namespace nix {
@@ -38,7 +37,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & po
PathSet context2;
for (auto & p : context)
context2.insert(p.at(0) == '=' ? std::string(p, 1) : p);
context2.insert(p.at(0) == '=' ? string(p, 1) : p);
v.mkString(*s, context2);
}
@@ -77,14 +76,14 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args,
auto contextInfos = std::map<Path, ContextInfo>();
for (const auto & p : context) {
Path drv;
std::string output;
string output;
const Path * path = &p;
if (p.at(0) == '=') {
drv = std::string(p, 1);
drv = string(p, 1);
path = &drv;
} else if (p.at(0) == '!') {
NixStringContextElem ctx = decodeContext(*state.store, p);
drv = state.store->printStorePath(ctx.first);
std::pair<string, string> ctx = decodeContext(p);
drv = ctx.first;
output = ctx.second;
path = &drv;
}
@@ -167,7 +166,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
.errPos = *i.pos
});
}
context.insert("=" + std::string(i.name));
context.insert("=" + string(i.name));
}
}
-161
View File
@@ -1,161 +0,0 @@
#include "primops.hh"
#include "store-api.hh"
#include "make-content-addressed.hh"
#include "url.hh"
namespace nix {
static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
std::optional<std::string> fromStoreUrl;
std::optional<StorePath> fromPath;
bool toCA = false;
std::optional<StorePath> toPath;
for (auto & attr : *args[0]->attrs) {
if (attr.name == "fromPath") {
PathSet context;
fromPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
}
else if (attr.name == "toPath") {
state.forceValue(*attr.value, *attr.pos);
toCA = true;
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
PathSet context;
toPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
}
}
else if (attr.name == "fromStore")
fromStoreUrl = state.forceStringNoCtx(*attr.value, *attr.pos);
else
throw Error({
.msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attr.name),
.errPos = pos
});
}
if (!fromPath)
throw Error({
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
.errPos = pos
});
if (!fromStoreUrl)
throw Error({
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
.errPos = pos
});
auto parsedURL = parseURL(*fromStoreUrl);
if (parsedURL.scheme != "http" &&
parsedURL.scheme != "https" &&
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
throw Error({
.msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"),
.errPos = pos
});
if (!parsedURL.query.empty())
throw Error({
.msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
.errPos = pos
});
auto fromStore = openStore(parsedURL.to_string());
if (toCA) {
if (!toPath || !state.store->isValidPath(*toPath)) {
auto remappings = makeContentAddressed(*fromStore, *state.store, { *fromPath });
auto i = remappings.find(*fromPath);
assert(i != remappings.end());
if (toPath && *toPath != i->second)
throw Error({
.msg = hintfmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
state.store->printStorePath(*fromPath),
state.store->printStorePath(i->second),
state.store->printStorePath(*toPath)),
.errPos = pos
});
if (!toPath)
throw Error({
.msg = hintfmt(
"rewriting '%s' to content-addressed form yielded '%s'; "
"please set this in the 'toPath' attribute passed to 'fetchClosure'",
state.store->printStorePath(*fromPath),
state.store->printStorePath(i->second)),
.errPos = pos
});
}
} else {
if (!state.store->isValidPath(*fromPath))
copyClosure(*fromStore, *state.store, RealisedPath::Set { *fromPath });
toPath = fromPath;
}
/* In pure mode, require a CA path. */
if (evalSettings.pureEval) {
auto info = state.store->queryPathInfo(*toPath);
if (!info->isContentAddressed(*state.store))
throw Error({
.msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't",
state.store->printStorePath(*toPath)),
.errPos = pos
});
}
auto toPathS = state.store->printStorePath(*toPath);
v.mkString(toPathS, {toPathS});
}
static RegisterPrimOp primop_fetchClosure({
.name = "__fetchClosure",
.args = {"args"},
.doc = R"(
Fetch a Nix store closure from a binary cache, rewriting it into
content-addressed form. For example,
```nix
builtins.fetchClosure {
fromStore = "https://cache.nixos.org";
fromPath = /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1;
toPath = /nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1;
}
```
fetches `/nix/store/r2jd...` from the specified binary cache,
and rewrites it into the content-addressed store path
`/nix/store/ldbh...`.
If `fromPath` is already content-addressed, or if you are
allowing impure evaluation (`--impure`), then `toPath` may be
omitted.
To find out the correct value for `toPath` given a `fromPath`,
you can use `nix store make-content-addressed`:
```console
# nix store make-content-addressed --from https://cache.nixos.org /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1
rewrote '/nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1' to '/nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1'
```
This function is similar to `builtins.storePath` in that it
allows you to use a previously built store path in a Nix
expression. However, it is more reproducible because it requires
specifying a binary cache from which the path can be fetched.
Also, requiring a content-addressed final store path avoids the
need for users to configure binary cache public keys.
This function is only available if you enable the experimental
feature `fetch-closure`.
)",
.fun = prim_fetchClosure,
.experimentalFeature = Xp::FetchClosure,
});
}
+1 -1
View File
@@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
fetchers::Attrs attrs;
attrs.insert_or_assign("type", "hg");
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
attrs.insert_or_assign("name", std::string(name));
attrs.insert_or_assign("name", string(name));
if (ref) attrs.insert_or_assign("ref", *ref);
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(std::move(attrs));
+11 -23
View File
@@ -19,7 +19,7 @@ void emitTreeAttrs(
bool emptyRevFallback,
bool forceDirty)
{
assert(input.isLocked());
assert(input.isImmutable());
auto attrs = state.buildBindings(8);
@@ -145,7 +145,7 @@ static void fetchTree(
if (!params.allowNameArgument)
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
throw Error({
.msg = hintfmt("attribute 'name' isn't supported in call to 'fetchTree'"),
.msg = hintfmt("attribute 'name' isn’t supported in call to 'fetchTree'"),
.errPos = pos
});
@@ -166,8 +166,8 @@ static void fetchTree(
if (!evalSettings.pureEval && !input.isDirect())
input = lookupInRegistries(state.store, input).first;
if (evalSettings.pureEval && !input.isLocked())
throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", pos);
if (evalSettings.pureEval && !input.isImmutable())
throw Error("in pure evaluation mode, 'fetchTree' requires an immutable input, at %s", pos);
auto [tree, input2] = input.fetch(state.store);
@@ -186,7 +186,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree);
static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
const std::string & who, bool unpack, std::string name)
const string & who, bool unpack, std::string name)
{
std::optional<std::string> url;
std::optional<Hash> expectedHash;
@@ -198,7 +198,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
std::string n(attr.name);
string n(attr.name);
if (n == "url")
url = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (n == "sha256")
@@ -230,21 +230,6 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
if (evalSettings.pureEval && !expectedHash)
throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who);
// early exit if pinned and already in the store
if (expectedHash && expectedHash->type == htSHA256) {
auto expectedPath =
unpack
? state.store->makeFixedOutputPath(FileIngestionMethod::Recursive, *expectedHash, name, {})
: state.store->makeFixedOutputPath(FileIngestionMethod::Flat, *expectedHash, name, {});
if (state.store->isValidPath(expectedPath)) {
state.allowAndSetStorePathString(expectedPath, v);
return;
}
}
// TODO: fetching may fail, yet the path may be substitutable.
// https://github.com/NixOS/nix/issues/4313
auto storePath =
unpack
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath
@@ -259,7 +244,10 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
*url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
}
state.allowAndSetStorePathString(storePath, v);
state.allowPath(storePath);
auto path = state.store->printStorePath(storePath);
v.mkString(path, PathSet({path}));
}
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)
@@ -329,7 +317,7 @@ static RegisterPrimOp primop_fetchTarball({
.fun = prim_fetchTarball,
});
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
static void prim_fetchGit(EvalState &state, const Pos &pos, Value **args, Value &v)
{
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
}
+1 -1
View File
@@ -9,7 +9,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
{
auto toml = state.forceStringNoCtx(*args[0], pos);
std::istringstream tomlStream(std::string{toml});
std::istringstream tomlStream(string{toml});
std::function<void(Value &, toml::value)> visit;
+1 -2
View File
@@ -84,8 +84,7 @@ void printValueAsJSON(EvalState & state, bool strict,
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
.errPos = v.determinePos(pos)
});
e.addTrace(pos, hintfmt("message for the trace"));
throw e;
throw e.addTrace(pos, hintfmt("message for the trace"));
}
}
+1 -1
View File
@@ -9,7 +9,7 @@
namespace nix {
static XMLAttrs singletonAttrs(const std::string & name, const std::string & value)
static XMLAttrs singletonAttrs(const string & name, const string & value)
{
XMLAttrs attrs;
attrs[name] = value;
+3 -10
View File
@@ -57,8 +57,6 @@ struct ExprLambda;
struct PrimOp;
class Symbol;
struct Pos;
class StorePath;
class Store;
class EvalState;
class XMLWriter;
class JSONPlaceholder;
@@ -66,8 +64,6 @@ class JSONPlaceholder;
typedef int64_t NixInt;
typedef double NixFloat;
typedef std::pair<StorePath, std::string> NixStringContextElem;
typedef std::vector<NixStringContextElem> NixStringContext;
/* External values must descend from ExternalValueBase, so that
* type-agnostic nix functions (e.g. showType) can be implemented
@@ -118,14 +114,11 @@ struct Value
private:
InternalType internalType;
friend std::string showType(const Value & v);
void print(std::ostream & str, std::set<const void *> * seen) const;
friend std::string showType(const Value & v);
friend void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v);
public:
void print(std::ostream & str, bool showRepeated = false) const;
// Functions needed to distinguish the type
// These should be removed eventually, by putting the functionality that's
// needed by callers into methods of this type
@@ -375,7 +368,7 @@ public:
non-trivial. */
bool isTrivial() const;
NixStringContext getContext(const Store &);
std::vector<std::pair<Path, std::string>> getContext();
auto listItems()
{
+4 -4
View File
@@ -52,13 +52,13 @@ struct CacheImpl : Cache
const Attrs & inAttrs,
const Attrs & infoAttrs,
const StorePath & storePath,
bool locked) override
bool immutable) override
{
_state.lock()->add.use()
(attrsToJSON(inAttrs).dump())
(attrsToJSON(infoAttrs).dump())
(store->printStorePath(storePath))
(locked)
(immutable)
(time(0)).exec();
}
@@ -91,7 +91,7 @@ struct CacheImpl : Cache
auto infoJSON = stmt.getStr(0);
auto storePath = store->parseStorePath(stmt.getStr(1));
auto locked = stmt.getInt(2) != 0;
auto immutable = stmt.getInt(2) != 0;
auto timestamp = stmt.getInt(3);
store->addTempRoot(storePath);
@@ -105,7 +105,7 @@ struct CacheImpl : Cache
inAttrsJSON, infoJSON, store->printStorePath(storePath));
return Result {
.expired = !locked && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)),
.expired = !immutable && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)),
.infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJSON)),
.storePath = std::move(storePath)
};
+1 -1
View File
@@ -13,7 +13,7 @@ struct Cache
const Attrs & inAttrs,
const Attrs & infoAttrs,
const StorePath & storePath,
bool locked) = 0;
bool immutable) = 0;
virtual std::optional<std::pair<Attrs, StorePath>> lookup(
ref<Store> store,
-13
View File
@@ -1,13 +0,0 @@
#include "fetch-settings.hh"
namespace nix {
FetchSettings::FetchSettings()
{
}
FetchSettings fetchSettings;
static GlobalConfig::Register rFetchSettings(&fetchSettings);
}
-93
View File
@@ -1,93 +0,0 @@
#pragma once
#include "types.hh"
#include "config.hh"
#include "util.hh"
#include <map>
#include <limits>
#include <sys/types.h>
namespace nix {
struct FetchSettings : public Config
{
FetchSettings();
Setting<StringMap> accessTokens{this, {}, "access-tokens",
R"(
Access tokens used to access protected GitHub, GitLab, or
other locations requiring token-based authentication.
Access tokens are specified as a string made up of
space-separated `host=token` values. The specific token
used is selected by matching the `host` portion against the
"host" specification of the input. The actual use of the
`token` value is determined by the type of resource being
accessed:
* Github: the token value is the OAUTH-TOKEN string obtained
as the Personal Access Token from the Github server (see
https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps).
* Gitlab: the token value is either the OAuth2 token or the
Personal Access Token (these are different types tokens
for gitlab, see
https://docs.gitlab.com/12.10/ee/api/README.html#authentication).
The `token` value should be `type:tokenstring` where
`type` is either `OAuth2` or `PAT` to indicate which type
of token is being specified.
Example `~/.config/nix/nix.conf`:
```
access-tokens = github.com=23ac...b289 gitlab.mycompany.com=PAT:A123Bp_Cd..EfG gitlab.com=OAuth2:1jklw3jk
```
Example `~/code/flake.nix`:
```nix
input.foo = {
type = "gitlab";
host = "gitlab.mycompany.com";
owner = "mycompany";
repo = "pro";
};
```
This example specifies three tokens, one each for accessing
github.com, gitlab.mycompany.com, and sourceforge.net.
The `input.foo` uses the "gitlab" fetcher, which might
requires specifying the token type along with the token
value.
)"};
Setting<bool> allowDirty{this, true, "allow-dirty",
"Whether to allow dirty Git/Mercurial trees."};
Setting<bool> warnDirty{this, true, "warn-dirty",
"Whether to warn about dirty Git/Mercurial trees."};
Setting<std::string> flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry",
"Path or URI of the global flake registry."};
Setting<bool> useRegistries{this, true, "use-registries",
"Whether to use flake registries to resolve flake references."};
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
"Whether to accept nix configuration from a flake without prompting."};
Setting<std::string> commitLockFileSummary{
this, "", "commit-lockfile-summary",
R"(
The commit summary to use when committing changed flake lock files. If
empty, the summary is generated based on the action performed.
)"};
};
// FIXME: don't use a global variable.
extern FetchSettings fetchSettings;
}
+7 -16
View File
@@ -24,11 +24,11 @@ static void fixupInput(Input & input)
input.getType();
input.getRef();
if (input.getRev())
input.locked = true;
input.immutable = true;
input.getRevCount();
input.getLastModified();
if (input.getNarHash())
input.locked = true;
input.immutable = true;
}
Input Input::fromURL(const ParsedURL & url)
@@ -165,7 +165,7 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
input.to_string(), *prevRevCount);
}
input.locked = true;
input.immutable = true;
assert(input.hasAllInfo());
@@ -209,7 +209,7 @@ StorePath Input::computeStorePath(Store & store) const
{
auto narHash = getNarHash();
if (!narHash)
throw Error("cannot compute store path for unlocked input '%s'", to_string());
throw Error("cannot compute store path for mutable input '%s'", to_string());
return store.makeFixedOutputPath(FileIngestionMethod::Recursive, *narHash, getName());
}
@@ -238,18 +238,9 @@ std::optional<std::string> Input::getRef() const
std::optional<Hash> Input::getRev() const
{
std::optional<Hash> hash = {};
if (auto s = maybeGetStrAttr(attrs, "rev")) {
try {
hash = Hash::parseAnyPrefixed(*s);
} catch (BadHash &e) {
// Default to sha1 for backwards compatibility with existing flakes
hash = Hash::parseAny(*s, htSHA1);
}
}
return hash;
if (auto s = maybeGetStrAttr(attrs, "rev"))
return Hash::parseAny(*s, htSHA1);
return {};
}
std::optional<uint64_t> Input::getRevCount() const
+5 -7
View File
@@ -34,7 +34,7 @@ struct Input
std::shared_ptr<InputScheme> scheme; // note: can be null
Attrs attrs;
bool locked = false;
bool immutable = false;
bool direct = true;
/* path of the parent of this input, used for relative path resolution */
@@ -59,9 +59,9 @@ public:
one that goes through a registry. */
bool isDirect() const { return direct; }
/* Check whether this is a "locked" input, that is,
/* Check whether this is an "immutable" input, that is,
one that contains a commit hash or content hash. */
bool isLocked() const { return locked; }
bool isImmutable() const { return immutable; }
bool hasAllInfo() const;
@@ -69,8 +69,6 @@ public:
bool contains(const Input & other) const;
/* Fetch the input into the Nix store, returning the location in
the Nix store and the locked input. */
std::pair<Tree, Input> fetch(ref<Store> store) const;
Input applyOverrides(
@@ -148,14 +146,14 @@ DownloadFileResult downloadFile(
ref<Store> store,
const std::string & url,
const std::string & name,
bool locked,
bool immutable,
const Headers & headers = {});
std::pair<Tree, time_t> downloadTarball(
ref<Store> store,
const std::string & url,
const std::string & name,
bool locked,
bool immutable,
const Headers & headers = {});
}
+29 -63
View File
@@ -6,8 +6,6 @@
#include "url-parts.hh"
#include "pathlocks.hh"
#include "fetch-settings.hh"
#include <sys/time.h>
#include <sys/wait.h>
@@ -28,7 +26,9 @@ static std::string readHead(const Path & path)
static bool isNotDotGitDirectory(const Path & path)
{
return baseNameOf(path) != ".git";
static const std::regex gitDirRegex("^(?:.*/)?\\.git$");
return not std::regex_match(path, gitDirRegex);
}
struct GitInputScheme : InputScheme
@@ -187,16 +187,8 @@ struct GitInputScheme : InputScheme
if (submodules) cacheType += "-submodules";
if (allRefs) cacheType += "-all-refs";
auto checkHashType = [&](const std::optional<Hash> & hash)
auto getImmutableAttrs = [&]()
{
if (hash.has_value() && !(hash->type == htSHA1 || hash->type == htSHA256))
throw Error("Hash '%s' is not supported by Git. Supported types are sha1 and sha256.", hash->to_string(Base16, true));
};
auto getLockedAttrs = [&]()
{
checkHashType(input.getRev());
return Attrs({
{"type", cacheType},
{"name", name},
@@ -216,7 +208,7 @@ struct GitInputScheme : InputScheme
};
if (input.getRev()) {
if (auto res = getCache()->lookup(store, getLockedAttrs()))
if (auto res = getCache()->lookup(store, getImmutableAttrs()))
return makeResult(res->first, std::move(res->second));
}
@@ -228,46 +220,22 @@ struct GitInputScheme : InputScheme
if (!input.getRef() && !input.getRev() && isLocal) {
bool clean = false;
auto env = getEnv();
// Set LC_ALL to C: because we rely on the error messages from git rev-parse to determine what went wrong
// that way unknown errors can lead to a failure instead of continuing through the wrong code path
env["LC_ALL"] = "C";
/* Check whether this repo has any commits. There are
probably better ways to do this. */
auto gitDir = actualUrl + "/.git";
auto commonGitDir = chomp(runProgram(
"git",
true,
{ "-C", actualUrl, "rev-parse", "--git-common-dir" }
));
if (commonGitDir != ".git")
gitDir = commonGitDir;
/* Check whether HEAD points to something that looks like a commit,
since that is the refrence we want to use later on. */
auto result = runProgram(RunOptions {
.program = "git",
.args = { "-C", actualUrl, "--git-dir=.git", "rev-parse", "--verify", "--no-revs", "HEAD^{commit}" },
.environment = env,
.mergeStderrToStdout = true
});
auto exitCode = WEXITSTATUS(result.first);
auto errorMessage = result.second;
bool haveCommits = !readDirectory(gitDir + "/refs/heads").empty();
if (errorMessage.find("fatal: not a git repository") != std::string::npos) {
throw Error("'%s' is not a Git repository", actualUrl);
} else if (errorMessage.find("fatal: Needed a single revision") != std::string::npos) {
// indicates that the repo does not have any commits
// we want to proceed and will consider it dirty later
} else if (exitCode != 0) {
// any other errors should lead to a failure
throw Error("getting the HEAD of the Git tree '%s' failed with exit code %d:\n%s", actualUrl, exitCode, errorMessage);
}
bool hasHead = exitCode == 0;
try {
if (hasHead) {
// Using git diff is preferrable over lower-level operations here,
// because its conceptually simpler and we only need the exit code anyways.
auto gitDiffOpts = Strings({ "-C", actualUrl, "diff", "HEAD", "--quiet"});
if (!submodules) {
// Changes in submodules should only make the tree dirty
// when those submodules will be copied as well.
gitDiffOpts.emplace_back("--ignore-submodules");
}
gitDiffOpts.emplace_back("--");
runProgram("git", true, gitDiffOpts);
if (haveCommits) {
runProgram("git", true, { "-C", actualUrl, "diff-index", "--quiet", "HEAD", "--" });
clean = true;
}
} catch (ExecError & e) {
@@ -278,10 +246,10 @@ struct GitInputScheme : InputScheme
/* This is an unclean working tree. So copy all tracked files. */
if (!fetchSettings.allowDirty)
if (!settings.allowDirty)
throw Error("Git tree '%s' is dirty", actualUrl);
if (fetchSettings.warnDirty)
if (settings.warnDirty)
warn("Git tree '%s' is dirty", actualUrl);
auto gitOpts = Strings({ "-C", actualUrl, "ls-files", "-z" });
@@ -291,11 +259,9 @@ struct GitInputScheme : InputScheme
auto files = tokenizeString<std::set<std::string>>(
runProgram("git", true, gitOpts), "\0"s);
Path actualPath(absPath(actualUrl));
PathFilter filter = [&](const Path & p) -> bool {
assert(hasPrefix(p, actualPath));
std::string file(p, actualPath.size() + 1);
assert(hasPrefix(p, actualUrl));
std::string file(p, actualUrl.size() + 1);
auto st = lstat(p);
@@ -308,13 +274,13 @@ struct GitInputScheme : InputScheme
return files.count(file);
};
auto storePath = store->addToStore(input.getName(), actualPath, FileIngestionMethod::Recursive, htSHA256, filter);
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
// FIXME: maybe we should use the timestamp of the last
// modified dirty file?
input.attrs.insert_or_assign(
"lastModified",
hasHead ? std::stoull(runProgram("git", true, { "-C", actualPath, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
haveCommits ? std::stoull(runProgram("git", true, { "-C", actualUrl, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
return {std::move(storePath), input};
}
@@ -322,7 +288,7 @@ struct GitInputScheme : InputScheme
if (!input.getRef()) input.attrs.insert_or_assign("ref", isLocal ? readHead(actualUrl) : "master");
Attrs unlockedAttrs({
Attrs mutableAttrs({
{"type", cacheType},
{"name", name},
{"url", actualUrl},
@@ -341,7 +307,7 @@ struct GitInputScheme : InputScheme
} else {
if (auto res = getCache()->lookup(store, unlockedAttrs)) {
if (auto res = getCache()->lookup(store, mutableAttrs)) {
auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), htSHA1);
if (!input.getRev() || input.getRev() == rev2) {
input.attrs.insert_or_assign("rev", rev2.gitRev());
@@ -438,7 +404,7 @@ struct GitInputScheme : InputScheme
/* Now that we know the ref, check again whether we have it in
the store. */
if (auto res = getCache()->lookup(store, getLockedAttrs()))
if (auto res = getCache()->lookup(store, getImmutableAttrs()))
return makeResult(res->first, std::move(res->second));
Path tmpDir = createTempDir();
@@ -510,14 +476,14 @@ struct GitInputScheme : InputScheme
if (!_input.getRev())
getCache()->add(
store,
unlockedAttrs,
mutableAttrs,
infoAttrs,
storePath,
false);
getCache()->add(
store,
getLockedAttrs(),
getImmutableAttrs(),
infoAttrs,
storePath,
true);
+9 -13
View File
@@ -1,13 +1,11 @@
#include "filetransfer.hh"
#include "cache.hh"
#include "fetchers.hh"
#include "globals.hh"
#include "store-api.hh"
#include "types.hh"
#include "url-parts.hh"
#include "fetchers.hh"
#include "fetch-settings.hh"
#include <optional>
#include <nlohmann/json.hpp>
#include <fstream>
@@ -159,7 +157,7 @@ struct GitArchiveInputScheme : InputScheme
std::optional<std::string> getAccessToken(const std::string & host) const
{
auto tokens = fetchSettings.accessTokens.get();
auto tokens = settings.accessTokens.get();
if (auto token = get(tokens, host))
return *token;
return {};
@@ -195,12 +193,12 @@ struct GitArchiveInputScheme : InputScheme
input.attrs.erase("ref");
input.attrs.insert_or_assign("rev", rev->gitRev());
Attrs lockedAttrs({
Attrs immutableAttrs({
{"type", "git-tarball"},
{"rev", rev->gitRev()},
});
if (auto res = getCache()->lookup(store, lockedAttrs)) {
if (auto res = getCache()->lookup(store, immutableAttrs)) {
input.attrs.insert_or_assign("lastModified", getIntAttr(res->first, "lastModified"));
return {std::move(res->second), input};
}
@@ -213,7 +211,7 @@ struct GitArchiveInputScheme : InputScheme
getCache()->add(
store,
lockedAttrs,
immutableAttrs,
{
{"rev", rev->gitRev()},
{"lastModified", uint64_t(lastModified)}
@@ -390,7 +388,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
ref_uri = line.substr(ref_index+5, line.length()-1);
} else
ref_uri = fmt("refs/(heads|tags)/%s", ref);
ref_uri = fmt("refs/heads/%s", ref);
auto file = store->toRealPath(
downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath);
@@ -399,11 +397,9 @@ struct SourceHutInputScheme : GitArchiveInputScheme
std::string line;
std::string id;
while(getline(is, line)) {
// Append $ to avoid partial name matches
std::regex pattern(fmt("%s$", ref_uri));
if (std::regex_search(line, pattern)) {
id = line.substr(0, line.find('\t'));
auto index = line.find(ref_uri);
if (index != std::string::npos) {
id = line.substr(0, index-1);
break;
}
}
+14 -27
View File
@@ -5,8 +5,6 @@
#include "store-api.hh"
#include "url-parts.hh"
#include "fetch-settings.hh"
#include <sys/time.h>
using namespace std::string_literals;
@@ -28,7 +26,7 @@ static RunOptions hgOptions(const Strings & args)
}
// runProgram wrapper that uses hgOptions instead of stock RunOptions.
static std::string runHg(const Strings & args, const std::optional<std::string> & input = {})
static string runHg(const Strings & args, const std::optional<std::string> & input = {})
{
RunOptions opts = hgOptions(args);
opts.input = input;
@@ -167,10 +165,10 @@ struct MercurialInputScheme : InputScheme
/* This is an unclean working tree. So copy all tracked
files. */
if (!fetchSettings.allowDirty)
if (!settings.allowDirty)
throw Error("Mercurial tree '%s' is unclean", actualUrl);
if (fetchSettings.warnDirty)
if (settings.warnDirty)
warn("Mercurial tree '%s' is unclean", actualUrl);
input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl })));
@@ -178,11 +176,9 @@ struct MercurialInputScheme : InputScheme
auto files = tokenizeString<std::set<std::string>>(
runHg({ "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);
Path actualPath(absPath(actualUrl));
PathFilter filter = [&](const Path & p) -> bool {
assert(hasPrefix(p, actualPath));
std::string file(p, actualPath.size() + 1);
assert(hasPrefix(p, actualUrl));
std::string file(p, actualUrl.size() + 1);
auto st = lstat(p);
@@ -195,7 +191,7 @@ struct MercurialInputScheme : InputScheme
return files.count(file);
};
auto storePath = store->addToStore(input.getName(), actualPath, FileIngestionMethod::Recursive, htSHA256, filter);
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
return {std::move(storePath), input};
}
@@ -203,17 +199,8 @@ struct MercurialInputScheme : InputScheme
if (!input.getRef()) input.attrs.insert_or_assign("ref", "default");
auto checkHashType = [&](const std::optional<Hash> & hash)
auto getImmutableAttrs = [&]()
{
if (hash.has_value() && hash->type != htSHA1)
throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", hash->to_string(Base16, true));
};
auto getLockedAttrs = [&]()
{
checkHashType(input.getRev());
return Attrs({
{"type", "hg"},
{"name", name},
@@ -231,20 +218,20 @@ struct MercurialInputScheme : InputScheme
};
if (input.getRev()) {
if (auto res = getCache()->lookup(store, getLockedAttrs()))
if (auto res = getCache()->lookup(store, getImmutableAttrs()))
return makeResult(res->first, std::move(res->second));
}
auto revOrRef = input.getRev() ? input.getRev()->gitRev() : *input.getRef();
Attrs unlockedAttrs({
Attrs mutableAttrs({
{"type", "hg"},
{"name", name},
{"url", actualUrl},
{"ref", *input.getRef()},
});
if (auto res = getCache()->lookup(store, unlockedAttrs)) {
if (auto res = getCache()->lookup(store, mutableAttrs)) {
auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), htSHA1);
if (!input.getRev() || input.getRev() == rev2) {
input.attrs.insert_or_assign("rev", rev2.gitRev());
@@ -267,7 +254,7 @@ struct MercurialInputScheme : InputScheme
runHg({ "pull", "-R", cacheDir, "--", actualUrl });
}
catch (ExecError & e) {
auto transJournal = cacheDir + "/.hg/store/journal";
string transJournal = cacheDir + "/.hg/store/journal";
/* hg throws "abandoned transaction" error only if this file exists */
if (pathExists(transJournal)) {
runHg({ "recover", "-R", cacheDir });
@@ -290,7 +277,7 @@ struct MercurialInputScheme : InputScheme
auto revCount = std::stoull(tokens[1]);
input.attrs.insert_or_assign("ref", tokens[2]);
if (auto res = getCache()->lookup(store, getLockedAttrs()))
if (auto res = getCache()->lookup(store, getImmutableAttrs()))
return makeResult(res->first, std::move(res->second));
Path tmpDir = createTempDir();
@@ -310,14 +297,14 @@ struct MercurialInputScheme : InputScheme
if (!_input.getRev())
getCache()->add(
store,
unlockedAttrs,
mutableAttrs,
infoAttrs,
storePath,
false);
getCache()->add(
store,
getLockedAttrs(),
getImmutableAttrs(),
infoAttrs,
storePath,
true);
+3 -11
View File
@@ -1,6 +1,5 @@
#include "fetchers.hh"
#include "store-api.hh"
#include "archive.hh"
namespace nix::fetchers {
@@ -81,9 +80,8 @@ struct PathInputScheme : InputScheme
// nothing to do
}
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
{
Input input(_input);
std::string absPath;
auto path = getStrAttr(input.attrs, "path");
@@ -113,15 +111,9 @@ struct PathInputScheme : InputScheme
if (storePath)
store->addTempRoot(*storePath);
time_t mtime = 0;
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) {
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath))
// FIXME: try to substitute storePath.
auto src = sinkToSource([&](Sink & sink) {
mtime = dumpPathAndGetMtime(absPath, sink, defaultPathFilter);
});
storePath = store->addToStoreFromDump(*src, "source");
}
input.attrs.insert_or_assign("lastModified", uint64_t(mtime));
storePath = store->addToStore("source", absPath);
return {std::move(*storePath), input};
}
+1 -3
View File
@@ -5,8 +5,6 @@
#include "store-api.hh"
#include "local-fs-store.hh"
#include "fetch-settings.hh"
#include <nlohmann/json.hpp>
namespace nix::fetchers {
@@ -152,7 +150,7 @@ void overrideRegistry(
static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store)
{
static auto reg = [&]() {
auto path = fetchSettings.flakeRegistry.get();
auto path = settings.flakeRegistry.get();
if (!hasPrefix(path, "/")) {
auto storePath = downloadFile(store, path, "flake-registry.json", false).storePath;
+7 -7
View File
@@ -13,7 +13,7 @@ DownloadFileResult downloadFile(
ref<Store> store,
const std::string & url,
const std::string & name,
bool locked,
bool immutable,
const Headers & headers)
{
// FIXME: check store
@@ -88,7 +88,7 @@ DownloadFileResult downloadFile(
inAttrs,
infoAttrs,
*storePath,
locked);
immutable);
if (url != res.effectiveUri)
getCache()->add(
@@ -100,7 +100,7 @@ DownloadFileResult downloadFile(
},
infoAttrs,
*storePath,
locked);
immutable);
return {
.storePath = std::move(*storePath),
@@ -113,7 +113,7 @@ std::pair<Tree, time_t> downloadTarball(
ref<Store> store,
const std::string & url,
const std::string & name,
bool locked,
bool immutable,
const Headers & headers)
{
Attrs inAttrs({
@@ -130,7 +130,7 @@ std::pair<Tree, time_t> downloadTarball(
getIntAttr(cached->infoAttrs, "lastModified")
};
auto res = downloadFile(store, url, name, locked, headers);
auto res = downloadFile(store, url, name, immutable, headers);
std::optional<StorePath> unpackedStorePath;
time_t lastModified;
@@ -160,7 +160,7 @@ std::pair<Tree, time_t> downloadTarball(
inAttrs,
infoAttrs,
*unpackedStorePath,
locked);
immutable);
return {
Tree { .actualPath = store->toRealPath(*unpackedStorePath), .storePath = std::move(*unpackedStorePath) },
@@ -202,7 +202,7 @@ struct TarballInputScheme : InputScheme
Input input;
input.attrs = attrs;
//input.locked = (bool) maybeGetStrAttr(input.attrs, "hash");
//input.immutable = (bool) maybeGetStrAttr(input.attrs, "hash");
return input;
}
+1 -1
View File
@@ -4,7 +4,7 @@
namespace nix {
MixCommonArgs::MixCommonArgs(const std::string & programName)
MixCommonArgs::MixCommonArgs(const string & programName)
: programName(programName)
{
addFlag({
+2 -2
View File
@@ -11,8 +11,8 @@ class MixCommonArgs : public virtual Args
{
void initialFlagsProcessed() override;
public:
std::string programName;
MixCommonArgs(const std::string & programName);
string programName;
MixCommonArgs(const string & programName);
protected:
virtual void pluginsInited() {}
};
+7 -8
View File
@@ -1,7 +1,6 @@
#include "globals.hh"
#include "shared.hh"
#include "store-api.hh"
#include "gc-store.hh"
#include "util.hh"
#include "loggers.hh"
@@ -95,7 +94,7 @@ void printMissing(ref<Store> store, const StorePathSet & willBuild,
}
std::string getArg(const std::string & opt,
string getArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end)
{
++i;
@@ -323,14 +322,14 @@ void parseCmdLine(int argc, char * * argv,
}
void parseCmdLine(const std::string & programName, const Strings & args,
void parseCmdLine(const string & programName, const Strings & args,
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
{
LegacyArgs(programName, parseArg).parseCmdline(args);
}
void printVersion(const std::string & programName)
void printVersion(const string & programName)
{
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
if (verbosity > lvlInfo) {
@@ -353,7 +352,7 @@ void printVersion(const std::string & programName)
}
void showManPage(const std::string & name)
void showManPage(const string & name)
{
restoreProcessContext();
setenv("MANPATH", settings.nixManDir.c_str(), 1);
@@ -362,13 +361,13 @@ void showManPage(const std::string & name)
}
int handleExceptions(const std::string & programName, std::function<void()> fun)
int handleExceptions(const string & programName, std::function<void()> fun)
{
ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this
ErrorInfo::programName = baseNameOf(programName);
std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
string error = ANSI_RED "error:" ANSI_NORMAL " ";
try {
try {
fun();
@@ -408,7 +407,7 @@ RunPager::RunPager()
if (!isatty(STDOUT_FILENO)) return;
char * pager = getenv("NIX_PAGER");
if (!pager) pager = getenv("PAGER");
if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return;
if (pager && ((string) pager == "" || (string) pager == "cat")) return;
Pipe toPager;
toPager.create();
+7 -7
View File
@@ -22,7 +22,7 @@ public:
virtual ~Exit();
};
int handleExceptions(const std::string & programName, std::function<void()> fun);
int handleExceptions(const string & programName, std::function<void()> fun);
/* Don't forget to call initPlugins() after settings are initialized! */
void initNix();
@@ -30,10 +30,10 @@ void initNix();
void parseCmdLine(int argc, char * * argv,
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg);
void parseCmdLine(const std::string & programName, const Strings & args,
void parseCmdLine(const string & programName, const Strings & args,
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg);
void printVersion(const std::string & programName);
void printVersion(const string & programName);
/* Ugh. No better place to put this. */
void printGCWarning();
@@ -50,10 +50,10 @@ void printMissing(ref<Store> store, const StorePathSet & willBuild,
const StorePathSet & willSubstitute, const StorePathSet & unknown,
uint64_t downloadSize, uint64_t narSize, Verbosity lvl = lvlInfo);
std::string getArg(const std::string & opt,
string getArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end);
template<class N> N getIntArg(const std::string & opt,
template<class N> N getIntArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end, bool allowUnit)
{
++i;
@@ -76,7 +76,7 @@ struct LegacyArgs : public MixCommonArgs
/* Show the manual page for the specified program. */
void showManPage(const std::string & name);
void showManPage(const string & name);
/* The constructor of this class starts a pager if stdout is a
terminal and $PAGER is set. Stdout is redirected to the pager. */
@@ -96,7 +96,7 @@ extern volatile ::sig_atomic_t blockInt;
/* GC helpers. */
std::string showBytes(uint64_t bytes);
string showBytes(uint64_t bytes);
struct GCResults;
+4 -13
View File
@@ -385,14 +385,8 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
}});
}
StorePath BinaryCacheStore::addToStore(
std::string_view name,
const Path & srcPath,
FileIngestionMethod method,
HashType hashAlgo,
PathFilter & filter,
RepairFlag repair,
const StorePathSet & references)
StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair, const StorePathSet & references)
{
/* FIXME: Make BinaryCacheStore::addToStoreCommon support
non-recursive+sha256 so we can just use the default
@@ -424,11 +418,8 @@ StorePath BinaryCacheStore::addToStore(
})->path;
}
StorePath BinaryCacheStore::addTextToStore(
std::string_view name,
std::string_view s,
const StorePathSet & references,
RepairFlag repair)
StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s,
const StorePathSet & references, RepairFlag repair)
{
auto textHash = hashString(htSHA256, s);
auto path = makeTextPath(name, textHash, references);
+6 -17
View File
@@ -2,7 +2,6 @@
#include "crypto.hh"
#include "store-api.hh"
#include "log-store.hh"
#include "pool.hh"
@@ -29,9 +28,7 @@ struct BinaryCacheStoreConfig : virtual StoreConfig
"other than -1 which we reserve to indicate Nix defaults should be used"};
};
class BinaryCacheStore : public virtual BinaryCacheStoreConfig,
public virtual Store,
public virtual LogStore
class BinaryCacheStore : public virtual BinaryCacheStoreConfig, public virtual Store
{
private:
@@ -104,20 +101,12 @@ public:
StorePath addToStoreFromDump(Source & dump, std::string_view name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override;
StorePath addToStore(
std::string_view name,
const Path & srcPath,
FileIngestionMethod method,
HashType hashAlgo,
PathFilter & filter,
RepairFlag repair,
const StorePathSet & references) override;
StorePath addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method, HashType hashAlgo,
PathFilter & filter, RepairFlag repair, const StorePathSet & references) override;
StorePath addTextToStore(
std::string_view name,
std::string_view s,
const StorePathSet & references,
RepairFlag repair) override;
StorePath addTextToStore(const string & name, const string & s,
const StorePathSet & references, RepairFlag repair) override;
void registerDrvOutput(const Realisation & info) override;
-92
View File
@@ -1,92 +0,0 @@
#pragma once
#include "realisation.hh"
#include "derived-path.hh"
#include <string>
#include <chrono>
namespace nix {
struct BuildResult
{
/* Note: don't remove status codes, and only add new status codes
at the end of the list, to prevent client/server
incompatibilities in the nix-store --serve protocol. */
enum Status {
Built = 0,
Substituted,
AlreadyValid,
PermanentFailure,
InputRejected,
OutputRejected,
TransientFailure, // possibly transient
CachedFailure, // no longer used
TimedOut,
MiscFailure,
DependencyFailed,
LogLimitExceeded,
NotDeterministic,
ResolvesToAlreadyValid,
NoSubstituters,
} status = MiscFailure;
// FIXME: include entire ErrorInfo object.
std::string errorMsg;
std::string toString() const {
auto strStatus = [&]() {
switch (status) {
case Built: return "Built";
case Substituted: return "Substituted";
case AlreadyValid: return "AlreadyValid";
case PermanentFailure: return "PermanentFailure";
case InputRejected: return "InputRejected";
case OutputRejected: return "OutputRejected";
case TransientFailure: return "TransientFailure";
case CachedFailure: return "CachedFailure";
case TimedOut: return "TimedOut";
case MiscFailure: return "MiscFailure";
case DependencyFailed: return "DependencyFailed";
case LogLimitExceeded: return "LogLimitExceeded";
case NotDeterministic: return "NotDeterministic";
case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid";
default: return "Unknown";
};
}();
return strStatus + ((errorMsg == "") ? "" : " : " + errorMsg);
}
/* How many times this build was performed. */
unsigned int timesBuilt = 0;
/* If timesBuilt > 1, whether some builds did not produce the same
result. (Note that 'isNonDeterministic = false' does not mean
the build is deterministic, just that we don't have evidence of
non-determinism.) */
bool isNonDeterministic = false;
/* The derivation we built or the store path we substituted. */
DerivedPath path;
/* For derivations, a mapping from the names of the wanted outputs
to actual paths. */
DrvOutputs builtOutputs;
/* The start/stop times of the build (or one of the rounds, if it
was repeated). */
time_t startTime = 0, stopTime = 0;
bool success()
{
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
}
void rethrow()
{
throw Error("%s", errorMsg);
}
};
}
+174 -241
View File
@@ -66,7 +66,7 @@ namespace nix {
DerivationGoal::DerivationGoal(const StorePath & drvPath,
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
: Goal(worker)
, useDerivation(true)
, drvPath(drvPath)
, wantedOutputs(wantedOutputs)
@@ -85,7 +85,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
: Goal(worker)
, useDerivation(false)
, drvPath(drvPath)
, wantedOutputs(wantedOutputs)
@@ -116,7 +116,7 @@ DerivationGoal::~DerivationGoal()
}
std::string DerivationGoal::key()
string DerivationGoal::key()
{
/* Ensure that derivations get built in order of their name,
i.e. a derivation named "aardvark" always comes before
@@ -135,7 +135,7 @@ void DerivationGoal::killChild()
void DerivationGoal::timedOut(Error && ex)
{
killChild();
done(BuildResult::TimedOut, {}, ex);
done(BuildResult::TimedOut, ex);
}
@@ -182,7 +182,7 @@ void DerivationGoal::loadDerivation()
trace("loading derivation");
if (nrFailed != 0) {
done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
done(BuildResult::MiscFailure, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
return;
}
@@ -204,33 +204,10 @@ void DerivationGoal::haveDerivation()
{
trace("have derivation");
parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
if (!drv->type().hasKnownOutputPaths())
if (drv->type() == DerivationType::CAFloating)
settings.requireExperimentalFeature(Xp::CaDerivations);
if (!drv->type().isPure()) {
settings.requireExperimentalFeature(Xp::ImpureDerivations);
for (auto & [outputName, output] : drv->outputs) {
auto randomPath = StorePath::random(outputPathName(drv->name, outputName));
assert(!worker.store.isValidPath(randomPath));
initialOutputs.insert({
outputName,
InitialOutput {
.wanted = true,
.outputHash = impureOutputHash,
.known = InitialOutputStatus {
.path = randomPath,
.status = PathStatus::Absent
}
}
});
}
gaveUpOnSubstitution();
return;
}
retrySubstitution = false;
for (auto & i : drv->outputsAndOptPaths(worker.store))
if (i.second.second)
@@ -238,23 +215,34 @@ void DerivationGoal::haveDerivation()
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
for (auto & [outputName, outputHash] : outputHashes)
initialOutputs.insert({
initialOutputs.insert({
outputName,
InitialOutput {
InitialOutput{
.wanted = true, // Will be refined later
.outputHash = outputHash
}
});
});
/* Check what outputs paths are not already valid. */
auto [allValid, validOutputs] = checkPathValidity();
checkPathValidity();
bool allValid = true;
for (auto & [_, status] : initialOutputs) {
if (!status.wanted) continue;
if (!status.known || !status.known->isValid()) {
allValid = false;
break;
}
}
/* If they are all valid, then we're done. */
if (allValid && buildMode == bmNormal) {
done(BuildResult::AlreadyValid, std::move(validOutputs));
done(BuildResult::AlreadyValid);
return;
}
parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
/* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build
them. */
@@ -288,10 +276,8 @@ void DerivationGoal::outputsSubstitutionTried()
{
trace("all outputs substituted (maybe)");
assert(drv->type().isPure());
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) {
done(BuildResult::TransientFailure, {},
done(BuildResult::TransientFailure,
Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
worker.store.printStorePath(drvPath)));
return;
@@ -315,17 +301,23 @@ void DerivationGoal::outputsSubstitutionTried()
return;
}
auto [allValid, validOutputs] = checkPathValidity();
checkPathValidity();
size_t nrInvalid = 0;
for (auto & [_, status] : initialOutputs) {
if (!status.wanted) continue;
if (!status.known || !status.known->isValid())
nrInvalid++;
}
if (buildMode == bmNormal && allValid) {
done(BuildResult::Substituted, std::move(validOutputs));
if (buildMode == bmNormal && nrInvalid == 0) {
done(BuildResult::Substituted);
return;
}
if (buildMode == bmRepair && allValid) {
if (buildMode == bmRepair && nrInvalid == 0) {
repairClosure();
return;
}
if (buildMode == bmCheck && !allValid)
if (buildMode == bmCheck && nrInvalid > 0)
throw Error("some outputs of '%s' are not valid, so checking is not possible",
worker.store.printStorePath(drvPath));
@@ -333,27 +325,18 @@ void DerivationGoal::outputsSubstitutionTried()
gaveUpOnSubstitution();
}
/* At least one of the output paths could not be
produced using a substitute. So we have to build instead. */
void DerivationGoal::gaveUpOnSubstitution()
{
/* The inputs must be built before we can build this goal. */
inputDrvOutputs.clear();
if (useDerivation)
for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
/* Ensure that pure, non-fixed-output derivations don't
depend on impure derivations. */
if (drv->type().isPure() && !drv->type().isFixed()) {
auto inputDrv = worker.evalStore.readDerivation(i.first);
if (!inputDrv.type().isPure())
throw Error("pure derivation '%s' depends on impure derivation '%s'",
worker.store.printStorePath(drvPath),
worker.store.printStorePath(i.first));
}
/* Make sure checkPathValidity() from now on checks all
outputs. */
wantedOutputs.clear();
/* The inputs must be built before we can build this goal. */
if (useDerivation)
for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs)
addWaitee(worker.makeDerivationGoal(i.first, i.second, buildMode == bmRepair ? bmRepair : bmNormal));
}
/* Copy the input sources from the eval store to the build
store. */
@@ -381,8 +364,6 @@ void DerivationGoal::gaveUpOnSubstitution()
void DerivationGoal::repairClosure()
{
assert(drv->type().isPure());
/* If we're repairing, we now know that our own outputs are valid.
Now check whether the other paths in the outputs closure are
good. If not, then start derivation goals for the derivations
@@ -428,7 +409,7 @@ void DerivationGoal::repairClosure()
}
if (waitees.empty()) {
done(BuildResult::AlreadyValid, assertPathValidity());
done(BuildResult::AlreadyValid);
return;
}
@@ -442,7 +423,7 @@ void DerivationGoal::closureRepaired()
if (nrFailed > 0)
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
worker.store.printStorePath(drvPath));
done(BuildResult::AlreadyValid, assertPathValidity());
done(BuildResult::AlreadyValid);
}
@@ -453,14 +434,13 @@ void DerivationGoal::inputsRealised()
if (nrFailed != 0) {
if (!useDerivation)
throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath));
done(BuildResult::DependencyFailed, {}, Error(
done(BuildResult::DependencyFailed, Error(
"%s dependencies of derivation '%s' failed to build",
nrFailed, worker.store.printStorePath(drvPath)));
return;
}
if (retrySubstitution && !retriedSubstitution) {
retriedSubstitution = true;
if (retrySubstitution) {
haveDerivation();
return;
}
@@ -474,40 +454,19 @@ void DerivationGoal::inputsRealised()
if (useDerivation) {
auto & fullDrv = *dynamic_cast<Derivation *>(drv.get());
auto drvType = fullDrv.type();
bool resolveDrv = std::visit(overloaded {
[&](const DerivationType::InputAddressed & ia) {
/* must resolve if deferred. */
return ia.deferred;
},
[&](const DerivationType::ContentAddressed & ca) {
return !fullDrv.inputDrvs.empty() && (
ca.fixed
/* Can optionally resolve if fixed, which is good
for avoiding unnecessary rebuilds. */
? settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
/* Must resolve if floating and there are any inputs
drvs. */
: true);
},
[&](const DerivationType::Impure &) {
return true;
}
}, drvType.raw());
if (resolveDrv && !fullDrv.inputDrvs.empty()) {
settings.requireExperimentalFeature(Xp::CaDerivations);
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) &&
((!fullDrv.inputDrvs.empty() && derivationIsCA(fullDrv.type()))
|| fullDrv.type() == DerivationType::DeferredInputAddressed)) {
/* We are be able to resolve this derivation based on the
now-known results of dependencies. If so, we become a
stub goal aliasing that resolved derivation goal. */
std::optional attempt = fullDrv.tryResolve(worker.store, inputDrvOutputs);
now-known results of dependencies. If so, we become a stub goal
aliasing that resolved derivation goal */
std::optional attempt = fullDrv.tryResolve(worker.store);
assert(attempt);
Derivation drvResolved { *std::move(attempt) };
auto pathResolved = writeDerivation(worker.store, drvResolved);
auto msg = fmt("resolved derivation: '%s' -> '%s'",
auto msg = fmt("Resolved derivation: '%s' -> '%s'",
worker.store.printStorePath(drvPath),
worker.store.printStorePath(pathResolved));
act = std::make_unique<Activity>(*logger, lvlInfo, actBuildWaiting, msg,
@@ -528,13 +487,21 @@ void DerivationGoal::inputsRealised()
/* Add the relevant output closures of the input derivation
`i' as input paths. Only add the closures of output paths
that are specified as inputs. */
for (auto & j : wantedDepOutputs)
if (auto outPath = get(inputDrvOutputs, { depDrvPath, j }))
worker.store.computeFSClosure(*outPath, inputPaths);
else
assert(worker.evalStore.isValidPath(drvPath));
auto outputs = worker.evalStore.queryPartialDerivationOutputMap(depDrvPath);
for (auto & j : wantedDepOutputs) {
if (outputs.count(j) > 0) {
auto optRealizedInput = outputs.at(j);
if (!optRealizedInput)
throw Error(
"derivation '%s' requires output '%s' from input derivation '%s', which is supposedly realized already, yet we still don't know what path corresponds to that output",
worker.store.printStorePath(drvPath), j, worker.store.printStorePath(depDrvPath));
worker.store.computeFSClosure(*optRealizedInput, inputPaths);
} else
throw Error(
"derivation '%s' requires non-existent output '%s' from input derivation '%s'",
worker.store.printStorePath(drvPath), j, worker.store.printStorePath(depDrvPath));
}
}
}
@@ -548,7 +515,7 @@ void DerivationGoal::inputsRealised()
/* Don't repeat fixed-output derivations since they're already
verified by their output hash.*/
nrRounds = derivationType.isFixed() ? 1 : settings.buildRepeat + 1;
nrRounds = derivationIsFixed(derivationType) ? 1 : settings.buildRepeat + 1;
/* Okay, try to build. Note that here we don't wait for a build
slot to become available, since we don't need one if there is a
@@ -556,11 +523,10 @@ void DerivationGoal::inputsRealised()
state = &DerivationGoal::tryToBuild;
worker.wakeUp(shared_from_this());
buildResult = BuildResult { .path = buildResult.path };
result = BuildResult();
}
void DerivationGoal::started()
{
void DerivationGoal::started() {
auto msg = fmt(
buildMode == bmRepair ? "repairing outputs of '%s'" :
buildMode == bmCheck ? "checking outputs of '%s'" :
@@ -622,12 +588,19 @@ void DerivationGoal::tryToBuild()
omitted, but that would be less efficient.) Note that since we
now hold the locks on the output paths, no other process can
build this derivation, so no further checks are necessary. */
auto [allValid, validOutputs] = checkPathValidity();
checkPathValidity();
bool allValid = true;
for (auto & [_, status] : initialOutputs) {
if (!status.wanted) continue;
if (!status.known || !status.known->isValid()) {
allValid = false;
break;
}
}
if (buildMode != bmCheck && allValid) {
debug("skipping build of derivation '%s', someone beat us to it", worker.store.printStorePath(drvPath));
outputLocks.setDeletion(true);
done(BuildResult::AlreadyValid, std::move(validOutputs));
done(BuildResult::AlreadyValid);
return;
}
@@ -653,7 +626,7 @@ void DerivationGoal::tryToBuild()
/* Yes, it has started doing so. Wait until we get
EOF from the hook. */
actLock.reset();
buildResult.startTime = time(0); // inexact
result.startTime = time(0); // inexact
state = &DerivationGoal::buildDone;
started();
return;
@@ -857,8 +830,8 @@ void DerivationGoal::buildDone()
debug("builder process for '%s' finished", worker.store.printStorePath(drvPath));
buildResult.timesBuilt++;
buildResult.stopTime = time(0);
result.timesBuilt++;
result.stopTime = time(0);
/* So the child is gone now. */
worker.childTerminated(this);
@@ -903,11 +876,11 @@ void DerivationGoal::buildDone()
/* Compute the FS closure of the outputs and register them as
being valid. */
auto builtOutputs = registerOutputs();
registerOutputs();
StorePathSet outputPaths;
for (auto & [_, output] : buildResult.builtOutputs)
outputPaths.insert(output.outPath);
for (auto & [_, path] : finalOutputs)
outputPaths.insert(path);
runPostBuildHook(
worker.store,
*logger,
@@ -917,7 +890,7 @@ void DerivationGoal::buildDone()
if (buildMode == bmCheck) {
cleanupPostOutputsRegisteredModeCheck();
done(BuildResult::Built, std::move(builtOutputs));
done(BuildResult::Built);
return;
}
@@ -938,8 +911,6 @@ void DerivationGoal::buildDone()
outputLocks.setDeletion(true);
outputLocks.unlock();
done(BuildResult::Built, std::move(builtOutputs));
} catch (BuildError & e) {
outputLocks.unlock();
@@ -955,66 +926,71 @@ void DerivationGoal::buildDone()
st =
dynamic_cast<NotDeterministic*>(&e) ? BuildResult::NotDeterministic :
statusOk(status) ? BuildResult::OutputRejected :
!derivationType.isSandboxed() || diskFull ? BuildResult::TransientFailure :
derivationIsImpure(derivationType) || diskFull ? BuildResult::TransientFailure :
BuildResult::PermanentFailure;
}
done(st, {}, e);
done(st, e);
return;
}
done(BuildResult::Built);
}
void DerivationGoal::resolvedFinished()
{
trace("resolved derivation finished");
void DerivationGoal::resolvedFinished() {
assert(resolvedDrvGoal);
auto resolvedDrv = *resolvedDrvGoal->drv;
auto & resolvedResult = resolvedDrvGoal->buildResult;
DrvOutputs builtOutputs;
auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
if (resolvedResult.success()) {
auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
StorePathSet outputPaths;
StorePathSet outputPaths;
// `wantedOutputs` might be empty, which means “all the outputs”
auto realWantedOutputs = wantedOutputs;
if (realWantedOutputs.empty())
realWantedOutputs = resolvedDrv.outputNames();
// `wantedOutputs` might be empty, which means “all the outputs”
auto realWantedOutputs = wantedOutputs;
if (realWantedOutputs.empty())
realWantedOutputs = resolvedDrv.outputNames();
for (auto & wantedOutput : realWantedOutputs) {
assert(initialOutputs.count(wantedOutput) != 0);
assert(resolvedHashes.count(wantedOutput) != 0);
auto realisation = resolvedResult.builtOutputs.at(
DrvOutput { resolvedHashes.at(wantedOutput), wantedOutput });
if (drv->type().isPure()) {
auto newRealisation = realisation;
newRealisation.id = DrvOutput { initialOutputs.at(wantedOutput).outputHash, wantedOutput };
newRealisation.signatures.clear();
if (!drv->type().isFixed())
newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation.outPath);
signRealisation(newRealisation);
worker.store.registerDrvOutput(newRealisation);
}
outputPaths.insert(realisation.outPath);
builtOutputs.emplace(realisation.id, realisation);
}
runPostBuildHook(
worker.store,
*logger,
drvPath,
outputPaths
for (auto & wantedOutput : realWantedOutputs) {
assert(initialOutputs.count(wantedOutput) != 0);
assert(resolvedHashes.count(wantedOutput) != 0);
auto realisation = worker.store.queryRealisation(
DrvOutput{resolvedHashes.at(wantedOutput), wantedOutput}
);
// We've just built it, but maybe the build failed, in which case the
// realisation won't be there
if (realisation) {
auto newRealisation = *realisation;
newRealisation.id = DrvOutput{initialOutputs.at(wantedOutput).outputHash, wantedOutput};
newRealisation.signatures.clear();
newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation->outPath);
signRealisation(newRealisation);
worker.store.registerDrvOutput(newRealisation);
outputPaths.insert(realisation->outPath);
} else {
// If we don't have a realisation, then it must mean that something
// failed when building the resolved drv
assert(!result.success());
}
}
auto status = resolvedResult.status;
if (status == BuildResult::AlreadyValid)
status = BuildResult::ResolvesToAlreadyValid;
runPostBuildHook(
worker.store,
*logger,
drvPath,
outputPaths
);
done(status, std::move(builtOutputs));
auto status = [&]() {
auto resolvedResult = resolvedDrvGoal->getResult();
switch (resolvedResult.status) {
case BuildResult::AlreadyValid:
return BuildResult::ResolvesToAlreadyValid;
default:
return resolvedResult.status;
}
}();
done(status);
}
HookReply DerivationGoal::tryBuildHook()
@@ -1037,7 +1013,7 @@ HookReply DerivationGoal::tryBuildHook()
/* Read the first line of input, which should be a word indicating
whether the hook wishes to perform the build. */
std::string reply;
string reply;
while (true) {
auto s = [&]() {
try {
@@ -1049,8 +1025,8 @@ HookReply DerivationGoal::tryBuildHook()
}();
if (handleJSONLogMessage(s, worker.act, worker.hook->activities, true))
;
else if (s.substr(0, 2) == "# ") {
reply = s.substr(2);
else if (string(s, 0, 2) == "# ") {
reply = string(s, 2);
break;
}
else {
@@ -1124,7 +1100,7 @@ HookReply DerivationGoal::tryBuildHook()
}
DrvOutputs DerivationGoal::registerOutputs()
void DerivationGoal::registerOutputs()
{
/* When using a build hook, the build hook can register the output
as valid (by doing `nix-store --import'). If so we don't have
@@ -1133,7 +1109,21 @@ DrvOutputs DerivationGoal::registerOutputs()
We can only early return when the outputs are known a priori. For
floating content-addressed derivations this isn't the case.
*/
return assertPathValidity();
for (auto & [outputName, optOutputPath] : worker.store.queryPartialDerivationOutputMap(drvPath)) {
if (!wantOutput(outputName, wantedOutputs))
continue;
if (!optOutputPath)
throw BuildError(
"output '%s' from derivation '%s' does not have a known output path",
outputName, worker.store.printStorePath(drvPath));
auto & outputPath = *optOutputPath;
if (!worker.store.isValidPath(outputPath))
throw BuildError(
"output '%s' from derivation '%s' is supposed to be at '%s' but that path is not valid",
outputName, worker.store.printStorePath(drvPath), worker.store.printStorePath(outputPath));
finalOutputs.insert_or_assign(outputName, outputPath);
}
}
Path DerivationGoal::openLogFile()
@@ -1150,10 +1140,10 @@ Path DerivationGoal::openLogFile()
logDir = localStore->logDir;
else
logDir = settings.nixLogDir;
Path dir = fmt("%s/%s/%s/", logDir, LocalFSStore::drvsLogDir, baseName.substr(0, 2));
Path dir = fmt("%s/%s/%s/", logDir, LocalFSStore::drvsLogDir, string(baseName, 0, 2));
createDirs(dir);
Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2),
Path logFileName = fmt("%s/%s%s", dir, string(baseName, 2),
settings.compressLog ? ".bz2" : "");
fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666);
@@ -1185,17 +1175,16 @@ bool DerivationGoal::isReadDesc(int fd)
return fd == hook->builderOut.readSide.get();
}
void DerivationGoal::handleChildOutput(int fd, std::string_view data)
void DerivationGoal::handleChildOutput(int fd, const string & data)
{
// local & `ssh://`-builds are dealt with here.
auto isWrittenToLog = isReadDesc(fd);
if (isWrittenToLog)
if (isReadDesc(fd))
{
logSize += data.size();
if (settings.maxLogSize && logSize > settings.maxLogSize) {
killChild();
done(
BuildResult::LogLimitExceeded, {},
BuildResult::LogLimitExceeded,
Error("%s killed after writing more than %d bytes of log output",
getName(), settings.maxLogSize));
return;
@@ -1218,16 +1207,7 @@ void DerivationGoal::handleChildOutput(int fd, std::string_view data)
if (hook && fd == hook->fromHook.readSide.get()) {
for (auto c : data)
if (c == '\n') {
auto json = parseJSONMessage(currentHookLine);
if (json) {
auto s = handleJSONLogMessage(*json, worker.act, hook->activities, true);
// ensure that logs from a builder using `ssh-ng://` as protocol
// are also available to `nix log`.
if (s && !isWrittenToLog && logSink && (*json)["type"] == resBuildLogLine) {
auto f = (*json)["fields"];
(*logSink)((f.size() > 0 ? f.at(0).get<std::string>() : "") + "\n");
}
}
handleJSONLogMessage(currentHookLine, worker.act, hook->activities, true);
currentHookLine.clear();
} else
currentHookLine += c;
@@ -1261,8 +1241,7 @@ void DerivationGoal::flushLine()
std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap()
{
assert(drv->type().isPure());
if (!useDerivation || drv->type().hasKnownOutputPaths()) {
if (!useDerivation || drv->type() != DerivationType::CAFloating) {
std::map<std::string, std::optional<StorePath>> res;
for (auto & [name, output] : drv->outputs)
res.insert_or_assign(name, output.path(worker.store, drv->name, name));
@@ -1274,8 +1253,7 @@ std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDeri
OutputPathMap DerivationGoal::queryDerivationOutputMap()
{
assert(drv->type().isPure());
if (!useDerivation || drv->type().hasKnownOutputPaths()) {
if (!useDerivation || drv->type() != DerivationType::CAFloating) {
OutputPathMap res;
for (auto & [name, output] : drv->outputsAndOptPaths(worker.store))
res.insert_or_assign(name, *output.second);
@@ -1286,14 +1264,10 @@ OutputPathMap DerivationGoal::queryDerivationOutputMap()
}
std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
void DerivationGoal::checkPathValidity()
{
if (!drv->type().isPure()) return { false, {} };
bool checkHash = buildMode == bmRepair;
auto wantedOutputsLeft = wantedOutputs;
DrvOutputs validOutputs;
for (auto & i : queryPartialDerivationOutputMap()) {
InitialOutput & info = initialOutputs.at(i.first);
info.wanted = wantOutput(i.first, wantedOutputs);
@@ -1310,30 +1284,27 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
: PathStatus::Corrupt,
};
}
auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first};
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first};
if (auto real = worker.store.queryRealisation(drvOutput)) {
info.known = {
.path = real->outPath,
.status = PathStatus::Valid,
};
} else if (info.known && info.known->isValid()) {
// We know the output because it's a static output of the
} else if (info.known && info.known->status == PathStatus::Valid) {
// We know the output because it' a static output of the
// derivation, and the output path is valid, but we don't have
// its realisation stored (probably because it has been built
// without the `ca-derivations` experimental flag).
// without the `ca-derivations` experimental flag)
worker.store.registerDrvOutput(
Realisation {
Realisation{
drvOutput,
info.known->path,
}
);
}
}
if (info.wanted && info.known && info.known->isValid())
validOutputs.emplace(drvOutput, Realisation { drvOutput, info.known->path });
}
// If we requested all the outputs via the empty set, we are always fine.
// If we requested specific elements, the loop above removes all the valid
// ones, so any that are left must be invalid.
@@ -1341,48 +1312,24 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
throw Error("derivation '%s' does not have wanted outputs %s",
worker.store.printStorePath(drvPath),
concatStringsSep(", ", quoteStrings(wantedOutputsLeft)));
bool allValid = true;
for (auto & [_, status] : initialOutputs) {
if (!status.wanted) continue;
if (!status.known || !status.known->isValid()) {
allValid = false;
break;
}
}
return { allValid, validOutputs };
}
DrvOutputs DerivationGoal::assertPathValidity()
void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
{
auto [allValid, validOutputs] = checkPathValidity();
if (!allValid)
throw Error("some outputs are unexpectedly invalid");
return validOutputs;
}
void DerivationGoal::done(
BuildResult::Status status,
DrvOutputs builtOutputs,
std::optional<Error> ex)
{
buildResult.status = status;
result.status = status;
if (ex)
buildResult.errorMsg = fmt("%s", normaltxt(ex->info().msg));
if (buildResult.status == BuildResult::TimedOut)
result.errorMsg = ex->what();
amDone(result.success() ? ecSuccess : ecFailed, ex);
if (result.status == BuildResult::TimedOut)
worker.timedOut = true;
if (buildResult.status == BuildResult::PermanentFailure)
if (result.status == BuildResult::PermanentFailure)
worker.permanentFailure = true;
mcExpectedBuilds.reset();
mcRunningBuilds.reset();
if (buildResult.success()) {
assert(!builtOutputs.empty());
buildResult.builtOutputs = std::move(builtOutputs);
if (result.success()) {
if (status == BuildResult::Built)
worker.doneBuilds++;
} else {
@@ -1396,23 +1343,9 @@ void DerivationGoal::done(
if (traceBuiltOutputsFile != "") {
std::fstream fs;
fs.open(traceBuiltOutputsFile, std::fstream::out);
fs << worker.store.printStorePath(drvPath) << "\t" << buildResult.toString() << std::endl;
fs << worker.store.printStorePath(drvPath) << "\t" << result.toString() << std::endl;
}
amDone(buildResult.success() ? ecSuccess : ecFailed, ex);
}
void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result)
{
Goal::waiteeDone(waitee, result);
if (waitee->buildResult.success())
if (auto bfd = std::get_if<DerivedPath::Built>(&waitee->buildResult.path))
for (auto & [output, realisation] : waitee->buildResult.builtOutputs)
inputDrvOutputs.insert_or_assign(
{ bfd->drvPath, output.outputName },
realisation.outPath);
}
}
+21 -28
View File
@@ -57,21 +57,12 @@ struct DerivationGoal : public Goal
them. */
StringSet wantedOutputs;
/* Mapping from input derivations + output names to actual store
paths. This is filled in by waiteeDone() as each dependency
finishes, before inputsRealised() is reached, */
std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
/* Whether additional wanted outputs have been added. */
bool needRestart = false;
/* Whether to retry substituting the outputs after building the
inputs. This is done in case of an incomplete closure. */
bool retrySubstitution = false;
/* Whether we've retried substitution, in which case we won't try
again. */
bool retriedSubstitution = false;
inputs. */
bool retrySubstitution;
/* The derivation stored at drvPath. */
std::unique_ptr<Derivation> drv;
@@ -113,8 +104,20 @@ struct DerivationGoal : public Goal
typedef void (DerivationGoal::*GoalState)();
GoalState state;
/* The final output paths of the build.
- For input-addressed derivations, always the precomputed paths
- For content-addressed derivations, calcuated from whatever the hash
ends up being. (Note that fixed outputs derivations that produce the
"wrong" output still install that data under its true content-address.)
*/
OutputPathMap finalOutputs;
BuildMode buildMode;
BuildResult result;
/* The current round, if we're building multiple times. */
size_t curRound = 1;
@@ -142,13 +145,15 @@ struct DerivationGoal : public Goal
void timedOut(Error && ex) override;
std::string key() override;
string key() override;
void work() override;
/* Add wanted outputs to an already existing derivation goal. */
void addWantedOutputs(const StringSet & outputs);
BuildResult getResult() { return result; }
/* The states. */
void getDerivation();
void loadDerivation();
@@ -170,7 +175,7 @@ struct DerivationGoal : public Goal
/* Check that the derivation outputs all exist and register them
as valid. */
virtual DrvOutputs registerOutputs();
virtual void registerOutputs();
/* Open a log file and a pipe to it. */
Path openLogFile();
@@ -195,7 +200,7 @@ struct DerivationGoal : public Goal
virtual bool isReadDesc(int fd);
/* Callback used by the worker to write to the log. */
void handleChildOutput(int fd, std::string_view data) override;
void handleChildOutput(int fd, const string & data) override;
void handleEOF(int fd) override;
void flushLine();
@@ -205,17 +210,8 @@ struct DerivationGoal : public Goal
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
OutputPathMap queryDerivationOutputMap();
/* Update 'initialOutputs' to determine the current status of the
outputs of the derivation. Also returns a Boolean denoting
whether all outputs are valid and non-corrupt, and a
'DrvOutputs' structure containing the valid and wanted
outputs. */
std::pair<bool, DrvOutputs> checkPathValidity();
/* Aborts if any output is not valid or corrupt, and otherwise
returns a 'DrvOutputs' structure containing the wanted
outputs. */
DrvOutputs assertPathValidity();
/* Return the set of (in)valid paths. */
void checkPathValidity();
/* Forcibly kill the child process, if any. */
virtual void killChild();
@@ -226,11 +222,8 @@ struct DerivationGoal : public Goal
void done(
BuildResult::Status status,
DrvOutputs builtOutputs = {},
std::optional<Error> ex = {});
void waiteeDone(GoalPtr waitee, ExitCode result) override;
StorePathSet exportReferences(const StorePathSet & storePaths);
};
@@ -6,12 +6,8 @@
namespace nix {
DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
const DrvOutput & id,
Worker & worker,
RepairFlag repair,
std::optional<ContentAddress> ca)
: Goal(worker, DerivedPath::Opaque { StorePath::dummy })
DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
: Goal(worker)
, id(id)
{
state = &DrvOutputSubstitutionGoal::init;
@@ -36,12 +32,12 @@ void DrvOutputSubstitutionGoal::init()
void DrvOutputSubstitutionGoal::tryNext()
{
trace("trying next substituter");
trace("Trying next substituter");
if (subs.size() == 0) {
/* None left. Terminate this goal and let someone else deal
with it. */
debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string());
debug("drv output '%s' is required, but there is no substituter that can provide it", id.to_string());
/* Hack: don't indicate failure if there were no substituters.
In that case the calling derivation should just do a
@@ -123,7 +119,7 @@ void DrvOutputSubstitutionGoal::realisationFetched()
void DrvOutputSubstitutionGoal::outPathValid()
{
assert(outputInfo);
trace("output path substituted");
trace("Output path substituted");
if (nrFailed > 0) {
debug("The output path of the derivation output '%s' could not be substituted", id.to_string());
@@ -141,7 +137,7 @@ void DrvOutputSubstitutionGoal::finished()
amDone(ecSuccess);
}
std::string DrvOutputSubstitutionGoal::key()
string DrvOutputSubstitutionGoal::key()
{
/* "a$" ensures substitution goals happen before derivation
goals. */
@@ -51,7 +51,7 @@ public:
void timedOut(Error && ex) override { abort(); };
std::string key() override;
string key() override;
void work() override;
void handleEOF(int fd) override;
+28 -36
View File
@@ -47,51 +47,43 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
}
}
std::vector<BuildResult> Store::buildPathsWithResults(
const std::vector<DerivedPath> & reqs,
BuildMode buildMode,
std::shared_ptr<Store> evalStore)
{
Worker worker(*this, evalStore ? *evalStore : *this);
Goals goals;
for (const auto & br : reqs) {
std::visit(overloaded {
[&](const DerivedPath::Built & bfd) {
goals.insert(worker.makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode));
},
[&](const DerivedPath::Opaque & bo) {
goals.insert(worker.makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair));
},
}, br.raw());
}
worker.run(goals);
std::vector<BuildResult> results;
for (auto & i : goals)
results.push_back(i->buildResult);
return results;
}
BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
BuildMode buildMode)
{
Worker worker(*this, *this);
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode);
BuildResult result;
try {
worker.run(Goals{goal});
return goal->buildResult;
result = goal->getResult();
} catch (Error & e) {
return BuildResult {
.status = BuildResult::MiscFailure,
.errorMsg = e.msg(),
.path = DerivedPath::Built { .drvPath = drvPath },
};
};
result.status = BuildResult::MiscFailure;
result.errorMsg = e.msg();
}
// XXX: Should use `goal->queryPartialDerivationOutputMap()` once it's
// extended to return the full realisation for each output
auto staticDrvOutputs = drv.outputsAndOptPaths(*this);
auto outputHashes = staticOutputHashes(*this, drv);
for (auto & [outputName, staticOutput] : staticDrvOutputs) {
auto outputId = DrvOutput{outputHashes.at(outputName), outputName};
if (staticOutput.second)
result.builtOutputs.insert_or_assign(
outputId,
Realisation{ outputId, *staticOutput.second}
);
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) {
auto realisation = this->queryRealisation(outputId);
if (realisation)
result.builtOutputs.insert_or_assign(
outputId,
*realisation
);
}
}
return result;
}
+3 -3
View File
@@ -5,8 +5,8 @@ namespace nix {
bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) const {
std::string s1 = a->key();
std::string s2 = b->key();
string s1 = a->key();
string s2 = b->key();
return s1 < s2;
}
@@ -28,7 +28,7 @@ void Goal::addWaitee(GoalPtr waitee)
void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
{
assert(waitees.count(waitee));
assert(waitees.find(waitee) != waitees.end());
waitees.erase(waitee);
trace(fmt("waitee '%s' done; %d left", waitee->name, waitees.size()));
+13 -16
View File
@@ -2,7 +2,6 @@
#include "types.hh"
#include "store-api.hh"
#include "build-result.hh"
namespace nix {
@@ -40,32 +39,30 @@ struct Goal : public std::enable_shared_from_this<Goal>
WeakGoals waiters;
/* Number of goals we are/were waiting for that have failed. */
size_t nrFailed = 0;
unsigned int nrFailed;
/* Number of substitution goals we are/were waiting for that
failed because there are no substituters. */
size_t nrNoSubstituters = 0;
unsigned int nrNoSubstituters;
/* Number of substitution goals we are/were waiting for that
failed because they had unsubstitutable references. */
size_t nrIncompleteClosure = 0;
unsigned int nrIncompleteClosure;
/* Name of this goal for debugging purposes. */
std::string name;
string name;
/* Whether the goal is finished. */
ExitCode exitCode = ecBusy;
/* Build result. */
BuildResult buildResult;
ExitCode exitCode;
/* Exception containing an error message, if any. */
std::optional<Error> ex;
Goal(Worker & worker, DerivedPath path)
: worker(worker)
, buildResult { .path = std::move(path) }
{ }
Goal(Worker & worker) : worker(worker)
{
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
exitCode = ecBusy;
}
virtual ~Goal()
{
@@ -78,7 +75,7 @@ struct Goal : public std::enable_shared_from_this<Goal>
virtual void waiteeDone(GoalPtr waitee, ExitCode result);
virtual void handleChildOutput(int fd, std::string_view data)
virtual void handleChildOutput(int fd, const string & data)
{
abort();
}
@@ -90,7 +87,7 @@ struct Goal : public std::enable_shared_from_this<Goal>
void trace(const FormatOrString & fs);
std::string getName()
string getName()
{
return name;
}
@@ -100,7 +97,7 @@ struct Goal : public std::enable_shared_from_this<Goal>
by the worker (important!), etc. */
virtual void timedOut(Error && ex) = 0;
virtual std::string key() = 0;
virtual string key() = 0;
void amDone(ExitCode result, std::optional<Error> ex = {});
+87 -127
View File
@@ -1,5 +1,4 @@
#include "local-derivation-goal.hh"
#include "gc-store.hh"
#include "hook-instance.hh"
#include "worker.hh"
#include "builtins.hh"
@@ -194,7 +193,7 @@ void LocalDerivationGoal::tryLocalBuild() {
outputLocks.unlock();
buildUser.reset();
worker.permanentFailure = true;
done(BuildResult::InputRejected, {}, e);
done(BuildResult::InputRejected, e);
return;
}
@@ -395,7 +394,7 @@ void LocalDerivationGoal::startBuilder()
else if (settings.sandboxMode == smDisabled)
useChroot = false;
else if (settings.sandboxMode == smRelaxed)
useChroot = derivationType.isSandboxed() && !noChroot;
useChroot = !(derivationIsImpure(derivationType)) && !noChroot;
}
auto & localStore = getLocalStore();
@@ -482,12 +481,12 @@ void LocalDerivationGoal::startBuilder()
temporary build directory. The text files have the format used
by `nix-store --register-validity'. However, the deriver
fields are left empty. */
auto s = get(drv->env, "exportReferencesGraph").value_or("");
string s = get(drv->env, "exportReferencesGraph").value_or("");
Strings ss = tokenizeString<Strings>(s);
if (ss.size() % 2 != 0)
throw BuildError("odd number of tokens in 'exportReferencesGraph': '%1%'", s);
for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
auto fileName = *i++;
string fileName = *i++;
static std::regex regex("[A-Za-z_][A-Za-z0-9_.-]*");
if (!std::regex_match(fileName, regex))
throw Error("invalid file name '%s' in 'exportReferencesGraph'", fileName);
@@ -518,10 +517,10 @@ void LocalDerivationGoal::startBuilder()
i.pop_back();
}
size_t p = i.find('=');
if (p == std::string::npos)
if (p == string::npos)
dirsInChroot[i] = {i, optional};
else
dirsInChroot[i.substr(0, p)] = {i.substr(p + 1), optional};
dirsInChroot[string(i, 0, p)] = {string(i, p + 1), optional};
}
dirsInChroot[tmpDirInSandbox] = tmpDir;
@@ -608,7 +607,7 @@ void LocalDerivationGoal::startBuilder()
"nogroup:x:65534:\n", sandboxGid()));
/* Create /etc/hosts with localhost entry. */
if (derivationType.isSandboxed())
if (!(derivationIsImpure(derivationType)))
writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n::1 localhost\n");
/* Make the closure of the inputs available in the chroot,
@@ -672,10 +671,9 @@ void LocalDerivationGoal::startBuilder()
auto state = stBegin;
auto lines = runProgram(settings.preBuildHook, false, args);
auto lastPos = std::string::size_type{0};
for (auto nlPos = lines.find('\n'); nlPos != std::string::npos;
nlPos = lines.find('\n', lastPos))
{
auto line = lines.substr(lastPos, nlPos - lastPos);
for (auto nlPos = lines.find('\n'); nlPos != string::npos;
nlPos = lines.find('\n', lastPos)) {
auto line = std::string{lines, lastPos, nlPos - lastPos};
lastPos = nlPos + 1;
if (state == stBegin) {
if (line == "extra-sandbox-paths" || line == "extra-chroot-dirs") {
@@ -688,10 +686,10 @@ void LocalDerivationGoal::startBuilder()
state = stBegin;
} else {
auto p = line.find('=');
if (p == std::string::npos)
if (p == string::npos)
dirsInChroot[line] = line;
else
dirsInChroot[line.substr(0, p)] = line.substr(p + 1);
dirsInChroot[string(line, 0, p)] = string(line, p + 1);
}
}
}
@@ -704,9 +702,6 @@ void LocalDerivationGoal::startBuilder()
/* Run the builder. */
printMsg(lvlChatty, "executing builder '%1%'", drv->builder);
printMsg(lvlChatty, "using builder args '%1%'", concatStringsSep(" ", drv->args));
for (auto & i : drv->env)
printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second);
/* Create the log file. */
Path logFile = openLogFile();
@@ -759,7 +754,7 @@ void LocalDerivationGoal::startBuilder()
if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term))
throw SysError("putting pseudoterminal into raw mode");
buildResult.startTime = time(0);
result.startTime = time(0);
/* Fork a child to build the package. */
@@ -799,7 +794,7 @@ void LocalDerivationGoal::startBuilder()
us.
*/
if (derivationType.isSandboxed())
if (!(derivationIsImpure(derivationType)))
privateNetwork = true;
userNamespaceSync.create();
@@ -946,7 +941,7 @@ void LocalDerivationGoal::startBuilder()
/* Check if setting up the build environment failed. */
std::vector<std::string> msgs;
while (true) {
std::string msg = [&]() {
string msg = [&]() {
try {
return readLine(builderOut.readSide.get());
} catch (Error & e) {
@@ -958,8 +953,8 @@ void LocalDerivationGoal::startBuilder()
throw;
}
}();
if (msg.substr(0, 1) == "\2") break;
if (msg.substr(0, 1) == "\1") {
if (string(msg, 0, 1) == "\2") break;
if (string(msg, 0, 1) == "\1") {
FdSource source(builderOut.readSide.get());
auto ex = readError(source);
ex.addTrace({}, "while setting up the build environment");
@@ -995,7 +990,7 @@ void LocalDerivationGoal::initTmpDir() {
env[i.first] = i.second;
} else {
auto hash = hashString(htSHA256, i.first);
std::string fn = ".attr-" + hash.to_string(Base32, false);
string fn = ".attr-" + hash.to_string(Base32, false);
Path p = tmpDir + "/" + fn;
writeFile(p, rewriteStrings(i.second, inputRewrites));
chownToBuilder(p);
@@ -1052,7 +1047,7 @@ void LocalDerivationGoal::initEnv()
derivation, tell the builder, so that for instance `fetchurl'
can skip checking the output. On older Nixes, this environment
variable won't be set, so `fetchurl' will do the check. */
if (derivationType.isFixed()) env["NIX_OUTPUT_CHECKED"] = "1";
if (derivationIsFixed(derivationType)) env["NIX_OUTPUT_CHECKED"] = "1";
/* *Only* if this is a fixed-output derivation, propagate the
values of the environment variables specified in the
@@ -1063,7 +1058,7 @@ void LocalDerivationGoal::initEnv()
to the builder is generally impure, but the output of
fixed-output derivations is by definition pure (since we
already know the cryptographic hash of the output). */
if (!derivationType.isSandboxed()) {
if (derivationIsImpure(derivationType)) {
for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings()))
env[i] = getEnv(i).value_or("");
}
@@ -1086,7 +1081,7 @@ void LocalDerivationGoal::writeStructuredAttrs()
for (auto & [i, v] : json["outputs"].get<nlohmann::json::object_t>()) {
/* The placeholder must have a rewrite, so we use it to cover both the
cases where we know or don't know the output path ahead of time. */
rewritten[i] = rewriteStrings((std::string) v, inputRewrites);
rewritten[i] = rewriteStrings(v, inputRewrites);
}
json["outputs"] = rewritten;
@@ -1131,7 +1126,7 @@ struct RestrictedStoreConfig : virtual LocalFSStoreConfig
/* A wrapper around LocalStore that only allows building/querying of
paths that are in the input closures of the build or were added via
recursive Nix calls. */
struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual LocalFSStore, public virtual GcStore
struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual LocalFSStore
{
ref<LocalStore> next;
@@ -1192,14 +1187,10 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
{ throw Error("queryPathFromHashPart"); }
StorePath addToStore(
std::string_view name,
const Path & srcPath,
FileIngestionMethod method,
HashType hashAlgo,
PathFilter & filter,
RepairFlag repair,
const StorePathSet & references) override
StorePath addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair,
const StorePathSet & references = StorePathSet()) override
{ throw Error("addToStore"); }
void addToStore(const ValidPathInfo & info, Source & narSource,
@@ -1209,24 +1200,17 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
goal.addDependency(info.path);
}
StorePath addTextToStore(
std::string_view name,
std::string_view s,
const StorePathSet & references,
RepairFlag repair = NoRepair) override
StorePath addTextToStore(const string & name, const string & s,
const StorePathSet & references, RepairFlag repair = NoRepair) override
{
auto path = next->addTextToStore(name, s, references, repair);
goal.addDependency(path);
return path;
}
StorePath addToStoreFromDump(
Source & dump,
std::string_view name,
FileIngestionMethod method,
HashType hashAlgo,
RepairFlag repair,
const StorePathSet & references) override
StorePath addToStoreFromDump(Source & dump, std::string_view name,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
const StorePathSet & references = StorePathSet()) override
{
auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, repair, references);
goal.addDependency(path);
@@ -1263,16 +1247,6 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
}
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
{
for (auto & result : buildPathsWithResults(paths, buildMode, evalStore))
if (!result.success())
result.rethrow();
}
std::vector<BuildResult> buildPathsWithResults(
const std::vector<DerivedPath> & paths,
BuildMode buildMode = bmNormal,
std::shared_ptr<Store> evalStore = nullptr) override
{
assert(!evalStore);
@@ -1286,13 +1260,26 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next));
}
auto results = next->buildPathsWithResults(paths, buildMode);
next->buildPaths(paths, buildMode);
for (auto & result : results) {
for (auto & [outputName, output] : result.builtOutputs) {
newPaths.insert(output.outPath);
newRealisations.insert(output);
}
for (auto & path : paths) {
auto p = std::get_if<DerivedPath::Built>(&path);
if (!p) continue;
auto & bfd = *p;
auto drv = readDerivation(bfd.drvPath);
auto drvHashes = staticOutputHashes(*this, drv);
auto outputs = next->queryDerivationOutputMap(bfd.drvPath);
for (auto & [outputName, outputPath] : outputs)
if (wantOutput(outputName, bfd.outputs)) {
newPaths.insert(outputPath);
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
auto thisRealisation = next->queryRealisation(
DrvOutput{drvHashes.at(outputName), outputName}
);
assert(thisRealisation);
newRealisations.insert(*thisRealisation);
}
}
}
StorePathSet closure;
@@ -1301,8 +1288,6 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
goal.addDependency(path);
for (auto & real : Realisation::closure(*next, newRealisations))
goal.addedDrvOutputs.insert(real.id);
return results;
}
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
@@ -1343,12 +1328,6 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
next->queryMissing(allowed, willBuild, willSubstitute,
unknown, downloadSize, narSize);
}
virtual std::optional<std::string> getBuildLog(const StorePath & path) override
{ return std::nullopt; }
virtual void addBuildLog(const StorePath & path, std::string_view log) override
{ unsupported("addBuildLog"); }
};
@@ -1677,7 +1656,7 @@ void LocalDerivationGoal::runChild()
/* Fixed-output derivations typically need to access the
network, so give them access to /etc/resolv.conf and so
on. */
if (!derivationType.isSandboxed()) {
if (derivationIsImpure(derivationType)) {
// Only use nss functions to resolve hosts and
// services. Don’t use it for anything else that may
// be configured for this system. This limits the
@@ -1921,7 +1900,7 @@ void LocalDerivationGoal::runChild()
sandboxProfile += "(import \"sandbox-defaults.sb\")\n";
if (!derivationType.isSandboxed())
if (derivationIsImpure(derivationType))
sandboxProfile += "(import \"sandbox-network.sb\")\n";
/* Add the output paths we'll use at build-time to the chroot */
@@ -1942,7 +1921,7 @@ void LocalDerivationGoal::runChild()
"can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
i.first, i.second.source);
std::string path = i.first;
string path = i.first;
struct stat st;
if (lstat(path.c_str(), &st)) {
if (i.second.optional && errno == ENOENT)
@@ -1994,7 +1973,7 @@ void LocalDerivationGoal::runChild()
args.push_back("IMPORT_DIR=" + settings.nixDataDir + "/nix/sandbox/");
if (allowLocalNetworking) {
args.push_back("-D");
args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
args.push_back(string("_ALLOW_LOCAL_NETWORKING=1"));
}
args.push_back(drv->builder);
} else {
@@ -2013,7 +1992,7 @@ void LocalDerivationGoal::runChild()
args.push_back(rewriteStrings(i, inputRewrites));
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
writeFull(STDERR_FILENO, string("\2\n"));
/* Execute the program. This should not return. */
if (drv->isBuiltin()) {
@@ -2031,7 +2010,7 @@ void LocalDerivationGoal::runChild()
else if (drv->builder == "builtin:unpack-channel")
builtinUnpackChannel(drv2);
else
throw Error("unsupported builtin builder '%1%'", drv->builder.substr(8));
throw Error("unsupported builtin builder '%1%'", string(drv->builder, 8));
_exit(0);
} catch (std::exception & e) {
writeFull(STDERR_FILENO, e.what() + std::string("\n"));
@@ -2077,7 +2056,7 @@ void LocalDerivationGoal::runChild()
}
DrvOutputs LocalDerivationGoal::registerOutputs()
void LocalDerivationGoal::registerOutputs()
{
/* When using a build hook, the build hook can register the output
as valid (by doing `nix-store --import'). If so we don't have
@@ -2086,8 +2065,10 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
We can only early return when the outputs are known a priori. For
floating content-addressed derivations this isn't the case.
*/
if (hook)
return DerivationGoal::registerOutputs();
if (hook) {
DerivationGoal::registerOutputs();
return;
}
std::map<std::string, ValidPathInfo> infos;
@@ -2210,8 +2191,6 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
std::reverse(sortedOutputNames.begin(), sortedOutputNames.end());
OutputPathMap finalOutputs;
for (auto & outputName : sortedOutputNames) {
auto output = drv->outputs.at(outputName);
auto & scratchPath = scratchOutputs.at(outputName);
@@ -2282,7 +2261,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
return res;
};
auto newInfoFromCA = [&](const DerivationOutput::CAFloating outputHash) -> ValidPathInfo {
auto newInfoFromCA = [&](const DerivationOutputCAFloating outputHash) -> ValidPathInfo {
auto & st = outputStats.at(outputName);
if (outputHash.method == FileIngestionMethod::Flat) {
/* The output path should be a regular file without execute permission. */
@@ -2348,8 +2327,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
};
ValidPathInfo newInfo = std::visit(overloaded {
[&](const DerivationOutput::InputAddressed & output) {
[&](const DerivationOutputInputAddressed & output) {
/* input-addressed case */
auto requiredFinalPath = output.path;
/* Preemptively add rewrite rule for final hash, as that is
@@ -2368,9 +2346,8 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
newInfo0.references.insert(newInfo0.path);
return newInfo0;
},
[&](const DerivationOutput::CAFixed & dof) {
auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating {
[&](const DerivationOutputCAFixed & dof) {
auto newInfo0 = newInfoFromCA(DerivationOutputCAFloating {
.method = dof.hash.method,
.hashType = dof.hash.hash.type,
});
@@ -2391,25 +2368,19 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
}
return newInfo0;
},
[&](const DerivationOutput::CAFloating & dof) {
[&](DerivationOutputCAFloating dof) {
return newInfoFromCA(dof);
},
[&](const DerivationOutput::Deferred &) -> ValidPathInfo {
[&](DerivationOutputDeferred) {
// No derivation should reach that point without having been
// rewritten first
assert(false);
// Ugly, but the compiler insists on having this return a value
// of type `ValidPathInfo` despite the `assert(false)`, so
// let's provide it
return *(ValidPathInfo*)0;
},
[&](const DerivationOutput::Impure & doi) {
return newInfoFromCA(DerivationOutput::CAFloating {
.method = doi.method,
.hashType = doi.hashType,
});
},
}, output.raw());
}, output.output);
/* FIXME: set proper permissions in restorePath() so
we don't have to do another traversal. */
@@ -2519,12 +2490,11 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
}
if (buildMode == bmCheck) {
/* In case of fixed-output derivations, if there are
mismatches on `--check` an error must be thrown as this is
also a source for non-determinism. */
// In case of FOD mismatches on `--check` an error must be thrown as this is also
// a source for non-determinism.
if (delayedException)
std::rethrow_exception(delayedException);
return assertPathValidity();
return;
}
/* Apply output checks. */
@@ -2536,7 +2506,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
assert(prevInfos.size() == infos.size());
for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
if (!(*i == *j)) {
buildResult.isNonDeterministic = true;
result.isNonDeterministic = true;
Path prev = worker.store.printStorePath(i->second.path) + checkSuffix;
bool prevExists = keepPreviousRound && pathExists(prev);
hintformat hint = prevExists
@@ -2574,7 +2544,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
if (curRound < nrRounds) {
prevInfos = std::move(infos);
return {};
return;
}
/* Remove the .check directories if we're done. FIXME: keep them
@@ -2609,27 +2579,17 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
means it's safe to link the derivation to the output hash. We must do
that for floating CA derivations, which otherwise couldn't be cached,
but it's fine to do in all cases. */
DrvOutputs builtOutputs;
for (auto & [outputName, newInfo] : infos) {
auto thisRealisation = Realisation {
.id = DrvOutput {
initialOutputs.at(outputName).outputHash,
outputName
},
.outPath = newInfo.path
};
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
&& drv->type().isPure())
{
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
for (auto& [outputName, newInfo] : infos) {
auto thisRealisation = Realisation{
.id = DrvOutput{initialOutputs.at(outputName).outputHash,
outputName},
.outPath = newInfo.path};
signRealisation(thisRealisation);
worker.store.registerDrvOutput(thisRealisation);
}
if (wantOutput(outputName, wantedOutputs))
builtOutputs.emplace(thisRealisation.id, thisRealisation);
}
return builtOutputs;
}
void LocalDerivationGoal::signRealisation(Realisation & realisation)
@@ -2638,7 +2598,7 @@ void LocalDerivationGoal::signRealisation(Realisation & realisation)
}
void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo> & outputs)
void LocalDerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs)
{
std::map<Path, const ValidPathInfo &> outputsByPath;
for (auto & output : outputs)
@@ -2710,8 +2670,8 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
for (auto & i : *value) {
if (worker.store.isStorePath(i))
spec.insert(worker.store.parseStorePath(i));
else if (outputs.count(i))
spec.insert(outputs.at(i).path);
else if (finalOutputs.count(i))
spec.insert(finalOutputs.at(i));
else throw BuildError("derivation contains an illegal reference specifier '%s'", i);
}
@@ -2734,7 +2694,7 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
}
if (!badPaths.empty()) {
std::string badPathsStr;
string badPathsStr;
for (auto & i : badPaths) {
badPathsStr += "\n ";
badPathsStr += worker.store.printStorePath(i);
+3 -3
View File
@@ -58,11 +58,11 @@ struct LocalDerivationGoal : public DerivationGoal
typedef map<Path, ChrootPath> DirsInChroot; // maps target path to source path
DirsInChroot dirsInChroot;
typedef map<std::string, std::string> Environment;
typedef map<string, string> Environment;
Environment env;
#if __APPLE__
typedef std::string SandboxProfile;
typedef string SandboxProfile;
SandboxProfile additionalSandboxProfile;
#endif
@@ -169,7 +169,7 @@ struct LocalDerivationGoal : public DerivationGoal
/* Check that the derivation outputs all exist and register them
as valid. */
DrvOutputs registerOutputs() override;
void registerOutputs() override;
void signRealisation(Realisation &) override;
+8 -26
View File
@@ -6,7 +6,7 @@
namespace nix {
PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
: Goal(worker, DerivedPath::Opaque { storePath })
: Goal(worker)
, storePath(storePath)
, repair(repair)
, ca(ca)
@@ -24,20 +24,6 @@ PathSubstitutionGoal::~PathSubstitutionGoal()
}
void PathSubstitutionGoal::done(
ExitCode result,
BuildResult::Status status,
std::optional<std::string> errorMsg)
{
buildResult.status = status;
if (errorMsg) {
debug(*errorMsg);
buildResult.errorMsg = *errorMsg;
}
amDone(result);
}
void PathSubstitutionGoal::work()
{
(this->*state)();
@@ -52,7 +38,7 @@ void PathSubstitutionGoal::init()
/* If the path already exists we're done. */
if (!repair && worker.store.isValidPath(storePath)) {
done(ecSuccess, BuildResult::AlreadyValid);
amDone(ecSuccess);
return;
}
@@ -74,14 +60,12 @@ void PathSubstitutionGoal::tryNext()
if (subs.size() == 0) {
/* None left. Terminate this goal and let someone else deal
with it. */
debug("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath));
/* Hack: don't indicate failure if there were no substituters.
In that case the calling derivation should just do a
build. */
done(
substituterFailed ? ecFailed : ecNoSubstituters,
BuildResult::NoSubstituters,
fmt("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath)));
amDone(substituterFailed ? ecFailed : ecNoSubstituters);
if (substituterFailed) {
worker.failedSubstitutions++;
@@ -178,10 +162,8 @@ void PathSubstitutionGoal::referencesValid()
trace("all references realised");
if (nrFailed > 0) {
done(
nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed,
BuildResult::DependencyFailed,
fmt("some references of path '%s' could not be realised", worker.store.printStorePath(storePath)));
debug("some references of path '%s' could not be realised", worker.store.printStorePath(storePath));
amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
return;
}
@@ -286,11 +268,11 @@ void PathSubstitutionGoal::finished()
worker.updateProgress();
done(ecSuccess, BuildResult::Substituted);
amDone(ecSuccess);
}
void PathSubstitutionGoal::handleChildOutput(int fd, std::string_view data)
void PathSubstitutionGoal::handleChildOutput(int fd, const string & data)
{
}
+2 -7
View File
@@ -53,18 +53,13 @@ struct PathSubstitutionGoal : public Goal
/* Content address for recomputing store path */
std::optional<ContentAddress> ca;
void done(
ExitCode result,
BuildResult::Status status,
std::optional<std::string> errorMsg = {});
public:
PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
~PathSubstitutionGoal();
void timedOut(Error && ex) override { abort(); };
std::string key() override
string key() override
{
/* "a$" ensures substitution goals happen before derivation
goals. */
@@ -82,7 +77,7 @@ public:
void finished();
/* Callback used by the worker to write to the log. */
void handleChildOutput(int fd, std::string_view data) override;
void handleChildOutput(int fd, const string & data) override;
void handleEOF(int fd) override;
void cleanup() override;
+1 -1
View File
@@ -394,7 +394,7 @@ void Worker::waitForInput()
} else {
printMsg(lvlVomit, "%1%: read %2% bytes",
goal->getName(), rd);
std::string data((char *) buffer.data(), rd);
string data((char *) buffer.data(), rd);
j->lastOutput = after;
goal->handleChildOutput(k, data);
}
+6 -8
View File
@@ -47,9 +47,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
throw;
}
/* The files below are special-cased to that they don't show
* up in user profiles, either because they are useless, or
* because they would cause pointless collisions (e.g., each
/* The files below are special-cased to that they don't show up
* in user profiles, either because they are useless, or
* because they would cauase pointless collisions (e.g., each
* Python package brings its own
* `$out/lib/pythonX.Y/site-packages/easy-install.pth'.)
*/
@@ -57,9 +57,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
hasSuffix(srcFile, "/nix-support") ||
hasSuffix(srcFile, "/perllocal.pod") ||
hasSuffix(srcFile, "/info/dir") ||
hasSuffix(srcFile, "/log") ||
hasSuffix(srcFile, "/manifest.nix") ||
hasSuffix(srcFile, "/manifest.json"))
hasSuffix(srcFile, "/log"))
continue;
else if (S_ISDIR(srcSt.st_mode)) {
@@ -125,7 +123,7 @@ void buildProfile(const Path & out, Packages && pkgs)
createLinks(state, pkgDir, out, priority);
try {
for (const auto & p : tokenizeString<std::vector<std::string>>(
for (const auto & p : tokenizeString<std::vector<string>>(
readFile(pkgDir + "/nix-support/propagated-user-env-packages"), " \n"))
if (!done.count(p))
postponed.insert(p);
@@ -163,7 +161,7 @@ void buildProfile(const Path & out, Packages && pkgs)
void builtinBuildenv(const BasicDerivation & drv)
{
auto getAttr = [&](const std::string & name) {
auto getAttr = [&](const string & name) {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
+1 -1
View File
@@ -16,7 +16,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
writeFile(settings.netrcFile, netrcData, 0600);
}
auto getAttr = [&](const std::string & name) {
auto getAttr = [&](const string & name) {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
+1 -1
View File
@@ -5,7 +5,7 @@ namespace nix {
void builtinUnpackChannel(const BasicDerivation & drv)
{
auto getAttr = [&](const std::string & name) {
auto getAttr = [&](const string & name) {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
+11 -42
View File
@@ -1,11 +1,7 @@
#include "daemon.hh"
#include "monitor-fd.hh"
#include "worker-protocol.hh"
#include "build-result.hh"
#include "store-api.hh"
#include "store-cast.hh"
#include "gc-store.hh"
#include "log-store.hh"
#include "path-with-outputs.hh"
#include "finally.hh"
#include "archive.hh"
@@ -483,8 +479,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
}
case wopAddTextToStore: {
std::string suffix = readString(from);
std::string s = readString(from);
string suffix = readString(from);
string s = readString(from);
auto refs = worker_proto::read(*store, from, Phantom<StorePathSet> {});
logger->startWork();
auto path = store->addTextToStore(suffix, s, refs, NoRepair);
@@ -534,25 +530,6 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
break;
}
case wopBuildPathsWithResults: {
auto drvs = readDerivedPaths(*store, clientVersion, from);
BuildMode mode = bmNormal;
mode = (BuildMode) readInt(from);
/* Repairing is not atomic, so disallowed for "untrusted"
clients. */
if (mode == bmRepair && !trusted)
throw Error("repairing is not allowed because you are not in 'trusted-users'");
logger->startWork();
auto results = store->buildPathsWithResults(drvs, mode);
logger->stopWork();
worker_proto::write(*store, to, results);
break;
}
case wopBuildDerivation: {
auto drvPath = store->parseStorePath(readString(from));
BasicDerivation drv;
@@ -560,8 +537,6 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
BuildMode buildMode = (BuildMode) readInt(from);
logger->startWork();
auto drvType = drv.type();
/* Content-addressed derivations are trustless because their output paths
are verified by their content alone, so any derivation is free to
try to produce such a path.
@@ -594,12 +569,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
derivations, we throw out the precomputed output paths and just
store the hashes, so there aren't two competing sources of truth an
attacker could exploit. */
if (!(drvType.isCA() || trusted))
if (drv.type() == DerivationType::InputAddressed && !trusted)
throw Error("you are not privileged to build input-addressed derivations");
/* Make sure that the non-input-addressed derivations that got this far
are in fact content-addressed if we don't trust them. */
assert(drvType.isCA() || trusted);
assert(derivationIsCA(drv.type()) || trusted);
/* Recompute the derivation path when we cannot trust the original. */
if (!trusted) {
@@ -608,7 +583,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
original not-necessarily-resolved derivation to verify the drv
derivation as adequate claim to the input-addressed output
paths. */
assert(drvType.isCA());
assert(derivationIsCA(drv.type()));
Derivation drv2;
static_cast<BasicDerivation &>(drv2) = drv;
@@ -647,12 +622,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopAddIndirectRoot: {
Path path = absPath(readString(from));
logger->startWork();
auto & gcStore = require<GcStore>(*store);
gcStore.addIndirectRoot(path);
store->addIndirectRoot(path);
logger->stopWork();
to << 1;
break;
}
@@ -667,8 +639,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopFindRoots: {
logger->startWork();
auto & gcStore = require<GcStore>(*store);
Roots roots = gcStore.findRoots(!trusted);
Roots roots = store->findRoots(!trusted);
logger->stopWork();
size_t size = 0;
@@ -699,8 +670,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
logger->startWork();
if (options.ignoreLiveness)
throw Error("you are not allowed to ignore liveness");
auto & gcStore = require<GcStore>(*store);
gcStore.collectGarbage(options, results);
store->collectGarbage(options, results);
logger->stopWork();
to << results.paths << results.bytesFreed << 0 /* obsolete */;
@@ -728,8 +698,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
unsigned int n = readInt(from);
for (unsigned int i = 0; i < n; i++) {
auto name = readString(from);
auto value = readString(from);
string name = readString(from);
string value = readString(from);
clientSettings.overrides.emplace(name, value);
}
}
@@ -957,12 +927,11 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
logger->startWork();
if (!trusted)
throw Error("you are not privileged to add logs");
auto & logStore = require<LogStore>(*store);
{
FramedSource source(from);
StringSink sink;
source.drainInto(sink);
logStore.addBuildLog(path, sink.s);
store->addBuildLog(path, sink.s);
}
logger->stopWork();
to << 1;
+199 -308
View File
@@ -11,120 +11,78 @@ namespace nix {
std::optional<StorePath> DerivationOutput::path(const Store & store, std::string_view drvName, std::string_view outputName) const
{
return std::visit(overloaded {
[](const DerivationOutput::InputAddressed & doi) -> std::optional<StorePath> {
[](const DerivationOutputInputAddressed & doi) -> std::optional<StorePath> {
return { doi.path };
},
[&](const DerivationOutput::CAFixed & dof) -> std::optional<StorePath> {
[&](const DerivationOutputCAFixed & dof) -> std::optional<StorePath> {
return {
dof.path(store, drvName, outputName)
};
},
[](const DerivationOutput::CAFloating & dof) -> std::optional<StorePath> {
[](const DerivationOutputCAFloating & dof) -> std::optional<StorePath> {
return std::nullopt;
},
[](const DerivationOutput::Deferred &) -> std::optional<StorePath> {
[](const DerivationOutputDeferred &) -> std::optional<StorePath> {
return std::nullopt;
},
[](const DerivationOutput::Impure &) -> std::optional<StorePath> {
return std::nullopt;
},
}, raw());
}, output);
}
StorePath DerivationOutput::CAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const
{
StorePath DerivationOutputCAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const {
return store.makeFixedOutputPath(
hash.method, hash.hash,
outputPathName(drvName, outputName));
}
bool DerivationType::isCA() const
{
/* Normally we do the full `std::visit` to make sure we have
exhaustively handled all variants, but so long as there is a
variant called `ContentAddressed`, it must be the only one for
which `isCA` is true for this to make sense!. */
return std::visit(overloaded {
[](const InputAddressed & ia) {
return false;
},
[](const ContentAddressed & ca) {
return true;
},
[](const Impure &) {
return true;
},
}, raw());
bool derivationIsCA(DerivationType dt) {
switch (dt) {
case DerivationType::InputAddressed: return false;
case DerivationType::CAFixed: return true;
case DerivationType::CAFloating: return true;
case DerivationType::DeferredInputAddressed: return false;
};
// Since enums can have non-variant values, but making a `default:` would
// disable exhaustiveness warnings.
assert(false);
}
bool DerivationType::isFixed() const
{
return std::visit(overloaded {
[](const InputAddressed & ia) {
return false;
},
[](const ContentAddressed & ca) {
return ca.fixed;
},
[](const Impure &) {
return false;
},
}, raw());
bool derivationIsFixed(DerivationType dt) {
switch (dt) {
case DerivationType::InputAddressed: return false;
case DerivationType::CAFixed: return true;
case DerivationType::CAFloating: return false;
case DerivationType::DeferredInputAddressed: return false;
};
assert(false);
}
bool DerivationType::hasKnownOutputPaths() const
{
return std::visit(overloaded {
[](const InputAddressed & ia) {
return !ia.deferred;
},
[](const ContentAddressed & ca) {
return ca.fixed;
},
[](const Impure &) {
return false;
},
}, raw());
bool derivationHasKnownOutputPaths(DerivationType dt) {
switch (dt) {
case DerivationType::InputAddressed: return true;
case DerivationType::CAFixed: return true;
case DerivationType::CAFloating: return false;
case DerivationType::DeferredInputAddressed: return false;
};
assert(false);
}
bool DerivationType::isSandboxed() const
{
return std::visit(overloaded {
[](const InputAddressed & ia) {
return true;
},
[](const ContentAddressed & ca) {
return ca.sandboxed;
},
[](const Impure &) {
return false;
},
}, raw());
}
bool DerivationType::isPure() const
{
return std::visit(overloaded {
[](const InputAddressed & ia) {
return true;
},
[](const ContentAddressed & ca) {
return true;
},
[](const Impure &) {
return false;
},
}, raw());
bool derivationIsImpure(DerivationType dt) {
switch (dt) {
case DerivationType::InputAddressed: return false;
case DerivationType::CAFixed: return true;
case DerivationType::CAFloating: return false;
case DerivationType::DeferredInputAddressed: return false;
};
assert(false);
}
bool BasicDerivation::isBuiltin() const
{
return builder.substr(0, 8) == "builtin:";
return string(builder, 0, 8) == "builtin:";
}
@@ -146,19 +104,19 @@ StorePath writeDerivation(Store & store,
/* Read string `s' from stream `str'. */
static void expect(std::istream & str, std::string_view s)
static void expect(std::istream & str, const string & s)
{
char s2[s.size()];
str.read(s2, s.size());
if (std::string(s2, s.size()) != s)
if (string(s2, s.size()) != s)
throw FormatError("expected string '%1%'", s);
}
/* Read a C-style string from stream `str'. */
static std::string parseString(std::istream & str)
static string parseString(std::istream & str)
{
std::string res;
string res;
expect(str, "\"");
int c;
while ((c = str.get()) != '"')
@@ -214,41 +172,42 @@ static DerivationOutput parseDerivationOutput(const Store & store,
{
if (hashAlgo != "") {
auto method = FileIngestionMethod::Flat;
if (hashAlgo.substr(0, 2) == "r:") {
if (string(hashAlgo, 0, 2) == "r:") {
method = FileIngestionMethod::Recursive;
hashAlgo = hashAlgo.substr(2);
}
const auto hashType = parseHashType(hashAlgo);
if (hash == "impure") {
settings.requireExperimentalFeature(Xp::ImpureDerivations);
assert(pathS == "");
return DerivationOutput::Impure {
.method = std::move(method),
.hashType = std::move(hashType),
};
} else if (hash != "") {
if (hash != "") {
validatePath(pathS);
return DerivationOutput::CAFixed {
.hash = FixedOutputHash {
.method = std::move(method),
.hash = Hash::parseNonSRIUnprefixed(hash, hashType),
return DerivationOutput {
.output = DerivationOutputCAFixed {
.hash = FixedOutputHash {
.method = std::move(method),
.hash = Hash::parseNonSRIUnprefixed(hash, hashType),
},
},
};
} else {
settings.requireExperimentalFeature(Xp::CaDerivations);
assert(pathS == "");
return DerivationOutput::CAFloating {
.method = std::move(method),
.hashType = std::move(hashType),
return DerivationOutput {
.output = DerivationOutputCAFloating {
.method = std::move(method),
.hashType = std::move(hashType),
},
};
}
} else {
if (pathS == "") {
return DerivationOutput::Deferred { };
return DerivationOutput {
.output = DerivationOutputDeferred { }
};
}
validatePath(pathS);
return DerivationOutput::InputAddressed {
.path = store.parseStorePath(pathS),
return DerivationOutput {
.output = DerivationOutputInputAddressed {
.path = store.parseStorePath(pathS),
}
};
}
}
@@ -301,8 +260,8 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
/* Parse the environment variables. */
expect(str, ",[");
while (!endOfList(str)) {
expect(str, "("); auto name = parseString(str);
expect(str, ","); auto value = parseString(str);
expect(str, "("); string name = parseString(str);
expect(str, ","); string value = parseString(str);
expect(str, ")");
drv.env[name] = value;
}
@@ -312,7 +271,7 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
}
static void printString(std::string & res, std::string_view s)
static void printString(string & res, std::string_view s)
{
boost::container::small_vector<char, 64 * 1024> buffer;
buffer.reserve(s.size() * 2 + 2);
@@ -330,7 +289,7 @@ static void printString(std::string & res, std::string_view s)
}
static void printUnquotedString(std::string & res, std::string_view s)
static void printUnquotedString(string & res, std::string_view s)
{
res += '"';
res.append(s);
@@ -339,7 +298,7 @@ static void printUnquotedString(std::string & res, std::string_view s)
template<class ForwardIterator>
static void printStrings(std::string & res, ForwardIterator i, ForwardIterator j)
static void printStrings(string & res, ForwardIterator i, ForwardIterator j)
{
res += '[';
bool first = true;
@@ -352,7 +311,7 @@ static void printStrings(std::string & res, ForwardIterator i, ForwardIterator j
template<class ForwardIterator>
static void printUnquotedStrings(std::string & res, ForwardIterator i, ForwardIterator j)
static void printUnquotedStrings(string & res, ForwardIterator i, ForwardIterator j)
{
res += '[';
bool first = true;
@@ -364,10 +323,10 @@ static void printUnquotedStrings(std::string & res, ForwardIterator i, ForwardIt
}
std::string Derivation::unparse(const Store & store, bool maskOutputs,
string Derivation::unparse(const Store & store, bool maskOutputs,
std::map<std::string, StringSet> * actualInputs) const
{
std::string s;
string s;
s.reserve(65536);
s += "Derive([";
@@ -376,33 +335,27 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs,
if (first) first = false; else s += ',';
s += '('; printUnquotedString(s, i.first);
std::visit(overloaded {
[&](const DerivationOutput::InputAddressed & doi) {
[&](const DerivationOutputInputAddressed & doi) {
s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(doi.path));
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, "");
},
[&](const DerivationOutput::CAFixed & dof) {
[&](const DerivationOutputCAFixed & dof) {
s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(dof.path(store, name, i.first)));
s += ','; printUnquotedString(s, dof.hash.printMethodAlgo());
s += ','; printUnquotedString(s, dof.hash.hash.to_string(Base16, false));
},
[&](const DerivationOutput::CAFloating & dof) {
[&](const DerivationOutputCAFloating & dof) {
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType));
s += ','; printUnquotedString(s, "");
},
[&](const DerivationOutput::Deferred &) {
[&](const DerivationOutputDeferred &) {
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, "");
},
[&](const DerivationOutputImpure & doi) {
// FIXME
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, makeFileIngestionPrefix(doi.method) + printHashType(doi.hashType));
s += ','; printUnquotedString(s, "impure");
}
}, i.second.raw());
}, i.second.output);
s += ')';
}
@@ -448,7 +401,7 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs,
// FIXME: remove
bool isDerivation(const std::string & fileName)
bool isDerivation(const string & fileName)
{
return hasSuffix(fileName, drvExtension);
}
@@ -466,100 +419,49 @@ std::string outputPathName(std::string_view drvName, std::string_view outputName
DerivationType BasicDerivation::type() const
{
std::set<std::string_view>
inputAddressedOutputs,
fixedCAOutputs,
floatingCAOutputs,
deferredIAOutputs,
impureOutputs;
std::set<std::string_view> inputAddressedOutputs, fixedCAOutputs, floatingCAOutputs, deferredIAOutputs;
std::optional<HashType> floatingHashType;
for (auto & i : outputs) {
std::visit(overloaded {
[&](const DerivationOutput::InputAddressed &) {
[&](const DerivationOutputInputAddressed &) {
inputAddressedOutputs.insert(i.first);
},
[&](const DerivationOutput::CAFixed &) {
[&](const DerivationOutputCAFixed &) {
fixedCAOutputs.insert(i.first);
},
[&](const DerivationOutput::CAFloating & dof) {
[&](const DerivationOutputCAFloating & dof) {
floatingCAOutputs.insert(i.first);
if (!floatingHashType) {
floatingHashType = dof.hashType;
} else {
if (*floatingHashType != dof.hashType)
throw Error("all floating outputs must use the same hash type");
throw Error("All floating outputs must use the same hash type");
}
},
[&](const DerivationOutput::Deferred &) {
deferredIAOutputs.insert(i.first);
[&](const DerivationOutputDeferred &) {
deferredIAOutputs.insert(i.first);
},
[&](const DerivationOutput::Impure &) {
impureOutputs.insert(i.first);
},
}, i.second.raw());
}, i.second.output);
}
if (inputAddressedOutputs.empty()
&& fixedCAOutputs.empty()
&& floatingCAOutputs.empty()
&& deferredIAOutputs.empty()
&& impureOutputs.empty())
throw Error("must have at least one output");
if (!inputAddressedOutputs.empty()
&& fixedCAOutputs.empty()
&& floatingCAOutputs.empty()
&& deferredIAOutputs.empty()
&& impureOutputs.empty())
return DerivationType::InputAddressed {
.deferred = false,
};
if (inputAddressedOutputs.empty()
&& !fixedCAOutputs.empty()
&& floatingCAOutputs.empty()
&& deferredIAOutputs.empty()
&& impureOutputs.empty())
{
if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) {
throw Error("Must have at least one output");
} else if (! inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) {
return DerivationType::InputAddressed;
} else if (inputAddressedOutputs.empty() && ! fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) {
if (fixedCAOutputs.size() > 1)
// FIXME: Experimental feature?
throw Error("only one fixed output is allowed for now");
throw Error("Only one fixed output is allowed for now");
if (*fixedCAOutputs.begin() != "out")
throw Error("single fixed output must be named \"out\"");
return DerivationType::ContentAddressed {
.sandboxed = false,
.fixed = true,
};
throw Error("Single fixed output must be named \"out\"");
return DerivationType::CAFixed;
} else if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && ! floatingCAOutputs.empty() && deferredIAOutputs.empty()) {
return DerivationType::CAFloating;
} else if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && !deferredIAOutputs.empty()) {
return DerivationType::DeferredInputAddressed;
} else {
throw Error("Can't mix derivation output types");
}
if (inputAddressedOutputs.empty()
&& fixedCAOutputs.empty()
&& !floatingCAOutputs.empty()
&& deferredIAOutputs.empty()
&& impureOutputs.empty())
return DerivationType::ContentAddressed {
.sandboxed = true,
.fixed = false,
};
if (inputAddressedOutputs.empty()
&& fixedCAOutputs.empty()
&& floatingCAOutputs.empty()
&& !deferredIAOutputs.empty()
&& impureOutputs.empty())
return DerivationType::InputAddressed {
.deferred = true,
};
if (inputAddressedOutputs.empty()
&& fixedCAOutputs.empty()
&& floatingCAOutputs.empty()
&& deferredIAOutputs.empty()
&& !impureOutputs.empty())
return DerivationType::Impure { };
throw Error("can't mix derivation output types");
}
@@ -571,7 +473,7 @@ Sync<DrvHashes> drvHashes;
/* Look up the derivation by value and memoize the
`hashDerivationModulo` call.
*/
static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPath)
static const DrvHashModulo pathDerivationModulo(Store & store, const StorePath & drvPath)
{
{
auto hashes = drvHashes.lock();
@@ -606,87 +508,92 @@ static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPa
don't leak the provenance of fixed outputs, reducing pointless cache
misses as the build itself won't know this.
*/
DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs)
DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs)
{
auto type = drv.type();
bool isDeferred = false;
/* Return a fixed hash for fixed-output derivations. */
if (type.isFixed()) {
switch (drv.type()) {
case DerivationType::CAFixed: {
std::map<std::string, Hash> outputHashes;
for (const auto & i : drv.outputs) {
auto & dof = std::get<DerivationOutput::CAFixed>(i.second.raw());
auto & dof = std::get<DerivationOutputCAFixed>(i.second.output);
auto hash = hashString(htSHA256, "fixed:out:"
+ dof.hash.printMethodAlgo() + ":"
+ dof.hash.hash.to_string(Base16, false) + ":"
+ store.printStorePath(dof.path(store, drv.name, i.first)));
outputHashes.insert_or_assign(i.first, std::move(hash));
}
return DrvHash {
.hashes = outputHashes,
.kind = DrvHash::Kind::Regular,
};
return outputHashes;
}
case DerivationType::CAFloating:
isDeferred = true;
break;
case DerivationType::InputAddressed:
break;
case DerivationType::DeferredInputAddressed:
break;
}
if (!type.isPure()) {
std::map<std::string, Hash> outputHashes;
for (const auto & [outputName, _] : drv.outputs)
outputHashes.insert_or_assign(outputName, impureOutputHash);
return DrvHash {
.hashes = outputHashes,
.kind = DrvHash::Kind::Deferred,
};
}
auto kind = std::visit(overloaded {
[](const DerivationType::InputAddressed & ia) {
/* This might be a "pesimistically" deferred output, so we don't
"taint" the kind yet. */
return DrvHash::Kind::Regular;
},
[](const DerivationType::ContentAddressed & ca) {
return ca.fixed
? DrvHash::Kind::Regular
: DrvHash::Kind::Deferred;
},
[](const DerivationType::Impure &) -> DrvHash::Kind {
assert(false);
}
}, drv.type().raw());
/* For other derivations, replace the inputs paths with recursive
calls to this function. */
std::map<std::string, StringSet> inputs2;
for (auto & [drvPath, inputOutputs0] : drv.inputDrvs) {
// Avoid lambda capture restriction with standard / Clang
auto & inputOutputs = inputOutputs0;
const auto & res = pathDerivationModulo(store, drvPath);
if (res.kind == DrvHash::Kind::Deferred)
kind = DrvHash::Kind::Deferred;
for (auto & outputName : inputOutputs) {
const auto h = res.hashes.at(outputName);
inputs2[h.to_string(Base16, false)].insert(outputName);
}
for (auto & i : drv.inputDrvs) {
const auto & res = pathDerivationModulo(store, i.first);
std::visit(overloaded {
// Regular non-CA derivation, replace derivation
[&](const Hash & drvHash) {
inputs2.insert_or_assign(drvHash.to_string(Base16, false), i.second);
},
[&](const DeferredHash & deferredHash) {
isDeferred = true;
inputs2.insert_or_assign(deferredHash.hash.to_string(Base16, false), i.second);
},
// CA derivation's output hashes
[&](const CaOutputHashes & outputHashes) {
std::set<std::string> justOut = { "out" };
for (auto & output : i.second) {
/* Put each one in with a single "out" output.. */
const auto h = outputHashes.at(output);
inputs2.insert_or_assign(
h.to_string(Base16, false),
justOut);
}
},
}, res);
}
auto hash = hashString(htSHA256, drv.unparse(store, maskOutputs, &inputs2));
std::map<std::string, Hash> outputHashes;
for (const auto & [outputName, _] : drv.outputs) {
outputHashes.insert_or_assign(outputName, hash);
}
return DrvHash {
.hashes = outputHashes,
.kind = kind,
};
if (isDeferred)
return DeferredHash { hash };
else
return hash;
}
std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation & drv)
{
return hashDerivationModulo(store, drv, true).hashes;
std::map<std::string, Hash> res;
std::visit(overloaded {
[&](const Hash & drvHash) {
for (auto & outputName : drv.outputNames()) {
res.insert({outputName, drvHash});
}
},
[&](const DeferredHash & deferredHash) {
for (auto & outputName : drv.outputNames()) {
res.insert({outputName, deferredHash.hash});
}
},
[&](const CaOutputHashes & outputHashes) {
res = outputHashes;
},
}, hashDerivationModulo(store, drv, true));
return res;
}
bool wantOutput(const std::string & output, const std::set<std::string> & wanted)
bool wantOutput(const string & output, const std::set<string> & wanted)
{
return wanted.empty() || wanted.find(output) != wanted.end();
}
@@ -709,8 +616,7 @@ StringSet BasicDerivation::outputNames() const
return names;
}
DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const Store & store) const
{
DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const Store & store) const {
DerivationOutputsAndOptPaths outsAndOptPaths;
for (auto output : outputs)
outsAndOptPaths.insert(std::make_pair(
@@ -721,8 +627,7 @@ DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const Store & s
return outsAndOptPaths;
}
std::string_view BasicDerivation::nameFromPath(const StorePath & drvPath)
{
std::string_view BasicDerivation::nameFromPath(const StorePath & drvPath) {
auto nameWithSuffix = drvPath.name();
constexpr std::string_view extension = ".drv";
assert(hasSuffix(nameWithSuffix, extension));
@@ -764,32 +669,27 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
for (auto & i : drv.outputs) {
out << i.first;
std::visit(overloaded {
[&](const DerivationOutput::InputAddressed & doi) {
[&](const DerivationOutputInputAddressed & doi) {
out << store.printStorePath(doi.path)
<< ""
<< "";
},
[&](const DerivationOutput::CAFixed & dof) {
[&](const DerivationOutputCAFixed & dof) {
out << store.printStorePath(dof.path(store, drv.name, i.first))
<< dof.hash.printMethodAlgo()
<< dof.hash.hash.to_string(Base16, false);
},
[&](const DerivationOutput::CAFloating & dof) {
[&](const DerivationOutputCAFloating & dof) {
out << ""
<< (makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType))
<< "";
},
[&](const DerivationOutput::Deferred &) {
[&](const DerivationOutputDeferred &) {
out << ""
<< ""
<< "";
},
[&](const DerivationOutput::Impure & doi) {
out << ""
<< (makeFileIngestionPrefix(doi.method) + printHashType(doi.hashType))
<< "impure";
},
}, i.second.raw());
}, i.second.output);
}
worker_proto::write(store, out, drv.inputSrcs);
out << drv.platform << drv.builder << drv.args;
@@ -814,19 +714,21 @@ std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath
}
static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites)
{
for (auto & rewrite : rewrites) {
static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites) {
debug("Rewriting the derivation");
for (auto &rewrite: rewrites) {
debug("rewriting %s as %s", rewrite.first, rewrite.second);
}
drv.builder = rewriteStrings(drv.builder, rewrites);
for (auto & arg : drv.args) {
for (auto & arg: drv.args) {
arg = rewriteStrings(arg, rewrites);
}
StringPairs newEnv;
for (auto & envVar : drv.env) {
for (auto & envVar: drv.env) {
auto envName = rewriteStrings(envVar.first, rewrites);
auto envValue = rewriteStrings(envVar.second, rewrites);
newEnv.emplace(envName, envValue);
@@ -835,52 +737,43 @@ static void rewriteDerivation(Store & store, BasicDerivation & drv, const String
auto hashModulo = hashDerivationModulo(store, Derivation(drv), true);
for (auto & [outputName, output] : drv.outputs) {
if (std::holds_alternative<DerivationOutput::Deferred>(output.raw())) {
auto & h = hashModulo.hashes.at(outputName);
if (std::holds_alternative<DerivationOutputDeferred>(output.output)) {
Hash h = std::get<Hash>(hashModulo);
auto outPath = store.makeOutputPath(outputName, h, drv.name);
drv.env[outputName] = store.printStorePath(outPath);
output = DerivationOutput::InputAddressed {
.path = std::move(outPath),
output = DerivationOutput {
.output = DerivationOutputInputAddressed {
.path = std::move(outPath),
},
};
}
}
}
std::optional<BasicDerivation> Derivation::tryResolve(Store & store) const
{
std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
for (auto & input : inputDrvs)
for (auto & [outputName, outputPath] : store.queryPartialDerivationOutputMap(input.first))
if (outputPath)
inputDrvOutputs.insert_or_assign({input.first, outputName}, *outputPath);
return tryResolve(store, inputDrvOutputs);
}
std::optional<BasicDerivation> Derivation::tryResolve(
Store & store,
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const
{
std::optional<BasicDerivation> Derivation::tryResolve(Store & store) {
BasicDerivation resolved { *this };
// Input paths that we'll want to rewrite in the derivation
StringMap inputRewrites;
for (auto & [inputDrv, inputOutputs] : inputDrvs) {
for (auto & outputName : inputOutputs) {
if (auto actualPath = get(inputDrvOutputs, { inputDrv, outputName })) {
inputRewrites.emplace(
downstreamPlaceholder(store, inputDrv, outputName),
store.printStorePath(*actualPath));
resolved.inputSrcs.insert(*actualPath);
} else {
warn("output '%s' of input '%s' missing, aborting the resolving",
for (auto & input : inputDrvs) {
auto inputDrvOutputs = store.queryPartialDerivationOutputMap(input.first);
StringSet newOutputNames;
for (auto & outputName : input.second) {
auto actualPathOpt = inputDrvOutputs.at(outputName);
if (!actualPathOpt) {
warn("output %s of input %s missing, aborting the resolving",
outputName,
store.printStorePath(inputDrv));
return {};
store.printStorePath(input.first)
);
return std::nullopt;
}
auto actualPath = *actualPathOpt;
inputRewrites.emplace(
downstreamPlaceholder(store, input.first, outputName),
store.printStorePath(actualPath));
resolved.inputSrcs.insert(std::move(actualPath));
}
}
@@ -889,6 +782,4 @@ std::optional<BasicDerivation> Derivation::tryResolve(
return resolved;
}
const Hash impureOutputHash = hashString(htSHA256, "impure");
}
+49 -123
View File
@@ -4,7 +4,6 @@
#include "types.hh"
#include "hash.hh"
#include "content-address.hh"
#include "repair-flag.hh"
#include "sync.hh"
#include <map>
@@ -41,124 +40,70 @@ struct DerivationOutputCAFloating
};
/* Input-addressed output which depends on a (CA) derivation whose hash isn't
* known yet.
* known atm
*/
struct DerivationOutputDeferred {};
/* Impure output which is moved to a content-addressed location (like
CAFloating) but isn't registered as a realization.
*/
struct DerivationOutputImpure
struct DerivationOutput
{
/* information used for expected hash computation */
FileIngestionMethod method;
HashType hashType;
};
typedef std::variant<
DerivationOutputInputAddressed,
DerivationOutputCAFixed,
DerivationOutputCAFloating,
DerivationOutputDeferred,
DerivationOutputImpure
> _DerivationOutputRaw;
struct DerivationOutput : _DerivationOutputRaw
{
using Raw = _DerivationOutputRaw;
using Raw::Raw;
using InputAddressed = DerivationOutputInputAddressed;
using CAFixed = DerivationOutputCAFixed;
using CAFloating = DerivationOutputCAFloating;
using Deferred = DerivationOutputDeferred;
using Impure = DerivationOutputImpure;
std::variant<
DerivationOutputInputAddressed,
DerivationOutputCAFixed,
DerivationOutputCAFloating,
DerivationOutputDeferred
> output;
/* Note, when you use this function you should make sure that you're passing
the right derivation name. When in doubt, you should use the safer
interface provided by BasicDerivation::outputsAndOptPaths */
std::optional<StorePath> path(const Store & store, std::string_view drvName, std::string_view outputName) const;
inline const Raw & raw() const {
return static_cast<const Raw &>(*this);
}
};
typedef std::map<std::string, DerivationOutput> DerivationOutputs;
typedef std::map<string, DerivationOutput> DerivationOutputs;
/* These are analogues to the previous DerivationOutputs data type, but they
also contains, for each output, the (optional) store path in which it would
be written. To calculate values of these types, see the corresponding
functions in BasicDerivation */
typedef std::map<std::string, std::pair<DerivationOutput, std::optional<StorePath>>>
typedef std::map<string, std::pair<DerivationOutput, std::optional<StorePath>>>
DerivationOutputsAndOptPaths;
/* For inputs that are sub-derivations, we specify exactly which
output IDs we are interested in. */
typedef std::map<StorePath, StringSet> DerivationInputs;
struct DerivationType_InputAddressed {
bool deferred;
typedef std::map<string, string> StringPairs;
enum struct DerivationType : uint8_t {
InputAddressed,
DeferredInputAddressed,
CAFixed,
CAFloating,
};
struct DerivationType_ContentAddressed {
bool sandboxed;
bool fixed;
};
/* Do the outputs of the derivation have paths calculated from their content,
or from the derivation itself? */
bool derivationIsCA(DerivationType);
struct DerivationType_Impure {
};
/* Is the content of the outputs fixed a-priori via a hash? Never true for
non-CA derivations. */
bool derivationIsFixed(DerivationType);
typedef std::variant<
DerivationType_InputAddressed,
DerivationType_ContentAddressed,
DerivationType_Impure
> _DerivationTypeRaw;
/* Is the derivation impure and needs to access non-deterministic resources, or
pure and can be sandboxed? Note that whether or not we actually sandbox the
derivation is controlled separately. Never true for non-CA derivations. */
bool derivationIsImpure(DerivationType);
struct DerivationType : _DerivationTypeRaw {
using Raw = _DerivationTypeRaw;
using Raw::Raw;
using InputAddressed = DerivationType_InputAddressed;
using ContentAddressed = DerivationType_ContentAddressed;
using Impure = DerivationType_Impure;
/* Do the outputs of the derivation have paths calculated from their content,
or from the derivation itself? */
bool isCA() const;
/* Is the content of the outputs fixed a-priori via a hash? Never true for
non-CA derivations. */
bool isFixed() const;
/* Whether the derivation is fully sandboxed. If false, the
sandbox is opened up, e.g. the derivation has access to the
network. Note that whether or not we actually sandbox the
derivation is controlled separately. Always true for non-CA
derivations. */
bool isSandboxed() const;
/* Whether the derivation is expected to produce the same result
every time, and therefore it only needs to be built once. This
is only false for derivations that have the attribute '__impure
= true'. */
bool isPure() const;
/* Does the derivation knows its own output paths?
Only true when there's no floating-ca derivation involved in the
closure, or if fixed output.
*/
bool hasKnownOutputPaths() const;
inline const Raw & raw() const {
return static_cast<const Raw &>(*this);
}
};
/* Does the derivation knows its own output paths?
* Only true when there's no floating-ca derivation involved in the closure.
*/
bool derivationHasKnownOutputPaths(DerivationType);
struct BasicDerivation
{
DerivationOutputs outputs; /* keyed on symbolic IDs */
StorePathSet inputSrcs; /* inputs that are sources */
std::string platform;
string platform;
Path builder;
Strings args;
StringPairs env;
@@ -197,14 +142,7 @@ struct Derivation : BasicDerivation
added directly to input sources.
2. Input placeholders are replaced with realized input store paths. */
std::optional<BasicDerivation> tryResolve(Store & store) const;
/* Like the above, but instead of querying the Nix database for
realisations, uses a given mapping from input derivation paths
+ output names to actual output store paths. */
std::optional<BasicDerivation> tryResolve(
Store & store,
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const;
std::optional<BasicDerivation> tryResolve(Store & store);
Derivation() = default;
Derivation(const BasicDerivation & bd) : BasicDerivation(bd) { }
@@ -214,6 +152,8 @@ struct Derivation : BasicDerivation
class Store;
enum RepairFlag : bool { NoRepair = false, Repair = true };
/* Write a derivation to the Nix store, and return its path. */
StorePath writeDerivation(Store & store,
const Derivation & drv,
@@ -224,7 +164,7 @@ StorePath writeDerivation(Store & store,
Derivation parseDerivation(const Store & store, std::string && s, std::string_view name);
// FIXME: remove
bool isDerivation(const std::string & fileName);
bool isDerivation(const string & fileName);
/* Calculate the name that will be used for the store path for this
output.
@@ -233,27 +173,17 @@ bool isDerivation(const std::string & fileName);
the output name is "out". */
std::string outputPathName(std::string_view drvName, std::string_view outputName);
// known CA drv's output hashes, current just for fixed-output derivations
// whose output hashes are always known since they are fixed up-front.
typedef std::map<std::string, Hash> CaOutputHashes;
// The hashes modulo of a derivation.
//
// Each output is given a hash, although in practice only the content-addressed
// derivations (fixed-output or not) will have a different hash for each
// output.
struct DrvHash {
std::map<std::string, Hash> hashes;
struct DeferredHash { Hash hash; };
enum struct Kind : bool {
// Statically determined derivations.
// This hash will be directly used to compute the output paths
Regular,
// Floating-output derivations (and their reverse dependencies).
Deferred,
};
Kind kind;
};
void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept;
typedef std::variant<
Hash, // regular DRV normalized hash
CaOutputHashes, // Fixed-output derivation hashes
DeferredHash // Deferred hashes for floating outputs drvs and their dependencies
> DrvHashModulo;
/* Returns hashes with the details of fixed-output subderivations
expunged.
@@ -278,23 +208,21 @@ void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept;
ATerm, after subderivations have been likewise expunged from that
derivation.
*/
DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs);
DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs);
/*
Return a map associating each output to a hash that uniquely identifies its
derivation (modulo the self-references).
FIXME: what is the Hash in this map?
*/
std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation & drv);
std::map<std::string, Hash> staticOutputHashes(Store& store, const Derivation& drv);
/* Memoisation of hashDerivationModulo(). */
typedef std::map<StorePath, DrvHash> DrvHashes;
typedef std::map<StorePath, DrvHashModulo> DrvHashes;
// FIXME: global, though at least thread-safe.
extern Sync<DrvHashes> drvHashes;
bool wantOutput(const std::string & output, const std::set<std::string> & wanted);
bool wantOutput(const string & output, const std::set<string> & wanted);
struct Source;
struct Sink;
@@ -319,6 +247,4 @@ std::string hashPlaceholder(const std::string_view outputName);
dependency which is a CA derivation. */
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName);
extern const Hash impureOutputHash;
}
Loaded 100 of 265 files, more files were not shown because too many files have changed in this diff. Show more