Compare commits
1 Commits
diff-closu
...
actions-ar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
577ee41730 |
@@ -1,7 +1,6 @@
|
||||
((c++-mode . (
|
||||
(c-file-style . "k&r")
|
||||
(c-basic-offset . 4)
|
||||
(c-block-comment-prefix . " ")
|
||||
(indent-tabs-mode . nil)
|
||||
(tab-width . 4)
|
||||
(show-trailing-whitespace . t)
|
||||
|
||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
17
.github/workflows/test.yml
vendored
17
.github/workflows/test.yml
vendored
@@ -10,15 +10,10 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: cachix/install-nix-action@v10
|
||||
- run: nix-build release.nix --arg nix '{ outPath = ./.; revCount = 123; shortRev = "abcdefgh"; }' --arg systems '[ builtins.currentSystem ]' -A installerScript -A perlBindings
|
||||
macos_perf_test:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Disable syspolicy assessments
|
||||
run: |
|
||||
spctl --status
|
||||
sudo spctl --master-disable
|
||||
- uses: actions/checkout@v2
|
||||
- uses: cachix/install-nix-action@v10
|
||||
- uses: cachix/install-nix-action@v8
|
||||
- run: nix-build release.nix --arg nix '{ outPath = ./.; revCount = 123; shortRev = "abcdefgh"; }' --arg systems '[ builtins.currentSystem ]' -A installerScript -A perlBindings
|
||||
- run: nix-build release.nix --arg nix '{ outPath = ./.; revCount = 123; shortRev = "abcdefgh"; }' --arg systems '[ builtins.currentSystem ]' -A binaryTarball -o binaryTarball
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: binaryTarball
|
||||
path: binaryTarball/*.tar.*
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -49,9 +49,6 @@ perl/Makefile.config
|
||||
# /src/libstore/
|
||||
*.gen.*
|
||||
|
||||
# /src/libutil/
|
||||
/src/libutil/tests/libutil-tests
|
||||
|
||||
/src/nix/nix
|
||||
|
||||
# /src/nix-env/
|
||||
|
||||
1
Makefile
1
Makefile
@@ -1,6 +1,7 @@
|
||||
makefiles = \
|
||||
mk/precompiled-headers.mk \
|
||||
local.mk \
|
||||
nix-rust/local.mk \
|
||||
src/libutil/local.mk \
|
||||
src/libutil/tests/local.mk \
|
||||
src/libstore/local.mk \
|
||||
|
||||
@@ -70,7 +70,7 @@ path just built.</para>
|
||||
|
||||
<screen>
|
||||
$ nix-build ./deterministic.nix -A stable
|
||||
this derivation will be built:
|
||||
these derivations will be built:
|
||||
/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv
|
||||
building '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
|
||||
/nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable
|
||||
@@ -85,7 +85,7 @@ checking outputs of '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
|
||||
|
||||
<screen>
|
||||
$ nix-build ./deterministic.nix -A unstable
|
||||
this derivation will be built:
|
||||
these derivations will be built:
|
||||
/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv
|
||||
building '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
|
||||
/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable
|
||||
@@ -193,7 +193,7 @@ repeat = 1
|
||||
An example output of this configuration:
|
||||
<screen>
|
||||
$ nix-build ./test.nix -A unstable
|
||||
this derivation will be built:
|
||||
these derivations will be built:
|
||||
/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv
|
||||
building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 1/2)...
|
||||
building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 2/2)...
|
||||
|
||||
@@ -122,7 +122,7 @@ post-build-hook = /etc/nix/upload-to-cache.sh
|
||||
|
||||
<screen>
|
||||
$ nix-build -E '(import <nixpkgs> {}).writeText "example" (builtins.toString builtins.currentTime)'
|
||||
this derivation will be built:
|
||||
these derivations will be built:
|
||||
/nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv
|
||||
building '/nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv'...
|
||||
running post-build-hook '/home/grahamc/projects/github.com/NixOS/nix/post-hook.sh'...
|
||||
|
||||
@@ -516,7 +516,7 @@ source:
|
||||
$ nix-env -f '<nixpkgs>' -iA hello --dry-run
|
||||
(dry run; not doing anything)
|
||||
installing ‘hello-2.10’
|
||||
this path will be fetched (0.04 MiB download, 0.19 MiB unpacked):
|
||||
these paths will be fetched (0.04 MiB download, 0.19 MiB unpacked):
|
||||
/nix/store/wkhdf9jinag5750mqlax6z2zbwhqb76n-hello-2.10
|
||||
<replaceable>...</replaceable></screen>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<nop xmlns="http://docbook.org/ns/docbook">
|
||||
|
||||
|
||||
<arg><option>--help</option></arg>
|
||||
<arg><option>--version</option></arg>
|
||||
<arg rep='repeat'>
|
||||
@@ -11,10 +11,6 @@
|
||||
<arg>
|
||||
<arg choice='plain'><option>--quiet</option></arg>
|
||||
</arg>
|
||||
<arg>
|
||||
<option>--log-format</option>
|
||||
<replaceable>format</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<group choice='plain'>
|
||||
<arg choice='plain'><option>--no-build-output</option></arg>
|
||||
|
||||
@@ -92,37 +92,6 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="opt-log-format"><term><option>--log-format</option> <replaceable>format</replaceable></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para>This option can be used to change the output of the log format, with
|
||||
<replaceable>format</replaceable> being one of:</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term>raw</term>
|
||||
<listitem><para>This is the raw format, as outputted by nix-build.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term>internal-json</term>
|
||||
<listitem><para>Outputs the logs in a structured manner. NOTE: the json schema is not guarantees to be stable between releases.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term>bar</term>
|
||||
<listitem><para>Only display a progress bar during the builds.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term>bar-with-logs</term>
|
||||
<listitem><para>Display the raw logs, with the progress bar at the bottom.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--no-build-output</option> / <option>-Q</option></term>
|
||||
|
||||
<listitem><para>By default, output written by builders to standard
|
||||
|
||||
@@ -39,7 +39,7 @@ bundle.</para>
|
||||
<step><para>Set the environment variable and install Nix</para>
|
||||
<screen>
|
||||
$ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
|
||||
$ sh <(curl -L https://nixos.org/nix/install)
|
||||
$ sh <(curl https://nixos.org/nix/install)
|
||||
</screen></step>
|
||||
|
||||
<step><para>In the shell profile and rc files (for example,
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
$ sh <(curl -L https://nixos.org/nix/install)
|
||||
$ sh <(curl https://nixos.org/nix/install)
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
@@ -39,7 +39,7 @@
|
||||
To explicitly select a single-user installation on your system:
|
||||
|
||||
<screen>
|
||||
sh <(curl -L https://nixos.org/nix/install) --no-daemon
|
||||
sh <(curl https://nixos.org/nix/install) --no-daemon
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
@@ -97,7 +97,7 @@ $ rm -rf /nix
|
||||
installation on your system:
|
||||
</para>
|
||||
|
||||
<screen>sh <(curl -L https://nixos.org/nix/install) --daemon</screen>
|
||||
<screen>sh <(curl https://nixos.org/nix/install) --daemon</screen>
|
||||
|
||||
<para>
|
||||
The multi-user installation of Nix will create build users between
|
||||
@@ -178,7 +178,7 @@ sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
is a bit of a misnomer). To use this approach, just install Nix with:
|
||||
</para>
|
||||
|
||||
<screen>$ sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume</screen>
|
||||
<screen>$ sh <(curl https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume</screen>
|
||||
|
||||
<para>
|
||||
If you don't like the sound of this, you'll want to weigh the
|
||||
@@ -429,7 +429,7 @@ LABEL=Nix\040Store /nix apfs rw,nobrowse
|
||||
NixOS.org installation script:
|
||||
|
||||
<screen>
|
||||
sh <(curl -L https://nixos.org/nix/install)
|
||||
sh <(curl https://nixos.org/nix/install)
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ to subsequent chapters.</para>
|
||||
<step><para>Install single-user Nix by running the following:
|
||||
|
||||
<screen>
|
||||
$ bash <(curl -L https://nixos.org/nix/install)
|
||||
$ bash <(curl https://nixos.org/nix/install)
|
||||
</screen>
|
||||
|
||||
This will install Nix in <filename>/nix</filename>. The install script
|
||||
|
||||
@@ -142,12 +142,8 @@ $oldName =~ s/"//g;
|
||||
sub getStorePath {
|
||||
my ($jobName) = @_;
|
||||
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
|
||||
for my $product (values %{$buildInfo->{buildproducts}}) {
|
||||
next unless $product->{type} eq "nix-build";
|
||||
next if $product->{path} =~ /[a-z]+$/;
|
||||
return $product->{path};
|
||||
}
|
||||
die;
|
||||
die unless $buildInfo->{buildproducts}->{1}->{type} eq "nix-build";
|
||||
return $buildInfo->{buildproducts}->{1}->{path};
|
||||
}
|
||||
|
||||
write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
||||
@@ -170,5 +166,15 @@ $channelsBucket->add_key(
|
||||
chdir("/home/eelco/Dev/nix-pristine") or die;
|
||||
system("git remote update origin") == 0 or die;
|
||||
system("git tag --force --sign $version $nixRev -m 'Tagging release $version'") == 0 or die;
|
||||
system("git push --tags") == 0 or die;
|
||||
system("git push --force-with-lease origin $nixRev:refs/heads/latest-release") == 0 or die;
|
||||
|
||||
# Update the website.
|
||||
my $siteDir = "/home/eelco/Dev/nixos-homepage-pristine";
|
||||
|
||||
system("cd $siteDir && git pull") == 0 or die;
|
||||
|
||||
write_file("$siteDir/nix-release.tt",
|
||||
"[%-\n" .
|
||||
"latestNixVersion = \"$version\"\n" .
|
||||
"-%]\n");
|
||||
|
||||
system("cd $siteDir && git commit -a -m 'Nix $version released'") == 0 or die;
|
||||
|
||||
@@ -125,8 +125,7 @@ define build-library
|
||||
$(1)_PATH := $$(_d)/$$($(1)_NAME).a
|
||||
|
||||
$$($(1)_PATH): $$($(1)_OBJS) | $$(_d)/
|
||||
$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$?
|
||||
$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
|
||||
$(trace-ar) $(AR) crs $$@ $$?
|
||||
|
||||
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS)
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -u
|
||||
|
||||
red=""
|
||||
green=""
|
||||
yellow=""
|
||||
normal=""
|
||||
|
||||
post_run_msg="ran test $1..."
|
||||
if [ -t 1 ]; then
|
||||
red="[31;1m"
|
||||
green="[32;1m"
|
||||
yellow="[33;1m"
|
||||
normal="[m"
|
||||
fi
|
||||
(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} init.sh 2>/dev/null > /dev/null)
|
||||
log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)"
|
||||
status=$?
|
||||
if [ $status -eq 0 ]; then
|
||||
echo "$post_run_msg [${green}PASS$normal]"
|
||||
elif [ $status -eq 99 ]; then
|
||||
echo "$post_run_msg [${yellow}SKIP$normal]"
|
||||
else
|
||||
echo "$post_run_msg [${red}FAIL$normal]"
|
||||
echo "$log" | sed 's/^/ /'
|
||||
exit "$status"
|
||||
fi
|
||||
44
mk/tests.mk
44
mk/tests.mk
@@ -1,15 +1,45 @@
|
||||
# Run program $1 as part of ‘make installcheck’.
|
||||
|
||||
test-deps =
|
||||
|
||||
define run-install-test
|
||||
|
||||
installcheck: $1.test
|
||||
installcheck: $1
|
||||
|
||||
.PHONY: $1.test
|
||||
$1.test: $1 $(test-deps)
|
||||
@env TEST_NAME=$(notdir $(basename $1)) TESTS_ENVIRONMENT="$(tests-environment)" mk/run_test.sh $1
|
||||
_installcheck-list += $1
|
||||
|
||||
endef
|
||||
|
||||
# Color code from https://unix.stackexchange.com/a/10065
|
||||
installcheck:
|
||||
@total=0; failed=0; \
|
||||
red=""; \
|
||||
green=""; \
|
||||
yellow=""; \
|
||||
normal=""; \
|
||||
if [ -t 1 ]; then \
|
||||
red="[31;1m"; \
|
||||
green="[32;1m"; \
|
||||
yellow="[33;1m"; \
|
||||
normal="[m"; \
|
||||
fi; \
|
||||
for i in $(_installcheck-list); do \
|
||||
total=$$((total + 1)); \
|
||||
printf "running test $$i..."; \
|
||||
log="$$(cd $$(dirname $$i) && $(tests-environment) $$(basename $$i) 2>&1)"; \
|
||||
status=$$?; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
echo " [$${green}PASS$$normal]"; \
|
||||
elif [ $$status -eq 99 ]; then \
|
||||
echo " [$${yellow}SKIP$$normal]"; \
|
||||
else \
|
||||
echo " [$${red}FAIL$$normal]"; \
|
||||
echo "$$log" | sed 's/^/ /'; \
|
||||
failed=$$((failed + 1)); \
|
||||
fi; \
|
||||
done; \
|
||||
if [ "$$failed" != 0 ]; then \
|
||||
echo "$${red}$$failed out of $$total tests failed $$normal"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "$${green}All tests succeeded$$normal"; \
|
||||
fi
|
||||
|
||||
.PHONY: check installcheck
|
||||
|
||||
@@ -80,7 +80,7 @@ SV * queryReferences(char * path)
|
||||
SV * queryPathHash(char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash.to_string(Base32, true);
|
||||
auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash.to_string();
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@@ -106,7 +106,7 @@ SV * queryPathInfo(char * path, int base32)
|
||||
XPUSHs(&PL_sv_undef);
|
||||
else
|
||||
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(*info->deriver).c_str(), 0)));
|
||||
auto s = info->narHash.to_string(base32 ? Base32 : Base16, true);
|
||||
auto s = info->narHash.to_string(base32 ? Base32 : Base16);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
mXPUSHi(info->registrationTime);
|
||||
mXPUSHi(info->narSize);
|
||||
@@ -182,7 +182,7 @@ void importPaths(int fd, int dontCheckSigs)
|
||||
PPCODE:
|
||||
try {
|
||||
FdSource source(fd);
|
||||
store()->importPaths(source, dontCheckSigs ? NoCheckSigs : CheckSigs);
|
||||
store()->importPaths(source, nullptr, dontCheckSigs ? NoCheckSigs : CheckSigs);
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
@@ -274,8 +274,7 @@ int checkSignature(SV * publicKey_, SV * sig_, char * msg)
|
||||
SV * addToStore(char * srcPath, int recursive, char * algo)
|
||||
PPCODE:
|
||||
try {
|
||||
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
||||
auto path = store()->addToStore(std::string(baseNameOf(srcPath)), srcPath, method, parseHashType(algo));
|
||||
auto path = store()->addToStore(std::string(baseNameOf(srcPath)), srcPath, recursive, parseHashType(algo));
|
||||
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(path).c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@@ -286,8 +285,7 @@ SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h(hash, parseHashType(algo));
|
||||
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
||||
auto path = store()->makeFixedOutputPath(method, h, name);
|
||||
auto path = store()->makeFixedOutputPath(recursive, h, name);
|
||||
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(path).c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
|
||||
@@ -50,6 +50,7 @@ rec {
|
||||
libarchive
|
||||
boost
|
||||
nlohmann_json
|
||||
rustc cargo
|
||||
|
||||
# Tests
|
||||
git
|
||||
|
||||
63
release.nix
63
release.nix
@@ -12,8 +12,64 @@ let
|
||||
builtins.readFile ./.version
|
||||
+ (if officialRelease then "" else "pre${toString nix.revCount}_${nix.shortRev}");
|
||||
|
||||
# Create a "vendor" directory that contains the crates listed in
|
||||
# Cargo.lock. This allows Nix to be built without network access.
|
||||
vendoredCrates' =
|
||||
let
|
||||
lockFile = builtins.fromTOML (builtins.readFile nix-rust/Cargo.lock);
|
||||
|
||||
files = map (pkg: import <nix/fetchurl.nix> {
|
||||
url = "https://crates.io/api/v1/crates/${pkg.name}/${pkg.version}/download";
|
||||
sha256 = lockFile.metadata."checksum ${pkg.name} ${pkg.version} (registry+https://github.com/rust-lang/crates.io-index)";
|
||||
}) (builtins.filter (pkg: pkg.source or "" == "registry+https://github.com/rust-lang/crates.io-index") lockFile.package);
|
||||
|
||||
in pkgs.runCommand "cargo-vendor-dir" {}
|
||||
''
|
||||
mkdir -p $out/vendor
|
||||
|
||||
cat > $out/vendor/config <<EOF
|
||||
[source.crates-io]
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source.vendored-sources]
|
||||
directory = "vendor"
|
||||
EOF
|
||||
|
||||
${toString (builtins.map (file: ''
|
||||
mkdir $out/vendor/tmp
|
||||
tar xvf ${file} -C $out/vendor/tmp
|
||||
dir=$(echo $out/vendor/tmp/*)
|
||||
|
||||
# Add just enough metadata to keep Cargo happy.
|
||||
printf '{"files":{},"package":"${file.outputHash}"}' > "$dir/.cargo-checksum.json"
|
||||
|
||||
# Clean up some cruft from the winapi crates. FIXME: find
|
||||
# a way to remove winapi* from our dependencies.
|
||||
if [[ $dir =~ /winapi ]]; then
|
||||
find $dir -name "*.a" -print0 | xargs -0 rm -f --
|
||||
fi
|
||||
|
||||
mv "$dir" $out/vendor/
|
||||
|
||||
rm -rf $out/vendor/tmp
|
||||
'') files)}
|
||||
'';
|
||||
|
||||
jobs = rec {
|
||||
|
||||
vendoredCrates =
|
||||
with pkgs;
|
||||
runCommand "vendored-crates" {}
|
||||
''
|
||||
mkdir -p $out/nix-support
|
||||
name=nix-vendored-crates-${version}
|
||||
fn=$out/$name.tar.xz
|
||||
tar cvfJ $fn -C ${vendoredCrates'} vendor \
|
||||
--owner=0 --group=0 --mode=u+rw,uga+r \
|
||||
--transform "s,vendor,$name,"
|
||||
echo "file crates-tarball $fn" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
|
||||
build = pkgs.lib.genAttrs systems (system:
|
||||
|
||||
let pkgs = import nixpkgs { inherit system; }; in
|
||||
@@ -45,6 +101,8 @@ let
|
||||
patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||
''}
|
||||
|
||||
ln -sfn ${vendoredCrates'}/vendor/ nix-rust/vendor
|
||||
|
||||
(cd perl; autoreconf --install --force --verbose)
|
||||
'';
|
||||
|
||||
@@ -189,6 +247,11 @@ let
|
||||
|
||||
src = nix;
|
||||
|
||||
preConfigure =
|
||||
''
|
||||
ln -sfn ${vendoredCrates'}/vendor/ nix-rust/vendor
|
||||
'';
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
buildInputs = buildDeps ++ propagatedDeps;
|
||||
|
||||
@@ -31,7 +31,7 @@ readonly NIX_FIRST_BUILD_UID="30001"
|
||||
readonly NIX_ROOT="/nix"
|
||||
readonly NIX_EXTRA_CONF=${NIX_EXTRA_CONF:-}
|
||||
|
||||
readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshenv")
|
||||
readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshrc")
|
||||
readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
|
||||
readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
|
||||
|
||||
@@ -526,7 +526,7 @@ This script is going to call sudo a lot. Normally, it would show you
|
||||
exactly what commands it is running and why. However, the script is
|
||||
run in a headless fashion, like this:
|
||||
|
||||
$ curl -L https://nixos.org/nix/install | sh
|
||||
$ curl https://nixos.org/nix/install | sh
|
||||
|
||||
or maybe in a CI pipeline. Because of that, we're going to skip the
|
||||
verbose output in the interest of brevity.
|
||||
@@ -534,7 +534,7 @@ verbose output in the interest of brevity.
|
||||
If you would like to
|
||||
see the output, try like this:
|
||||
|
||||
$ curl -L -o install-nix https://nixos.org/nix/install
|
||||
$ curl -o install-nix https://nixos.org/nix/install
|
||||
$ sh ./install-nix
|
||||
|
||||
EOF
|
||||
|
||||
@@ -113,7 +113,7 @@ if [ "$(uname -s)" = "Darwin" ]; then
|
||||
(
|
||||
echo ""
|
||||
echo "Installing on macOS >=10.15 requires relocating the store to an apfs volume."
|
||||
echo "Use sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume or run the preparation steps manually."
|
||||
echo "Use sh <(curl https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume or run the preparation steps manually."
|
||||
echo "See https://nixos.org/nix/manual/#sect-macos-installation"
|
||||
echo ""
|
||||
) >&2
|
||||
|
||||
@@ -36,9 +36,7 @@ tarball="$tmpDir/$(basename "$tmpDir/nix-@nixVersion@-$system.tar.xz")"
|
||||
|
||||
require_util curl "download the binary tarball"
|
||||
require_util tar "unpack the binary tarball"
|
||||
if [ "$(uname -s)" != "Darwin" ]; then
|
||||
require_util xz "unpack the binary tarball"
|
||||
fi
|
||||
require_util xz "unpack the binary tarball"
|
||||
|
||||
echo "downloading Nix @nixVersion@ binary tarball for $system from '$url' to '$tmpDir'..."
|
||||
curl -L "$url" -o "$tarball" || oops "failed to download '$url'"
|
||||
|
||||
@@ -7,7 +7,7 @@ with import ./release-common.nix { inherit pkgs; };
|
||||
(if useClang then clangStdenv else stdenv).mkDerivation {
|
||||
name = "nix";
|
||||
|
||||
buildInputs = buildDeps ++ propagatedDeps ++ perlDeps;
|
||||
buildInputs = buildDeps ++ propagatedDeps ++ perlDeps ++ [ pkgs.rustfmt ];
|
||||
|
||||
inherit configureFlags;
|
||||
|
||||
|
||||
@@ -200,12 +200,9 @@ static int _main(int argc, char * * argv)
|
||||
|
||||
} catch (std::exception & e) {
|
||||
auto msg = chomp(drainFD(5, false));
|
||||
logError({
|
||||
.name = "Remote build",
|
||||
.hint = hintfmt("cannot build on '%s': %s%s",
|
||||
bestMachine->storeUri, e.what(),
|
||||
(msg.empty() ? "" : ": " + msg))
|
||||
});
|
||||
printError("cannot build on '%s': %s%s",
|
||||
bestMachine->storeUri, e.what(),
|
||||
(msg.empty() ? "" : ": " + msg));
|
||||
bestMachine->enabled = false;
|
||||
continue;
|
||||
}
|
||||
@@ -244,7 +241,7 @@ connected:
|
||||
|
||||
uploadLock = -1;
|
||||
|
||||
auto drv = store->readDerivation(*drvPath);
|
||||
BasicDerivation drv(readDerivation(*store, store->realStoreDir + "/" + std::string(drvPath->to_string())));
|
||||
drv.inputSrcs = store->parseStorePathSet(inputs);
|
||||
|
||||
auto result = sshStore->buildDerivation(*drvPath, drv);
|
||||
|
||||
66
src/error-demo/error-demo.cc
Normal file
66
src/error-demo/error-demo.cc
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "error.hh"
|
||||
#include "nixexpr.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace nix;
|
||||
|
||||
// In each program where errors occur, this has to be set.
|
||||
ErrorInfo::programName = std::optional("error-demo");
|
||||
|
||||
// Error in a program; no hint and no nix code.
|
||||
printErrorInfo(
|
||||
ErrorInfo { .level = elError,
|
||||
.name = "name",
|
||||
.description = "error description",
|
||||
});
|
||||
|
||||
// Warning with name, description, and hint.
|
||||
// The hintfmt function makes all the substituted text yellow.
|
||||
printErrorInfo(
|
||||
ErrorInfo { .level = elWarning,
|
||||
.name = "name",
|
||||
.description = "error description",
|
||||
.hint = std::optional(
|
||||
hintfmt("there was a %1%", "warning")),
|
||||
});
|
||||
|
||||
|
||||
// Warning with nix file, line number, column, and the lines of
|
||||
// code where a warning occurred.
|
||||
SymbolTable testTable;
|
||||
auto problem_file = testTable.create("myfile.nix");
|
||||
|
||||
printErrorInfo(
|
||||
ErrorInfo{
|
||||
.level = elWarning,
|
||||
.name = "warning name",
|
||||
.description = "warning description",
|
||||
.hint = hintfmt("this hint has %1% templated %2%!!", "yellow", "values"),
|
||||
.nixCode = NixCode {
|
||||
.errPos = Pos(problem_file, 40, 13),
|
||||
.prevLineOfCode = std::nullopt,
|
||||
.errLineOfCode = "this is the problem line of code",
|
||||
.nextLineOfCode = std::nullopt
|
||||
}});
|
||||
|
||||
// Error with previous and next lines of code.
|
||||
printErrorInfo(
|
||||
ErrorInfo{
|
||||
.level = elError,
|
||||
.name = "error name",
|
||||
.description = "error description",
|
||||
.hint = hintfmt("this hint has %1% templated %2%!!", "yellow", "values"),
|
||||
.nixCode = NixCode {
|
||||
.errPos = Pos(problem_file, 40, 13),
|
||||
.prevLineOfCode = std::optional("previous line of code"),
|
||||
.errLineOfCode = "this is the problem line of code",
|
||||
.nextLineOfCode = std::optional("next line of code"),
|
||||
}});
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
src/error-demo/local.mk
Normal file
12
src/error-demo/local.mk
Normal file
@@ -0,0 +1,12 @@
|
||||
programs += error-demo
|
||||
|
||||
error-demo_DIR := $(d)
|
||||
|
||||
error-demo_SOURCES := \
|
||||
$(wildcard $(d)/*.cc) \
|
||||
|
||||
error-demo_CXXFLAGS += -I src/libutil -I src/libexpr
|
||||
|
||||
error-demo_LIBS = libutil libexpr
|
||||
|
||||
error-demo_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -lboost_context -lboost_thread -lboost_system
|
||||
@@ -6,11 +6,11 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
static Strings parseAttrPath(std::string_view s)
|
||||
static Strings parseAttrPath(const string & s)
|
||||
{
|
||||
Strings res;
|
||||
string cur;
|
||||
auto i = s.begin();
|
||||
string::const_iterator i = s.begin();
|
||||
while (i != s.end()) {
|
||||
if (*i == '.') {
|
||||
res.push_back(cur);
|
||||
@@ -19,7 +19,7 @@ static Strings parseAttrPath(std::string_view s)
|
||||
++i;
|
||||
while (1) {
|
||||
if (i == s.end())
|
||||
throw Error("missing closing quote in selection path '%1%'", s);
|
||||
throw Error(format("missing closing quote in selection path '%1%'") % s);
|
||||
if (*i == '"') break;
|
||||
cur.push_back(*i++);
|
||||
}
|
||||
@@ -32,15 +32,6 @@ static Strings parseAttrPath(std::string_view s)
|
||||
}
|
||||
|
||||
|
||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
|
||||
{
|
||||
std::vector<Symbol> res;
|
||||
for (auto & a : parseAttrPath(s))
|
||||
res.push_back(state.symbols.create(a));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Value & vIn)
|
||||
{
|
||||
@@ -69,11 +60,11 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||
|
||||
if (v->type != tAttrs)
|
||||
throw TypeError(
|
||||
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||
attrPath,
|
||||
showType(*v));
|
||||
format("the expression selected by the selection path '%1%' should be a set but is %2%")
|
||||
% attrPath % showType(*v));
|
||||
|
||||
if (attr.empty())
|
||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||
throw Error(format("empty attribute name in selection path '%1%'") % attrPath);
|
||||
|
||||
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
||||
if (a == v->attrs->end())
|
||||
@@ -86,9 +77,9 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||
|
||||
if (!v->isList())
|
||||
throw TypeError(
|
||||
"the expression selected by the selection path '%1%' should be a list but is %2%",
|
||||
attrPath,
|
||||
showType(*v));
|
||||
format("the expression selected by the selection path '%1%' should be a list but is %2%")
|
||||
% attrPath % showType(*v));
|
||||
|
||||
if (attrIndex >= v->listSize())
|
||||
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", attrIndex, attrPath);
|
||||
|
||||
@@ -130,7 +121,7 @@ Pos findDerivationFilename(EvalState & state, Value & v, std::string what)
|
||||
|
||||
Symbol file = state.symbols.create(filename);
|
||||
|
||||
return { foFile, file, lineno, 0 };
|
||||
return { file, lineno, 0 };
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,4 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||
/* Heuristic to find the filename and lineno or a nix value. */
|
||||
Pos findDerivationFilename(EvalState & state, Value & v, std::string what);
|
||||
|
||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
||||
|
||||
}
|
||||
|
||||
@@ -76,11 +76,7 @@ public:
|
||||
{
|
||||
auto a = get(name);
|
||||
if (!a)
|
||||
throw Error({
|
||||
.hint = hintfmt("attribute '%s' missing", name),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
throw Error("attribute '%s' missing, at %s", name, pos);
|
||||
return *a;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,26 +7,20 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw EvalError({
|
||||
.hint = hintfmt(s),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
||||
{
|
||||
throw TypeError(s, showType(v));
|
||||
throw TypeError(format(s) % showType(v));
|
||||
}
|
||||
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v, const Pos & pos))
|
||||
{
|
||||
throw TypeError({
|
||||
.hint = hintfmt(s, showType(v)),
|
||||
.errPos = pos
|
||||
});
|
||||
throw TypeError(format(s) % showType(v) % pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +43,7 @@ void EvalState::forceValue(Value & v, const Pos & pos)
|
||||
else if (v.type == tApp)
|
||||
callFunction(*v.app.left, *v.app.right, v, noPos);
|
||||
else if (v.type == tBlackhole)
|
||||
throwEvalError(pos, "infinite recursion encountered");
|
||||
throwEvalError("infinite recursion encountered, at %1%", pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +59,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type != tAttrs)
|
||||
throwTypeError(pos, "value is %1% while a set was expected", v);
|
||||
throwTypeError("value is %1% while a set was expected, at %2%", v, pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +75,7 @@ inline void EvalState::forceList(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (!v.isList())
|
||||
throwTypeError(pos, "value is %1% while a list was expected", v);
|
||||
throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
|
||||
}
|
||||
|
||||
/* Note: Various places expect the allocated memory to be zeroed. */
|
||||
|
||||
@@ -161,12 +161,12 @@ const Value *getPrimOp(const Value &v) {
|
||||
}
|
||||
|
||||
|
||||
string showType(ValueType type)
|
||||
string showType(const Value & v)
|
||||
{
|
||||
switch (type) {
|
||||
switch (v.type) {
|
||||
case tInt: return "an integer";
|
||||
case tBool: return "a Boolean";
|
||||
case tString: return "a string";
|
||||
case tBool: return "a boolean";
|
||||
case tString: return v.string.context ? "a string with context" : "a string";
|
||||
case tPath: return "a path";
|
||||
case tNull: return "null";
|
||||
case tAttrs: return "a set";
|
||||
@@ -175,27 +175,14 @@ string showType(ValueType type)
|
||||
case tApp: return "a function application";
|
||||
case tLambda: return "a function";
|
||||
case tBlackhole: return "a black hole";
|
||||
case tPrimOp: return "a built-in function";
|
||||
case tPrimOpApp: return "a partially applied built-in function";
|
||||
case tExternal: return "an external value";
|
||||
case tFloat: return "a float";
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
string showType(const Value & v)
|
||||
{
|
||||
switch (v.type) {
|
||||
case tString: return v.string.context ? "a string with context" : "a string";
|
||||
case tPrimOp:
|
||||
return fmt("the built-in function '%s'", string(v.primOp->name));
|
||||
case tPrimOpApp:
|
||||
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
|
||||
case tExternal: return v.external->showType();
|
||||
default:
|
||||
return showType(v.type);
|
||||
case tFloat: return "a float";
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
@@ -336,7 +323,6 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||
, sOutputHash(symbols.create("outputHash"))
|
||||
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
|
||||
, sOutputHashMode(symbols.create("outputHashMode"))
|
||||
, sRecurseForDerivations(symbols.create("recurseForDerivations"))
|
||||
, repair(NoRepair)
|
||||
, store(store)
|
||||
, baseEnv(allocEnv(128))
|
||||
@@ -366,7 +352,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||
|
||||
if (store->isInStore(r.second)) {
|
||||
StorePathSet closure;
|
||||
store->computeFSClosure(store->toStorePath(r.second).first, closure);
|
||||
store->computeFSClosure(store->parseStorePath(store->toStorePath(r.second)), closure);
|
||||
for (auto & path : closure)
|
||||
allowedPaths->insert(store->printStorePath(path));
|
||||
} else
|
||||
@@ -485,21 +471,14 @@ Value * EvalState::addConstant(const string & name, Value & v)
|
||||
Value * EvalState::addPrimOp(const string & name,
|
||||
size_t arity, PrimOpFun primOp)
|
||||
{
|
||||
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
|
||||
the primop to a dummy value. */
|
||||
if (arity == 0) {
|
||||
auto vPrimOp = allocValue();
|
||||
vPrimOp->type = tPrimOp;
|
||||
vPrimOp->primOp = new PrimOp(primOp, 1, sym);
|
||||
Value v;
|
||||
mkApp(v, *vPrimOp, *vPrimOp);
|
||||
primOp(*this, noPos, nullptr, v);
|
||||
return addConstant(name, v);
|
||||
}
|
||||
|
||||
Value * v = allocValue();
|
||||
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
|
||||
Symbol sym = symbols.create(name2);
|
||||
v->type = tPrimOp;
|
||||
v->primOp = new PrimOp(primOp, arity, sym);
|
||||
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
||||
@@ -522,84 +501,67 @@ Value & EvalState::getBuiltin(const string & name)
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
||||
{
|
||||
throw EvalError(s, s2);
|
||||
throw EvalError(format(s) % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2))
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const Pos & pos))
|
||||
{
|
||||
throw EvalError({
|
||||
.hint = hintfmt(s, s2),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format(s) % s2 % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
|
||||
{
|
||||
throw EvalError(s, s2, s3);
|
||||
throw EvalError(format(s) % s2 % s3);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3))
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3, const Pos & pos))
|
||||
{
|
||||
throw EvalError({
|
||||
.hint = hintfmt(s, s2, s3),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format(s) % s2 % s3 % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2))
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const Symbol & sym, const Pos & p1, const Pos & p2))
|
||||
{
|
||||
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
||||
throw EvalError({
|
||||
.hint = hintfmt(s, sym, p2),
|
||||
.errPos = p1
|
||||
});
|
||||
throw EvalError(format(s) % sym % p1 % p2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw TypeError({
|
||||
.hint = hintfmt(s),
|
||||
.errPos = pos
|
||||
});
|
||||
throw TypeError(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1))
|
||||
{
|
||||
throw TypeError(s, s1);
|
||||
throw TypeError(format(s) % s1);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const ExprLambda & fun, const Symbol & s2, const Pos & pos))
|
||||
{
|
||||
throw TypeError({
|
||||
.hint = hintfmt(s, fun.showNamePos(), s2),
|
||||
.errPos = pos
|
||||
});
|
||||
throw TypeError(format(s) % fun.showNamePos() % s2 % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
|
||||
LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s1, const Pos & pos))
|
||||
{
|
||||
throw AssertionError({
|
||||
.hint = hintfmt(s, s1),
|
||||
.errPos = pos
|
||||
});
|
||||
throw AssertionError(format(s) % s1 % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1))
|
||||
LocalNoInlineNoReturn(void throwUndefinedVarError(const char * s, const string & s1, const Pos & pos))
|
||||
{
|
||||
throw UndefinedVarError({
|
||||
.hint = hintfmt(s, s1),
|
||||
.errPos = pos
|
||||
});
|
||||
throw UndefinedVarError(format(s) % s1 % pos);
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorTrace(Error & e, const char * s, const string & s2))
|
||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
|
||||
{
|
||||
e.addTrace(std::nullopt, s, s2);
|
||||
e.addPrefix(format(s) % s2);
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const string & s2))
|
||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const ExprLambda & fun, const Pos & pos))
|
||||
{
|
||||
e.addTrace(pos, s, s2);
|
||||
e.addPrefix(format(s) % fun.showNamePos() % pos);
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos))
|
||||
{
|
||||
e.addPrefix(format(s) % s2 % pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -652,7 +614,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||
return j->value;
|
||||
}
|
||||
if (!env->prevWith)
|
||||
throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name);
|
||||
throwUndefinedVarError("undefined variable '%1%' at %2%", var.name, var.pos);
|
||||
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
||||
}
|
||||
}
|
||||
@@ -813,7 +775,7 @@ void EvalState::evalFile(const Path & path_, Value & v)
|
||||
try {
|
||||
eval(e, v);
|
||||
} catch (Error & e) {
|
||||
addErrorTrace(e, "while evaluating the file '%1%':", path2);
|
||||
addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -850,7 +812,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
|
||||
Value v;
|
||||
e->eval(*this, env, v);
|
||||
if (v.type != tBool)
|
||||
throwTypeError(pos, "value is %1% while a Boolean was expected", v);
|
||||
throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
|
||||
return v.boolean;
|
||||
}
|
||||
|
||||
@@ -964,7 +926,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
||||
Bindings::iterator j = v.attrs->find(nameSym);
|
||||
if (j != v.attrs->end())
|
||||
throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos);
|
||||
throwEvalError("dynamic attribute '%1%' at %2% already defined at %3%", nameSym, i.pos, *j->pos);
|
||||
|
||||
i.valueExpr->setName(nameSym);
|
||||
/* Keep sorted order so find can catch duplicates */
|
||||
@@ -1052,7 +1014,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
} else {
|
||||
state.forceAttrs(*vAttrs, pos);
|
||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
throwEvalError(pos, "attribute '%1%' missing", name);
|
||||
throwEvalError("attribute '%1%' missing, at %2%", name, pos);
|
||||
}
|
||||
vAttrs = j->value;
|
||||
pos2 = j->pos;
|
||||
@@ -1063,8 +1025,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
} catch (Error & e) {
|
||||
if (pos2 && pos2->file != state.sDerivationNix)
|
||||
addErrorTrace(e, *pos2, "while evaluating the attribute '%1%'",
|
||||
showAttrPath(state, env, attrPath));
|
||||
addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n",
|
||||
showAttrPath(state, env, attrPath), *pos2);
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -1178,7 +1140,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||
}
|
||||
|
||||
if (fun.type != tLambda)
|
||||
throwTypeError(pos, "attempt to call something which is not a function but %1%", fun);
|
||||
throwTypeError("attempt to call something which is not a function but %1%, at %2%", fun, pos);
|
||||
|
||||
ExprLambda & lambda(*fun.lambda.fun);
|
||||
|
||||
@@ -1206,8 +1168,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||
for (auto & i : lambda.formals->formals) {
|
||||
Bindings::iterator j = arg.attrs->find(i.name);
|
||||
if (j == arg.attrs->end()) {
|
||||
if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'",
|
||||
lambda, i.name);
|
||||
if (!i.def) throwTypeError("%1% called without required argument '%2%', at %3%",
|
||||
lambda, i.name, pos);
|
||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||
} else {
|
||||
attrsUsed++;
|
||||
@@ -1222,7 +1184,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||
user. */
|
||||
for (auto & i : *arg.attrs)
|
||||
if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
|
||||
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
|
||||
throwTypeError("%1% called with unexpected argument '%2%', at %3%", lambda, i.name, pos);
|
||||
abort(); // can't happen
|
||||
}
|
||||
}
|
||||
@@ -1232,15 +1194,11 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||
|
||||
/* Evaluate the body. This is conditional on showTrace, because
|
||||
catching exceptions makes this function not tail-recursive. */
|
||||
if (loggerSettings.showTrace.get())
|
||||
if (settings.showTrace)
|
||||
try {
|
||||
lambda.body->eval(*this, env2, v);
|
||||
} catch (Error & e) {
|
||||
addErrorTrace(e, lambda.pos, "while evaluating %s",
|
||||
(lambda.name.set()
|
||||
? "'" + (string) lambda.name + "'"
|
||||
: "anonymous lambdaction"));
|
||||
addErrorTrace(e, pos, "from call site%s", "");
|
||||
addErrorPrefix(e, "while evaluating %1%, called from %2%:\n", lambda, pos);
|
||||
throw;
|
||||
}
|
||||
else
|
||||
@@ -1315,7 +1273,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||
if (!state.evalBool(env, cond, pos)) {
|
||||
std::ostringstream out;
|
||||
cond->show(out);
|
||||
throwAssertionError(pos, "assertion '%1%' failed at %2%", out.str());
|
||||
throwAssertionError("assertion '%1%' failed at %2%", out.str(), pos);
|
||||
}
|
||||
body->eval(state, env, v);
|
||||
}
|
||||
@@ -1467,14 +1425,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
nf = n;
|
||||
nf += vTmp.fpoint;
|
||||
} else
|
||||
throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp));
|
||||
throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp), pos);
|
||||
} else if (firstType == tFloat) {
|
||||
if (vTmp.type == tInt) {
|
||||
nf += vTmp.integer;
|
||||
} else if (vTmp.type == tFloat) {
|
||||
nf += vTmp.fpoint;
|
||||
} else
|
||||
throwEvalError(pos, "cannot add %1% to a float", showType(vTmp));
|
||||
throwEvalError("cannot add %1% to a float, at %2%", showType(vTmp), pos);
|
||||
} else
|
||||
s << state.coerceToString(pos, vTmp, context, false, firstType == tString);
|
||||
}
|
||||
@@ -1485,7 +1443,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
mkFloat(v, nf);
|
||||
else if (firstType == tPath) {
|
||||
if (!context.empty())
|
||||
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
||||
throwEvalError("a string that refers to a store path cannot be appended to a path, at %1%", pos);
|
||||
auto path = canonPath(s.str());
|
||||
mkPath(v, path.c_str());
|
||||
} else
|
||||
@@ -1515,7 +1473,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||
try {
|
||||
recurse(*i.value);
|
||||
} catch (Error & e) {
|
||||
addErrorTrace(e, *i.pos, "while evaluating the attribute '%1%'", i.name);
|
||||
addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n", i.name, *i.pos);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -1534,7 +1492,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type != tInt)
|
||||
throwTypeError(pos, "value is %1% while an integer was expected", v);
|
||||
throwTypeError("value is %1% while an integer was expected, at %2%", v, pos);
|
||||
return v.integer;
|
||||
}
|
||||
|
||||
@@ -1545,7 +1503,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
|
||||
if (v.type == tInt)
|
||||
return v.integer;
|
||||
else if (v.type != tFloat)
|
||||
throwTypeError(pos, "value is %1% while a float was expected", v);
|
||||
throwTypeError("value is %1% while a float was expected, at %2%", v, pos);
|
||||
return v.fpoint;
|
||||
}
|
||||
|
||||
@@ -1554,7 +1512,7 @@ bool EvalState::forceBool(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type != tBool)
|
||||
throwTypeError(pos, "value is %1% while a Boolean was expected", v);
|
||||
throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
|
||||
return v.boolean;
|
||||
}
|
||||
|
||||
@@ -1569,7 +1527,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp && !isFunctor(v))
|
||||
throwTypeError(pos, "value is %1% while a function was expected", v);
|
||||
throwTypeError("value is %1% while a function was expected, at %2%", v, pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -1578,7 +1536,7 @@ string EvalState::forceString(Value & v, const Pos & pos)
|
||||
forceValue(v, pos);
|
||||
if (v.type != tString) {
|
||||
if (pos)
|
||||
throwTypeError(pos, "value is %1% while a string was expected", v);
|
||||
throwTypeError("value is %1% while a string was expected, at %2%", v, pos);
|
||||
else
|
||||
throwTypeError("value is %1% while a string was expected", v);
|
||||
}
|
||||
@@ -1607,8 +1565,8 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
|
||||
string s = forceString(v, pos);
|
||||
if (v.string.context) {
|
||||
if (pos)
|
||||
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||
v.string.s, v.string.context[0]);
|
||||
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%'), at %3%",
|
||||
v.string.s, v.string.context[0], pos);
|
||||
else
|
||||
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||
v.string.s, v.string.context[0]);
|
||||
@@ -1664,7 +1622,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||
return *maybeString;
|
||||
}
|
||||
auto i = v.attrs->find(sOutPath);
|
||||
if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
|
||||
if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string, at %1%", pos);
|
||||
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
||||
}
|
||||
|
||||
@@ -1695,7 +1653,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||
}
|
||||
}
|
||||
|
||||
throwTypeError(pos, "cannot coerce %1% to a string", v);
|
||||
throwTypeError("cannot coerce %1% to a string, at %2%", v, pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -1711,7 +1669,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||
else {
|
||||
auto p = settings.readOnlyMode
|
||||
? store->computeStorePathForPath(std::string(baseNameOf(path)), checkSourcePath(path)).first
|
||||
: store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair);
|
||||
: store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), true, htSHA256, defaultPathFilter, repair);
|
||||
dstPath = store->printStorePath(p);
|
||||
srcToStore.insert_or_assign(path, std::move(p));
|
||||
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, dstPath);
|
||||
@@ -1726,7 +1684,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
||||
{
|
||||
string path = coerceToString(pos, v, context, false, false);
|
||||
if (path == "" || path[0] != '/')
|
||||
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
|
||||
throwEvalError("string '%1%' doesn't represent an absolute path, at %2%", path, pos);
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -1933,10 +1891,8 @@ void EvalState::printStats()
|
||||
|
||||
string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
|
||||
{
|
||||
throw TypeError({
|
||||
.hint = hintfmt("cannot coerce %1% to a string", showType()),
|
||||
.errPos = pos
|
||||
});
|
||||
throw TypeError(format("cannot coerce %1% to a string, at %2%") %
|
||||
showType() % pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace nix {
|
||||
|
||||
class Store;
|
||||
class EvalState;
|
||||
class StorePath;
|
||||
struct StorePath;
|
||||
enum RepairFlag : bool;
|
||||
|
||||
|
||||
@@ -74,8 +74,7 @@ public:
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||
sFile, sLine, sColumn, sFunctor, sToString,
|
||||
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
|
||||
sOutputHash, sOutputHashAlgo, sOutputHashMode,
|
||||
sRecurseForDerivations;
|
||||
sOutputHash, sOutputHashAlgo, sOutputHashMode;
|
||||
Symbol sDerivationNix;
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
@@ -250,7 +249,7 @@ private:
|
||||
friend struct ExprAttrs;
|
||||
friend struct ExprLet;
|
||||
|
||||
Expr * parse(const char * text, FileOrigin origin, const Path & path,
|
||||
Expr * parse(const char * text, const Path & path,
|
||||
const Path & basePath, StaticEnv & staticEnv);
|
||||
|
||||
public:
|
||||
@@ -325,7 +324,6 @@ private:
|
||||
|
||||
|
||||
/* Return a string representing the type of the value `v'. */
|
||||
string showType(ValueType type);
|
||||
string showType(const Value & v);
|
||||
|
||||
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "get-drvs.hh"
|
||||
#include "util.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "store-api.hh"
|
||||
#include "derivations.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
@@ -348,7 +348,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
if (i->value->type == tAttrs) {
|
||||
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
||||
Bindings::iterator j = i->value->attrs->find(state.symbols.create("recurseForDerivations"));
|
||||
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos))
|
||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
|
||||
@@ -127,14 +127,14 @@ or { return OR_KW; }
|
||||
try {
|
||||
yylval->n = boost::lexical_cast<int64_t>(yytext);
|
||||
} catch (const boost::bad_lexical_cast &) {
|
||||
throw ParseError("invalid integer '%1%'", yytext);
|
||||
throw ParseError(format("invalid integer '%1%'") % yytext);
|
||||
}
|
||||
return INT;
|
||||
}
|
||||
{FLOAT} { errno = 0;
|
||||
yylval->nf = strtod(yytext, 0);
|
||||
if (errno != 0)
|
||||
throw ParseError("invalid float '%1%'", yytext);
|
||||
throw ParseError(format("invalid float '%1%'") % yytext);
|
||||
return FLOAT;
|
||||
}
|
||||
|
||||
@@ -219,3 +219,4 @@ or { return OR_KW; }
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ libexpr_SOURCES := $(wildcard $(d)/*.cc) $(wildcard $(d)/primops/*.cc) $(d)/lexe
|
||||
|
||||
libexpr_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libmain -I src/libexpr
|
||||
|
||||
libexpr_LIBS = libutil libstore libfetchers
|
||||
libexpr_LIBS = libutil libstore libfetchers libnixrust
|
||||
|
||||
libexpr_LDFLAGS =
|
||||
ifneq ($(OS), FreeBSD)
|
||||
|
||||
@@ -197,22 +197,7 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
|
||||
if (!pos)
|
||||
str << "undefined position";
|
||||
else
|
||||
{
|
||||
auto f = format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%");
|
||||
switch (pos.origin) {
|
||||
case foFile:
|
||||
f % (string) pos.file;
|
||||
break;
|
||||
case foStdin:
|
||||
case foString:
|
||||
f % "(string)";
|
||||
break;
|
||||
default:
|
||||
throw Error("unhandled Pos origin!");
|
||||
}
|
||||
str << (f % pos.line % pos.column).str();
|
||||
}
|
||||
|
||||
str << (format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%") % (string) pos.file % pos.line % pos.column).str();
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -282,11 +267,8 @@ void ExprVar::bindVars(const StaticEnv & env)
|
||||
/* Otherwise, the variable must be obtained from the nearest
|
||||
enclosing `with'. If there is no `with', then we can issue an
|
||||
"undefined variable" error now. */
|
||||
if (withLevel == -1)
|
||||
throw UndefinedVarError({
|
||||
.hint = hintfmt("undefined variable '%1%'", name),
|
||||
.errPos = pos
|
||||
});
|
||||
if (withLevel == -1) throw UndefinedVarError(format("undefined variable '%1%' at %2%") % name % pos);
|
||||
|
||||
fromWith = true;
|
||||
this->level = withLevel;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "value.hh"
|
||||
#include "symbol-table.hh"
|
||||
#include "error.hh"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -24,12 +23,11 @@ MakeError(RestrictedPathError, Error);
|
||||
|
||||
struct Pos
|
||||
{
|
||||
FileOrigin origin;
|
||||
Symbol file;
|
||||
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) { };
|
||||
Pos() : line(0), column(0) { };
|
||||
Pos(const Symbol & file, unsigned int line, unsigned int column)
|
||||
: file(file), line(line), column(column) { };
|
||||
operator bool() const
|
||||
{
|
||||
return line != 0;
|
||||
@@ -237,10 +235,8 @@ struct ExprLambda : Expr
|
||||
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
|
||||
{
|
||||
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
||||
throw ParseError({
|
||||
.hint = hintfmt("duplicate formal function argument '%1%'", arg),
|
||||
.errPos = pos
|
||||
});
|
||||
throw ParseError(format("duplicate formal function argument '%1%' at %2%")
|
||||
% arg % pos);
|
||||
};
|
||||
void setName(Symbol & name);
|
||||
string showNamePos() const;
|
||||
|
||||
@@ -30,9 +30,8 @@ namespace nix {
|
||||
SymbolTable & symbols;
|
||||
Expr * result;
|
||||
Path basePath;
|
||||
Symbol file;
|
||||
FileOrigin origin;
|
||||
ErrorInfo error;
|
||||
Symbol path;
|
||||
string error;
|
||||
Symbol sLetBody;
|
||||
ParseData(EvalState & state)
|
||||
: state(state)
|
||||
@@ -65,19 +64,15 @@ namespace nix {
|
||||
|
||||
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
|
||||
{
|
||||
throw ParseError({
|
||||
.hint = hintfmt("attribute '%1%' already defined at %2%",
|
||||
showAttrPath(attrPath), prevPos),
|
||||
.errPos = pos
|
||||
});
|
||||
throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
|
||||
% showAttrPath(attrPath) % pos % prevPos);
|
||||
}
|
||||
|
||||
|
||||
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
||||
{
|
||||
throw ParseError({
|
||||
.hint = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos),
|
||||
.errPos = pos
|
||||
});
|
||||
throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
|
||||
% attr % pos % prevPos);
|
||||
}
|
||||
|
||||
|
||||
@@ -145,11 +140,8 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||
{
|
||||
if (!formals->argNames.insert(formal.name).second)
|
||||
throw ParseError({
|
||||
.hint = hintfmt("duplicate formal function argument '%1%'",
|
||||
formal.name),
|
||||
.errPos = pos
|
||||
});
|
||||
throw ParseError(format("duplicate formal function argument '%1%' at %2%")
|
||||
% formal.name % pos);
|
||||
formals->formals.push_front(formal);
|
||||
}
|
||||
|
||||
@@ -246,7 +238,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
|
||||
|
||||
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||
{
|
||||
return Pos(data->origin, data->file, loc.first_line, loc.first_column);
|
||||
return Pos(data->path, loc.first_line, loc.first_column);
|
||||
}
|
||||
|
||||
#define CUR_POS makeCurPos(*yylocp, data)
|
||||
@@ -257,10 +249,8 @@ static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||
|
||||
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
|
||||
{
|
||||
data->error = {
|
||||
.hint = hintfmt(error),
|
||||
.errPos = makeCurPos(*loc, data)
|
||||
};
|
||||
data->error = (format("%1%, at %2%")
|
||||
% error % makeCurPos(*loc, data)).str();
|
||||
}
|
||||
|
||||
|
||||
@@ -337,10 +327,8 @@ expr_function
|
||||
{ $$ = new ExprWith(CUR_POS, $2, $4); }
|
||||
| LET binds IN expr_function
|
||||
{ if (!$2->dynamicAttrs.empty())
|
||||
throw ParseError({
|
||||
.hint = hintfmt("dynamic attributes not allowed in let"),
|
||||
.errPos = CUR_POS
|
||||
});
|
||||
throw ParseError(format("dynamic attributes not allowed in let at %1%")
|
||||
% CUR_POS);
|
||||
$$ = new ExprLet($2, $4);
|
||||
}
|
||||
| expr_if
|
||||
@@ -417,10 +405,7 @@ expr_simple
|
||||
| URI {
|
||||
static bool noURLLiterals = settings.isExperimentalFeatureEnabled("no-url-literals");
|
||||
if (noURLLiterals)
|
||||
throw ParseError({
|
||||
.hint = hintfmt("URL literals are disabled"),
|
||||
.errPos = CUR_POS
|
||||
});
|
||||
throw ParseError("URL literals are disabled, at %s", CUR_POS);
|
||||
$$ = new ExprString(data->symbols.create($1));
|
||||
}
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
@@ -490,10 +475,8 @@ attrs
|
||||
$$->push_back(AttrName(str->s));
|
||||
delete str;
|
||||
} else
|
||||
throw ParseError({
|
||||
.hint = hintfmt("dynamic attributes not allowed in inherit"),
|
||||
.errPos = makeCurPos(@2, data)
|
||||
});
|
||||
throw ParseError(format("dynamic attributes not allowed in inherit at %1%")
|
||||
% makeCurPos(@2, data));
|
||||
}
|
||||
| { $$ = new AttrPath; }
|
||||
;
|
||||
@@ -569,24 +552,13 @@ formal
|
||||
namespace nix {
|
||||
|
||||
|
||||
Expr * EvalState::parse(const char * text, FileOrigin origin,
|
||||
Expr * EvalState::parse(const char * text,
|
||||
const Path & path, const Path & basePath, StaticEnv & staticEnv)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
ParseData data(*this);
|
||||
data.origin = origin;
|
||||
switch (origin) {
|
||||
case foFile:
|
||||
data.file = data.symbols.create(path);
|
||||
break;
|
||||
case foStdin:
|
||||
case foString:
|
||||
data.file = data.symbols.create(text);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
data.basePath = basePath;
|
||||
data.path = data.symbols.create(path);
|
||||
|
||||
yylex_init(&scanner);
|
||||
yy_scan_string(text, scanner);
|
||||
@@ -636,13 +608,13 @@ Expr * EvalState::parseExprFromFile(const Path & path)
|
||||
|
||||
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
|
||||
{
|
||||
return parse(readFile(path).c_str(), foFile, path, dirOf(path), staticEnv);
|
||||
return parse(readFile(path).c_str(), path, dirOf(path), staticEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv)
|
||||
{
|
||||
return parse(s.data(), foString, "", basePath, staticEnv);
|
||||
return parse(s.data(), "(string)", basePath, staticEnv);
|
||||
}
|
||||
|
||||
|
||||
@@ -655,7 +627,7 @@ Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath)
|
||||
Expr * EvalState::parseStdin()
|
||||
{
|
||||
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
|
||||
return parse(drainFD(0).data(), foStdin, "", absPath("."), staticBaseEnv);
|
||||
return parseExprFromString(drainFD(0), absPath("."));
|
||||
}
|
||||
|
||||
|
||||
@@ -699,13 +671,11 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
|
||||
Path res = r.second + suffix;
|
||||
if (pathExists(res)) return canonPath(res);
|
||||
}
|
||||
throw ThrownError({
|
||||
.hint = hintfmt(evalSettings.pureEval
|
||||
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
||||
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
||||
path),
|
||||
.errPos = pos
|
||||
});
|
||||
format f = format(
|
||||
"file '%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)"
|
||||
+ string(pos ? ", at %2%" : ""));
|
||||
f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
|
||||
throw ThrownError(f % path % pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -721,10 +691,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
|
||||
res = { true, store->toRealPath(fetchers::downloadTarball(
|
||||
store, resolveUri(elem.second), "source", false).storePath) };
|
||||
} catch (FileTransferError & e) {
|
||||
logWarning({
|
||||
.name = "Entry download",
|
||||
.hint = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second)
|
||||
});
|
||||
printError(format("warning: Nix search path entry '%1%' cannot be downloaded, ignoring") % elem.second);
|
||||
res = { false, "" };
|
||||
}
|
||||
} else {
|
||||
@@ -732,10 +699,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
|
||||
if (pathExists(path))
|
||||
res = { true, path };
|
||||
else {
|
||||
logWarning({
|
||||
.name = "Entry not found",
|
||||
.hint = hintfmt("warning: Nix search path entry '%1%' does not exist, ignoring", elem.second)
|
||||
});
|
||||
printError(format("warning: Nix search path entry '%1%' does not exist, ignoring") % elem.second);
|
||||
res = { false, "" };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,20 +50,20 @@ void EvalState::realiseContext(const PathSet & context)
|
||||
std::vector<StorePathWithOutputs> drvs;
|
||||
|
||||
for (auto & i : context) {
|
||||
auto [ctxS, outputName] = decodeContext(i);
|
||||
auto ctx = store->parseStorePath(ctxS);
|
||||
std::pair<string, string> decoded = decodeContext(i);
|
||||
auto ctx = store->parseStorePath(decoded.first);
|
||||
if (!store->isValidPath(ctx))
|
||||
throw InvalidPathError(store->printStorePath(ctx));
|
||||
if (!outputName.empty() && ctx.isDerivation()) {
|
||||
drvs.push_back(StorePathWithOutputs{ctx, {outputName}});
|
||||
if (!decoded.second.empty() && ctx.isDerivation()) {
|
||||
drvs.push_back(StorePathWithOutputs{ctx.clone(), {decoded.second}});
|
||||
|
||||
/* Add the output of this derivation to the allowed
|
||||
paths. */
|
||||
if (allowedPaths) {
|
||||
auto drv = store->derivationFromPath(ctx);
|
||||
DerivationOutputs::iterator i = drv.outputs.find(outputName);
|
||||
auto drv = store->derivationFromPath(store->parseStorePath(decoded.first));
|
||||
DerivationOutputs::iterator i = drv.outputs.find(decoded.second);
|
||||
if (i == drv.outputs.end())
|
||||
throw Error("derivation '%s' does not have an output named '%s'", ctxS, outputName);
|
||||
throw Error("derivation '%s' does not have an output named '%s'", decoded.first, decoded.second);
|
||||
allowedPaths->insert(store->printStorePath(i->second.path));
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,6 @@ void EvalState::realiseContext(const PathSet & context)
|
||||
StorePathSet willBuild, willSubstitute, unknown;
|
||||
unsigned long long downloadSize, narSize;
|
||||
store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
|
||||
store->buildPaths(drvs);
|
||||
}
|
||||
|
||||
@@ -94,10 +93,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
|
||||
@@ -173,12 +170,8 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt(
|
||||
"cannot import '%1%', since path '%2%' is not valid",
|
||||
path, e.path),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
path = state.checkSourcePath(path);
|
||||
@@ -187,17 +180,17 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
|
||||
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle)
|
||||
throw EvalError("could not open '%1%': %2%", path, dlerror());
|
||||
throw EvalError(format("could not open '%1%': %2%") % path % dlerror());
|
||||
|
||||
dlerror();
|
||||
ValueInitializer func = (ValueInitializer) dlsym(handle, sym.c_str());
|
||||
if(!func) {
|
||||
char *message = dlerror();
|
||||
if (message)
|
||||
throw EvalError("could not load symbol '%1%' from '%2%': %3%", sym, path, message);
|
||||
throw EvalError(format("could not load symbol '%1%' from '%2%': %3%") % sym % path % message);
|
||||
else
|
||||
throw EvalError("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected",
|
||||
sym, path);
|
||||
throw EvalError(format("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected")
|
||||
% sym % path);
|
||||
}
|
||||
|
||||
(func)(state, v);
|
||||
@@ -213,10 +206,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
auto elems = args[0]->listElems();
|
||||
auto count = args[0]->listSize();
|
||||
if (count == 0) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("at least one argument to 'exec' required"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("at least one argument to 'exec' required, at %1%") % pos);
|
||||
}
|
||||
PathSet context;
|
||||
auto program = state.coerceToString(pos, *elems[0], context, false, false);
|
||||
@@ -227,11 +217,8 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
|
||||
program, e.path),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("cannot execute '%1%', since path '%2%' is not valid, at %3%")
|
||||
% program % e.path % pos);
|
||||
}
|
||||
|
||||
auto output = runProgram(program, true, commandArgs);
|
||||
@@ -239,13 +226,13 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
try {
|
||||
parsed = state.parseExprFromString(output, pos.file);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, "While parsing the output from '%1%'", program);
|
||||
e.addPrefix(format("While parsing the output from '%1%', at %2%\n") % program % pos);
|
||||
throw;
|
||||
}
|
||||
try {
|
||||
state.eval(parsed, v);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(pos, "While evaluating the output from '%1%'", program);
|
||||
e.addPrefix(format("While evaluating the output from '%1%', at %2%\n") % program % pos);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -351,7 +338,7 @@ struct CompareValues
|
||||
if (v1->type == tInt && v2->type == tFloat)
|
||||
return v1->integer < v2->fpoint;
|
||||
if (v1->type != v2->type)
|
||||
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
|
||||
throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
|
||||
switch (v1->type) {
|
||||
case tInt:
|
||||
return v1->integer < v2->integer;
|
||||
@@ -362,7 +349,7 @@ struct CompareValues
|
||||
case tPath:
|
||||
return strcmp(v1->path, v2->path) < 0;
|
||||
default:
|
||||
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
|
||||
throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -383,10 +370,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||
Bindings::iterator startSet =
|
||||
args[0]->attrs->find(state.symbols.create("startSet"));
|
||||
if (startSet == args[0]->attrs->end())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("attribute 'startSet' required"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("attribute 'startSet' required, at %1%") % pos);
|
||||
state.forceList(*startSet->value, pos);
|
||||
|
||||
ValueList workSet;
|
||||
@@ -397,10 +381,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||
Bindings::iterator op =
|
||||
args[0]->attrs->find(state.symbols.create("operator"));
|
||||
if (op == args[0]->attrs->end())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("attribute 'operator' required"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("attribute 'operator' required, at %1%") % pos);
|
||||
state.forceValue(*op->value, pos);
|
||||
|
||||
/* Construct the closure by applying the operator to element of
|
||||
@@ -419,10 +400,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||
Bindings::iterator key =
|
||||
e->attrs->find(state.symbols.create("key"));
|
||||
if (key == e->attrs->end())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("attribute 'key' required"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("attribute 'key' required, at %1%") % pos);
|
||||
state.forceValue(*key->value, pos);
|
||||
|
||||
if (!doneKeys.insert(key->value).second) continue;
|
||||
@@ -452,7 +430,7 @@ static void prim_abort(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
{
|
||||
PathSet context;
|
||||
string s = state.coerceToString(pos, *args[0], context);
|
||||
throw Abort("evaluation aborted with the following error message: '%1%'", s);
|
||||
throw Abort(format("evaluation aborted with the following error message: '%1%'") % s);
|
||||
}
|
||||
|
||||
|
||||
@@ -471,7 +449,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a
|
||||
v = *args[1];
|
||||
} catch (Error & e) {
|
||||
PathSet context;
|
||||
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context));
|
||||
e.addPrefix(format("%1%\n") % state.coerceToString(pos, *args[0], context));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -527,9 +505,9 @@ static void prim_trace(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
if (args[0]->type == tString)
|
||||
printError("trace: %1%", args[0]->string.s);
|
||||
printError(format("trace: %1%") % args[0]->string.s);
|
||||
else
|
||||
printError("trace: %1%", *args[0]);
|
||||
printError(format("trace: %1%") % *args[0]);
|
||||
state.forceValue(*args[1], pos);
|
||||
v = *args[1];
|
||||
}
|
||||
@@ -554,16 +532,13 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
/* Figure out the name first (for stack backtraces). */
|
||||
Bindings::iterator attr = args[0]->attrs->find(state.sName);
|
||||
if (attr == args[0]->attrs->end())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("required attribute 'name' missing"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("required attribute 'name' missing, at %1%") % pos);
|
||||
string drvName;
|
||||
Pos & posDrvName(*attr->pos);
|
||||
try {
|
||||
drvName = state.forceStringNoCtx(*attr->value, pos);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(posDrvName, "while evaluating the derivation attribute 'name'");
|
||||
e.addPrefix(format("while evaluating the derivation attribute 'name' at %1%:\n") % posDrvName);
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -587,7 +562,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
|
||||
std::optional<std::string> outputHash;
|
||||
std::string outputHashAlgo;
|
||||
auto ingestionMethod = FileIngestionMethod::Flat;
|
||||
bool outputHashRecursive = false;
|
||||
|
||||
StringSet outputs;
|
||||
outputs.insert("out");
|
||||
@@ -598,40 +573,27 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
vomit("processing attribute '%1%'", key);
|
||||
|
||||
auto handleHashMode = [&](const std::string & s) {
|
||||
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
|
||||
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
|
||||
else
|
||||
throw EvalError({
|
||||
.hint = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
|
||||
.errPos = posDrvName
|
||||
});
|
||||
if (s == "recursive") outputHashRecursive = true;
|
||||
else if (s == "flat") outputHashRecursive = false;
|
||||
else throw EvalError("invalid value '%s' for 'outputHashMode' attribute, at %s", s, posDrvName);
|
||||
};
|
||||
|
||||
auto handleOutputs = [&](const Strings & ss) {
|
||||
outputs.clear();
|
||||
for (auto & j : ss) {
|
||||
if (outputs.find(j) != outputs.end())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("duplicate derivation output '%1%'", j),
|
||||
.errPos = posDrvName
|
||||
});
|
||||
throw EvalError(format("duplicate derivation output '%1%', at %2%") % j % posDrvName);
|
||||
/* !!! Check whether j is a valid attribute
|
||||
name. */
|
||||
/* Derivations cannot be named ‘drv’, because
|
||||
then we'd have an attribute ‘drvPath’ in
|
||||
the resulting set. */
|
||||
if (j == "drv")
|
||||
throw EvalError({
|
||||
.hint = hintfmt("invalid derivation output name 'drv'" ),
|
||||
.errPos = posDrvName
|
||||
});
|
||||
throw EvalError(format("invalid derivation output name 'drv', at %1%") % posDrvName);
|
||||
outputs.insert(j);
|
||||
}
|
||||
if (outputs.empty())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("derivation cannot have an empty set of outputs"),
|
||||
.errPos = posDrvName
|
||||
});
|
||||
throw EvalError(format("derivation cannot have an empty set of outputs, at %1%") % posDrvName);
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -696,9 +658,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
}
|
||||
|
||||
} catch (Error & e) {
|
||||
e.addTrace(posDrvName,
|
||||
"while evaluating the attribute '%1%' of the derivation '%2%'",
|
||||
key, drvName);
|
||||
e.addPrefix(format("while evaluating the attribute '%1%' of the derivation '%2%' at %3%:\n")
|
||||
% key % drvName % posDrvName);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -725,9 +686,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
StorePathSet refs;
|
||||
state.store->computeFSClosure(state.store->parseStorePath(std::string_view(path).substr(1)), refs);
|
||||
for (auto & j : refs) {
|
||||
drv.inputSrcs.insert(j);
|
||||
drv.inputSrcs.insert(j.clone());
|
||||
if (j.isDerivation())
|
||||
drv.inputDrvs[j] = state.store->readDerivation(j).outputNames();
|
||||
drv.inputDrvs[j.clone()] = state.store->queryDerivationOutputNames(j);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,44 +705,27 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
|
||||
/* Do we have all required attributes? */
|
||||
if (drv.builder == "")
|
||||
throw EvalError({
|
||||
.hint = hintfmt("required attribute 'builder' missing"),
|
||||
.errPos = posDrvName
|
||||
});
|
||||
|
||||
throw EvalError(format("required attribute 'builder' missing, at %1%") % posDrvName);
|
||||
if (drv.platform == "")
|
||||
throw EvalError({
|
||||
.hint = hintfmt("required attribute 'system' missing"),
|
||||
.errPos = posDrvName
|
||||
});
|
||||
throw EvalError(format("required attribute 'system' missing, at %1%") % posDrvName);
|
||||
|
||||
/* Check whether the derivation name is valid. */
|
||||
if (isDerivation(drvName))
|
||||
throw EvalError({
|
||||
.hint = hintfmt("derivation names are not allowed to end in '%s'", drvExtension),
|
||||
.errPos = posDrvName
|
||||
});
|
||||
throw EvalError("derivation names are not allowed to end in '%s', at %s", drvExtension, posDrvName);
|
||||
|
||||
if (outputHash) {
|
||||
/* Handle fixed-output derivations. */
|
||||
if (outputs.size() != 1 || *(outputs.begin()) != "out")
|
||||
throw Error({
|
||||
.hint = hintfmt("multiple outputs are not supported in fixed-output derivations"),
|
||||
.errPos = posDrvName
|
||||
});
|
||||
throw Error(format("multiple outputs are not supported in fixed-output derivations, at %1%") % posDrvName);
|
||||
|
||||
std::optional<HashType> ht = parseHashTypeOpt(outputHashAlgo);
|
||||
Hash h = newHashAllowEmpty(*outputHash, ht);
|
||||
HashType ht = outputHashAlgo.empty() ? htUnknown : parseHashType(outputHashAlgo);
|
||||
Hash h(*outputHash, ht);
|
||||
|
||||
auto outPath = state.store->makeFixedOutputPath(ingestionMethod, h, drvName);
|
||||
auto outPath = state.store->makeFixedOutputPath(outputHashRecursive, h, drvName);
|
||||
if (!jsonObject) drv.env["out"] = state.store->printStorePath(outPath);
|
||||
drv.outputs.insert_or_assign("out", DerivationOutput {
|
||||
.path = std::move(outPath),
|
||||
.hash = FixedOutputHash {
|
||||
.method = ingestionMethod,
|
||||
.hash = std::move(h),
|
||||
},
|
||||
});
|
||||
drv.outputs.insert_or_assign("out", DerivationOutput(std::move(outPath),
|
||||
(outputHashRecursive ? "r:" : "") + printHashType(h.type),
|
||||
h.to_string(Base16, false)));
|
||||
}
|
||||
|
||||
else {
|
||||
@@ -794,10 +738,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
for (auto & i : outputs) {
|
||||
if (!jsonObject) drv.env[i] = "";
|
||||
drv.outputs.insert_or_assign(i,
|
||||
DerivationOutput {
|
||||
.path = StorePath::dummy,
|
||||
.hash = std::optional<FixedOutputHash> {},
|
||||
});
|
||||
DerivationOutput(StorePath::dummy.clone(), "", ""));
|
||||
}
|
||||
|
||||
Hash h = hashDerivationModulo(*state.store, Derivation(drv), true);
|
||||
@@ -806,10 +747,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
auto outPath = state.store->makeOutputPath(i, h, drvName);
|
||||
if (!jsonObject) drv.env[i] = state.store->printStorePath(outPath);
|
||||
drv.outputs.insert_or_assign(i,
|
||||
DerivationOutput {
|
||||
.path = std::move(outPath),
|
||||
.hash = std::optional<FixedOutputHash>(),
|
||||
});
|
||||
DerivationOutput(std::move(outPath), "", ""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,7 +760,7 @@ 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. */
|
||||
drvHashes.insert_or_assign(drvPath,
|
||||
drvHashes.insert_or_assign(drvPath.clone(),
|
||||
hashDerivationModulo(*state.store, Derivation(drv), false));
|
||||
|
||||
state.mkAttrs(v, 1 + drv.outputs.size());
|
||||
@@ -879,14 +817,11 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
|
||||
e.g. nix-push does the right thing. */
|
||||
if (!state.store->isStorePath(path)) path = canonPath(path, true);
|
||||
if (!state.store->isInStore(path))
|
||||
throw EvalError({
|
||||
.hint = hintfmt("path '%1%' is not in the Nix store", path),
|
||||
.errPos = pos
|
||||
});
|
||||
auto path2 = state.store->toStorePath(path).first;
|
||||
throw EvalError(format("path '%1%' is not in the Nix store, at %2%") % path % pos);
|
||||
Path path2 = state.store->toStorePath(path);
|
||||
if (!settings.readOnlyMode)
|
||||
state.store->ensurePath(path2);
|
||||
context.insert(state.store->printStorePath(path2));
|
||||
state.store->ensurePath(state.store->parseStorePath(path2));
|
||||
context.insert(path2);
|
||||
mkString(v, path, context);
|
||||
}
|
||||
|
||||
@@ -898,12 +833,9 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt(
|
||||
"cannot check the existence of '%1%', since path '%2%' is not valid",
|
||||
path, e.path),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format(
|
||||
"cannot check the existence of '%1%', since path '%2%' is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -946,14 +878,12 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
string s = readFile(state.checkSourcePath(state.toRealPath(path, context)));
|
||||
if (s.find((char) 0) != string::npos)
|
||||
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
|
||||
throw Error(format("the contents of the file '%1%' cannot be represented as a Nix string") % path);
|
||||
mkString(v, s.c_str());
|
||||
}
|
||||
|
||||
@@ -977,10 +907,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
|
||||
i = v2.attrs->find(state.symbols.create("path"));
|
||||
if (i == v2.attrs->end())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("attribute 'path' missing"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("attribute 'path' missing, at %1%") % pos);
|
||||
|
||||
PathSet context;
|
||||
string path = state.coerceToString(pos, *i->value, context, false, false);
|
||||
@@ -988,10 +915,8 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("cannot find '%1%', since path '%2%' is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
searchPath.emplace_back(prefix, path);
|
||||
@@ -1006,17 +931,14 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
string type = state.forceStringNoCtx(*args[0], pos);
|
||||
std::optional<HashType> ht = parseHashType(type);
|
||||
if (!ht)
|
||||
throw Error({
|
||||
.hint = hintfmt("unknown hash type '%1%'", type),
|
||||
.errPos = pos
|
||||
});
|
||||
HashType ht = parseHashType(type);
|
||||
if (ht == htUnknown)
|
||||
throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
|
||||
|
||||
PathSet context; // discarded
|
||||
Path p = state.coerceToPath(pos, *args[1], context);
|
||||
|
||||
mkString(v, hashFile(*ht, state.checkSourcePath(p)).to_string(Base16, false), context);
|
||||
mkString(v, hashFile(ht, state.checkSourcePath(p)).to_string(Base16, false), context);
|
||||
}
|
||||
|
||||
/* Read a directory (without . or ..) */
|
||||
@@ -1027,10 +949,8 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
|
||||
try {
|
||||
state.realiseContext(ctx);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
DirEntries entries = readDirectory(state.checkSourcePath(path));
|
||||
@@ -1100,13 +1020,9 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||
|
||||
for (auto path : context) {
|
||||
if (path.at(0) != '/')
|
||||
throw EvalError( {
|
||||
.hint = hintfmt(
|
||||
"in 'toFile': the file named '%1%' must not contain a reference "
|
||||
"to a derivation but contains (%2%)",
|
||||
name, path),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format(
|
||||
"in 'toFile': the file named '%1%' must not contain a reference "
|
||||
"to a derivation but contains (%2%), at %3%") % name % path % pos);
|
||||
refs.insert(state.store->parseStorePath(path));
|
||||
}
|
||||
|
||||
@@ -1123,7 +1039,7 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||
|
||||
|
||||
static void addPath(EvalState & state, const Pos & pos, const string & name, const Path & path_,
|
||||
Value * filterFun, FileIngestionMethod method, const Hash & expectedHash, Value & v)
|
||||
Value * filterFun, bool recursive, const Hash & expectedHash, Value & v)
|
||||
{
|
||||
const auto path = evalSettings.pureEval && expectedHash ?
|
||||
path_ :
|
||||
@@ -1154,12 +1070,12 @@ static void addPath(EvalState & state, const Pos & pos, const string & name, con
|
||||
|
||||
std::optional<StorePath> expectedStorePath;
|
||||
if (expectedHash)
|
||||
expectedStorePath = state.store->makeFixedOutputPath(method, expectedHash, name);
|
||||
expectedStorePath = state.store->makeFixedOutputPath(recursive, expectedHash, name);
|
||||
Path dstPath;
|
||||
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
||||
dstPath = state.store->printStorePath(settings.readOnlyMode
|
||||
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
|
||||
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair));
|
||||
? state.store->computeStorePathForPath(name, path, recursive, htSHA256, filter).first
|
||||
: state.store->addToStore(name, path, recursive, htSHA256, filter, state.repair));
|
||||
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
|
||||
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
|
||||
} else
|
||||
@@ -1174,21 +1090,13 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
||||
PathSet context;
|
||||
Path path = state.coerceToPath(pos, *args[1], context);
|
||||
if (!context.empty())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("string '%1%' cannot refer to other paths", path),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
|
||||
|
||||
state.forceValue(*args[0], pos);
|
||||
if (args[0]->type != tLambda)
|
||||
throw TypeError({
|
||||
.hint = hintfmt(
|
||||
"first argument in call to 'filterSource' is not a function but %1%",
|
||||
showType(*args[0])),
|
||||
.errPos = pos
|
||||
});
|
||||
throw TypeError(format("first argument in call to 'filterSource' is not a function but %1%, at %2%") % showType(*args[0]) % pos);
|
||||
|
||||
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, Hash(), v);
|
||||
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], true, Hash(), v);
|
||||
}
|
||||
|
||||
static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
@@ -1197,7 +1105,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
Path path;
|
||||
string name;
|
||||
Value * filterFun = nullptr;
|
||||
auto method = FileIngestionMethod::Recursive;
|
||||
auto recursive = true;
|
||||
Hash expectedHash;
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
@@ -1206,34 +1114,25 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
PathSet context;
|
||||
path = state.coerceToPath(*attr.pos, *attr.value, context);
|
||||
if (!context.empty())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("string '%1%' cannot refer to other paths", path),
|
||||
.errPos = *attr.pos
|
||||
});
|
||||
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % *attr.pos);
|
||||
} else if (attr.name == state.sName)
|
||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else if (n == "filter") {
|
||||
state.forceValue(*attr.value, pos);
|
||||
filterFun = attr.value;
|
||||
} else if (n == "recursive")
|
||||
method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos) };
|
||||
recursive = state.forceBool(*attr.value, *attr.pos);
|
||||
else if (n == "sha256")
|
||||
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||
else
|
||||
throw EvalError({
|
||||
.hint = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name),
|
||||
.errPos = *attr.pos
|
||||
});
|
||||
throw EvalError(format("unsupported argument '%1%' to 'addPath', at %2%") % attr.name % *attr.pos);
|
||||
}
|
||||
if (path.empty())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("'path' required"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("'path' required, at %1%") % pos);
|
||||
if (name.empty())
|
||||
name = baseNameOf(path);
|
||||
|
||||
addPath(state, pos, name, path, filterFun, method, expectedHash, v);
|
||||
addPath(state, pos, name, path, filterFun, recursive, expectedHash, v);
|
||||
}
|
||||
|
||||
|
||||
@@ -1287,10 +1186,7 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
// !!! Should we create a symbol here or just do a lookup?
|
||||
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
|
||||
if (i == args[1]->attrs->end())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("attribute '%1%' missing", attr),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("attribute '%1%' missing, at %2%") % attr % pos);
|
||||
// !!! add to stack trace?
|
||||
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
|
||||
state.forceValue(*i->value, pos);
|
||||
@@ -1370,20 +1266,15 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||
|
||||
Bindings::iterator j = v2.attrs->find(state.sName);
|
||||
if (j == v2.attrs->end())
|
||||
throw TypeError({
|
||||
.hint = hintfmt("'name' attribute missing in a call to 'listToAttrs'"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw TypeError(format("'name' attribute missing in a call to 'listToAttrs', at %1%") % pos);
|
||||
string name = state.forceStringNoCtx(*j->value, pos);
|
||||
|
||||
Symbol sym = state.symbols.create(name);
|
||||
if (seen.insert(sym).second) {
|
||||
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
|
||||
if (j2 == v2.attrs->end())
|
||||
throw TypeError({
|
||||
.hint = hintfmt("'value' attribute missing in a call to 'listToAttrs'"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw TypeError(format("'value' attribute missing in a call to 'listToAttrs', at %1%") % pos);
|
||||
|
||||
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
||||
}
|
||||
}
|
||||
@@ -1456,10 +1347,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
if (args[0]->type != tLambda)
|
||||
throw TypeError({
|
||||
.hint = hintfmt("'functionArgs' requires a function"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw TypeError(format("'functionArgs' requires a function, at %1%") % pos);
|
||||
|
||||
if (!args[0]->lambda.fun->matchAttrs) {
|
||||
state.mkAttrs(v, 0);
|
||||
@@ -1512,10 +1400,7 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu
|
||||
{
|
||||
state.forceList(list, pos);
|
||||
if (n < 0 || (unsigned int) n >= list.listSize())
|
||||
throw Error({
|
||||
.hint = hintfmt("list index %1% is out of bounds", n),
|
||||
.errPos = pos
|
||||
});
|
||||
throw Error(format("list index %1% is out of bounds, at %2%") % n % pos);
|
||||
state.forceValue(*list.listElems()[n], pos);
|
||||
v = *list.listElems()[n];
|
||||
}
|
||||
@@ -1542,11 +1427,7 @@ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
{
|
||||
state.forceList(*args[0], pos);
|
||||
if (args[0]->listSize() == 0)
|
||||
throw Error({
|
||||
.hint = hintfmt("'tail' called on an empty list"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
throw Error(format("'tail' called on an empty list, at %1%") % pos);
|
||||
state.mkList(v, args[0]->listSize() - 1);
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
v.listElems()[n] = args[0]->listElems()[n + 1];
|
||||
@@ -1687,10 +1568,7 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val
|
||||
auto len = state.forceInt(*args[1], pos);
|
||||
|
||||
if (len < 0)
|
||||
throw EvalError({
|
||||
.hint = hintfmt("cannot create list of size %1%", len),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("cannot create list of size %1%, at %2%") % len % pos);
|
||||
|
||||
state.mkList(v, len);
|
||||
|
||||
@@ -1848,11 +1726,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
|
||||
state.forceValue(*args[1], pos);
|
||||
|
||||
NixFloat f2 = state.forceFloat(*args[1], pos);
|
||||
if (f2 == 0)
|
||||
throw EvalError({
|
||||
.hint = hintfmt("division by zero"),
|
||||
.errPos = pos
|
||||
});
|
||||
if (f2 == 0) throw EvalError(format("division by zero, at %1%") % pos);
|
||||
|
||||
if (args[0]->type == tFloat || args[1]->type == tFloat) {
|
||||
mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
|
||||
@@ -1861,11 +1735,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
|
||||
NixInt i2 = state.forceInt(*args[1], pos);
|
||||
/* Avoid division overflow as it might raise SIGFPE. */
|
||||
if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1)
|
||||
throw EvalError({
|
||||
.hint = hintfmt("overflow in integer division"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
throw EvalError(format("overflow in integer division, at %1%") % pos);
|
||||
mkInt(v, i1 / i2);
|
||||
}
|
||||
}
|
||||
@@ -1921,11 +1791,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
|
||||
PathSet context;
|
||||
string s = state.coerceToString(pos, *args[2], context);
|
||||
|
||||
if (start < 0)
|
||||
throw EvalError({
|
||||
.hint = hintfmt("negative start position in 'substring'"),
|
||||
.errPos = pos
|
||||
});
|
||||
if (start < 0) throw EvalError(format("negative start position in 'substring', at %1%") % pos);
|
||||
|
||||
mkString(v, (unsigned int) start >= s.size() ? "" : string(s, start, len), context);
|
||||
}
|
||||
@@ -1943,17 +1809,14 @@ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args
|
||||
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
string type = state.forceStringNoCtx(*args[0], pos);
|
||||
std::optional<HashType> ht = parseHashType(type);
|
||||
if (!ht)
|
||||
throw Error({
|
||||
.hint = hintfmt("unknown hash type '%1%'", type),
|
||||
.errPos = pos
|
||||
});
|
||||
HashType ht = parseHashType(type);
|
||||
if (ht == htUnknown)
|
||||
throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
|
||||
|
||||
PathSet context; // discarded
|
||||
string s = state.forceString(*args[1], context, pos);
|
||||
|
||||
mkString(v, hashString(*ht, s).to_string(Base16, false), context);
|
||||
mkString(v, hashString(ht, s).to_string(Base16, false), context);
|
||||
}
|
||||
|
||||
|
||||
@@ -1990,16 +1853,10 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
|
||||
} catch (std::regex_error &e) {
|
||||
if (e.code() == std::regex_constants::error_space) {
|
||||
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
||||
throw EvalError({
|
||||
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
|
||||
.errPos = pos
|
||||
});
|
||||
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
||||
throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
|
||||
} else {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("invalid regular expression '%s'", re),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError("invalid regular expression '%s', at %s", re, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2063,16 +1920,10 @@ static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
|
||||
} catch (std::regex_error &e) {
|
||||
if (e.code() == std::regex_constants::error_space) {
|
||||
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
||||
throw EvalError({
|
||||
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
|
||||
.errPos = pos
|
||||
});
|
||||
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
||||
throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
|
||||
} else {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("invalid regular expression '%s'", re),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError("invalid regular expression '%s', at %s", re, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2103,10 +1954,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
|
||||
state.forceList(*args[0], pos);
|
||||
state.forceList(*args[1], pos);
|
||||
if (args[0]->listSize() != args[1]->listSize())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("'from' and 'to' arguments to 'replaceStrings' have different lengths, at %1%") % pos);
|
||||
|
||||
vector<string> from;
|
||||
from.reserve(args[0]->listSize());
|
||||
@@ -2209,11 +2057,10 @@ static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args
|
||||
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
|
||||
|
||||
|
||||
RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun,
|
||||
std::optional<std::string> requiredFeature)
|
||||
RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun)
|
||||
{
|
||||
if (!primOps) primOps = new PrimOps;
|
||||
primOps->push_back({name, arity, fun, requiredFeature});
|
||||
primOps->emplace_back(name, arity, fun);
|
||||
}
|
||||
|
||||
|
||||
@@ -2405,8 +2252,7 @@ void EvalState::createBaseEnv()
|
||||
|
||||
if (RegisterPrimOp::primOps)
|
||||
for (auto & primOp : *RegisterPrimOp::primOps)
|
||||
if (!primOp.requiredFeature || settings.isExperimentalFeatureEnabled(*primOp.requiredFeature))
|
||||
addPrimOp(primOp.name, primOp.arity, primOp.primOp);
|
||||
addPrimOp(std::get<0>(primOp), std::get<1>(primOp), std::get<2>(primOp));
|
||||
|
||||
/* Now that we've added all primops, sort the `builtins' set,
|
||||
because attribute lookups expect it to be sorted. */
|
||||
|
||||
@@ -7,25 +7,12 @@ namespace nix {
|
||||
|
||||
struct RegisterPrimOp
|
||||
{
|
||||
struct Info
|
||||
{
|
||||
std::string name;
|
||||
size_t arity;
|
||||
PrimOpFun primOp;
|
||||
std::optional<std::string> requiredFeature;
|
||||
};
|
||||
|
||||
typedef std::vector<Info> PrimOps;
|
||||
typedef std::vector<std::tuple<std::string, size_t, PrimOpFun>> PrimOps;
|
||||
static PrimOps * primOps;
|
||||
|
||||
/* You can register a constant by passing an arity of 0. fun
|
||||
will get called during EvalState initialization, so there
|
||||
may be primops not yet added and builtins is not yet sorted. */
|
||||
RegisterPrimOp(
|
||||
std::string name,
|
||||
size_t arity,
|
||||
PrimOpFun fun,
|
||||
std::optional<std::string> requiredFeature = {});
|
||||
RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun);
|
||||
};
|
||||
|
||||
/* These primops are disabled without enableNativeCode, but plugins
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "primops.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "store-api.hh"
|
||||
#include "derivations.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -146,10 +146,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||
auto sAllOutputs = state.symbols.create("allOutputs");
|
||||
for (auto & i : *args[1]->attrs) {
|
||||
if (!state.store->isStorePath(i.name))
|
||||
throw EvalError({
|
||||
.hint = hintfmt("Context key '%s' is not a store path", i.name),
|
||||
.errPos = *i.pos
|
||||
});
|
||||
throw EvalError("Context key '%s' is not a store path, at %s", i.name, i.pos);
|
||||
if (!settings.readOnlyMode)
|
||||
state.store->ensurePath(state.store->parseStorePath(i.name));
|
||||
state.forceAttrs(*i.value, *i.pos);
|
||||
@@ -163,10 +160,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||
if (iter != i.value->attrs->end()) {
|
||||
if (state.forceBool(*iter->value, *iter->pos)) {
|
||||
if (!isDerivation(i.name)) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
|
||||
.errPos = *i.pos
|
||||
});
|
||||
throw EvalError("Tried to add all-outputs context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
|
||||
}
|
||||
context.insert("=" + string(i.name));
|
||||
}
|
||||
@@ -176,10 +170,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||
if (iter != i.value->attrs->end()) {
|
||||
state.forceList(*iter->value, *iter->pos);
|
||||
if (iter->value->listSize() && !isDerivation(i.name)) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
|
||||
.errPos = *i.pos
|
||||
});
|
||||
throw EvalError("Tried to add derivation output context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
|
||||
}
|
||||
for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
|
||||
auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
|
||||
|
||||
@@ -35,17 +35,11 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
else if (n == "submodules")
|
||||
fetchSubmodules = state.forceBool(*attr.value, *attr.pos);
|
||||
else
|
||||
throw EvalError({
|
||||
.hint = hintfmt("unsupported argument '%s' to 'fetchGit'", attr.name),
|
||||
.errPos = *attr.pos
|
||||
});
|
||||
throw EvalError("unsupported argument '%s' to 'fetchGit', at %s", attr.name, *attr.pos);
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("'url' argument required"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("'url' argument required, at %1%") % pos);
|
||||
|
||||
} else
|
||||
url = state.coerceToString(pos, *args[0], context, false, false);
|
||||
|
||||
@@ -38,17 +38,11 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else
|
||||
throw EvalError({
|
||||
.hint = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
|
||||
.errPos = *attr.pos
|
||||
});
|
||||
throw EvalError("unsupported argument '%s' to 'fetchMercurial', at %s", attr.name, *attr.pos);
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
throw EvalError({
|
||||
.hint = hintfmt("'url' argument required"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError(format("'url' argument required, at %1%") % pos);
|
||||
|
||||
} else
|
||||
url = state.coerceToString(pos, *args[0], context, false, false);
|
||||
|
||||
@@ -23,7 +23,7 @@ void emitTreeAttrs(
|
||||
|
||||
assert(tree.info.narHash);
|
||||
mkString(*state.allocAttr(v, state.symbols.create("narHash")),
|
||||
tree.info.narHash.to_string(SRI, true));
|
||||
tree.info.narHash.to_string(SRI));
|
||||
|
||||
if (input->getRev()) {
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), input->getRev()->gitRev());
|
||||
@@ -66,10 +66,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
|
||||
}
|
||||
|
||||
if (!attrs.count("type"))
|
||||
throw Error({
|
||||
.hint = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw Error("attribute 'type' is missing in call to 'fetchTree', at %s", pos);
|
||||
|
||||
input = fetchers::inputFromAttrs(attrs);
|
||||
} else
|
||||
@@ -106,21 +103,17 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||
if (n == "url")
|
||||
url = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else if (n == "sha256")
|
||||
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else
|
||||
throw EvalError({
|
||||
.hint = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
|
||||
.errPos = *attr.pos
|
||||
});
|
||||
}
|
||||
throw EvalError("unsupported argument '%s' to '%s', at %s",
|
||||
attr.name, who, *attr.pos);
|
||||
}
|
||||
|
||||
if (!url)
|
||||
throw EvalError({
|
||||
.hint = hintfmt("'url' argument required"),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError("'url' argument required, at %s", pos);
|
||||
|
||||
} else
|
||||
url = state.forceStringNoCtx(*args[0], pos);
|
||||
|
||||
@@ -147,7 +140,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||
: hashFile(htSHA256, path);
|
||||
if (hash != *expectedHash)
|
||||
throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n wanted: %s\n got: %s",
|
||||
*url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
|
||||
*url, expectedHash->to_string(), hash.to_string());
|
||||
}
|
||||
|
||||
if (state.allowedPaths)
|
||||
|
||||
@@ -81,10 +81,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
try {
|
||||
visit(v, parser(tomlStream).parse());
|
||||
} catch (std::runtime_error & e) {
|
||||
throw EvalError({
|
||||
.hint = hintfmt("while parsing a TOML string: %s", e.what()),
|
||||
.errPos = pos
|
||||
});
|
||||
throw EvalError("while parsing a TOML string at %s: %s", pos, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||
break;
|
||||
|
||||
default:
|
||||
throw TypeError("cannot convert %1% to JSON", showType(v));
|
||||
throw TypeError(format("cannot convert %1% to JSON") % showType(v));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
|
||||
JSONPlaceholder & out, PathSet & context) const
|
||||
{
|
||||
throw TypeError("cannot convert %1% to JSON", showType());
|
||||
throw TypeError(format("cannot convert %1% to JSON") % showType());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ namespace nix::fetchers {
|
||||
|
||||
struct Cache
|
||||
{
|
||||
virtual ~Cache() { }
|
||||
|
||||
virtual void add(
|
||||
ref<Store> store,
|
||||
const Attrs & inAttrs,
|
||||
|
||||
@@ -36,7 +36,7 @@ std::unique_ptr<Input> inputFromAttrs(const Attrs & attrs)
|
||||
if (res) {
|
||||
if (auto narHash = maybeGetStrAttr(attrs, "narHash"))
|
||||
// FIXME: require SRI hash.
|
||||
res->narHash = newHashAllowEmpty(*narHash, {});
|
||||
res->narHash = Hash(*narHash);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ Attrs Input::toAttrs() const
|
||||
{
|
||||
auto attrs = toAttrsInternal();
|
||||
if (narHash)
|
||||
attrs.emplace("narHash", narHash->to_string(SRI, true));
|
||||
attrs.emplace("narHash", narHash->to_string(SRI));
|
||||
attrs.emplace("type", type());
|
||||
return attrs;
|
||||
}
|
||||
@@ -67,7 +67,7 @@ std::pair<Tree, std::shared_ptr<const Input>> Input::fetchTree(ref<Store> store)
|
||||
|
||||
if (narHash && narHash != input->narHash)
|
||||
throw Error("NAR hash mismatch in input '%s' (%s), expected '%s', got '%s'",
|
||||
to_string(), tree.actualPath, narHash->to_string(SRI, true), input->narHash->to_string(SRI, true));
|
||||
to_string(), tree.actualPath, narHash->to_string(SRI), input->narHash->to_string(SRI));
|
||||
|
||||
return {std::move(tree), input};
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ struct GitInput : Input
|
||||
return files.count(file);
|
||||
};
|
||||
|
||||
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
auto storePath = store->addToStore("source", actualUrl, true, htSHA256, filter);
|
||||
|
||||
auto tree = Tree {
|
||||
.actualPath = store->printStorePath(storePath),
|
||||
@@ -282,10 +282,7 @@ struct GitInput : Input
|
||||
// FIXME: git stderr messes up our progress indicator, so
|
||||
// we're using --quiet for now. Should process its stderr.
|
||||
try {
|
||||
auto fetchRef = input->ref->compare(0, 5, "refs/") == 0
|
||||
? *input->ref
|
||||
: "refs/heads/" + *input->ref;
|
||||
runProgram("git", true, { "-C", repoDir, "fetch", "--quiet", "--force", "--", actualUrl, fmt("%s:%s", fetchRef, fetchRef) });
|
||||
runProgram("git", true, { "-C", repoDir, "fetch", "--quiet", "--force", "--", actualUrl, fmt("%s:%s", *input->ref, *input->ref) });
|
||||
} catch (Error & e) {
|
||||
if (!pathExists(localRefFile)) throw;
|
||||
warn("could not update local clone of Git repository '%s'; continuing with the most recent version", actualUrl);
|
||||
@@ -350,7 +347,7 @@ struct GitInput : Input
|
||||
unpackTarfile(*source, tmpDir);
|
||||
}
|
||||
|
||||
auto storePath = store->addToStore(name, tmpDir, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
auto storePath = store->addToStore(name, tmpDir, true, htSHA256, filter);
|
||||
|
||||
auto lastModified = std::stoull(runProgram("git", true, { "-C", repoDir, "log", "-1", "--format=%ct", input->rev->gitRev() }));
|
||||
|
||||
@@ -421,7 +418,7 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
auto input = std::make_unique<GitInput>(parseURL(getStrAttr(attrs, "url")));
|
||||
if (auto ref = maybeGetStrAttr(attrs, "ref")) {
|
||||
if (std::regex_search(*ref, badGitRefRegex))
|
||||
if (!std::regex_match(*ref, refRegex))
|
||||
throw BadURL("invalid Git branch/tag name '%s'", *ref);
|
||||
input->ref = *ref;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ struct GitHubInput : Input
|
||||
readFile(
|
||||
store->toRealPath(
|
||||
downloadFile(store, url, "source", false).storePath)));
|
||||
rev = Hash(std::string { json["sha"] }, htSHA1);
|
||||
rev = Hash(json["sha"], htSHA1);
|
||||
debug("HEAD revision for '%s' is %s", url, rev->gitRev());
|
||||
}
|
||||
|
||||
|
||||
@@ -8,4 +8,4 @@ libfetchers_SOURCES := $(wildcard $(d)/*.cc)
|
||||
|
||||
libfetchers_CXXFLAGS += -I src/libutil -I src/libstore
|
||||
|
||||
libfetchers_LIBS = libutil libstore
|
||||
libfetchers_LIBS = libutil libstore libnixrust
|
||||
|
||||
@@ -114,7 +114,7 @@ struct MercurialInput : Input
|
||||
return files.count(file);
|
||||
};
|
||||
|
||||
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
auto storePath = store->addToStore("source", actualUrl, true, htSHA256, filter);
|
||||
|
||||
return {Tree {
|
||||
.actualPath = store->printStorePath(storePath),
|
||||
|
||||
@@ -67,15 +67,11 @@ DownloadFileResult downloadFile(
|
||||
StringSink sink;
|
||||
dumpString(*res.data, sink);
|
||||
auto hash = hashString(htSHA256, *res.data);
|
||||
ValidPathInfo info(store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name));
|
||||
ValidPathInfo info(store->makeFixedOutputPath(false, hash, name));
|
||||
info.narHash = hashString(htSHA256, *sink.s);
|
||||
info.narSize = sink.s->size();
|
||||
info.ca = FixedOutputHash {
|
||||
.method = FileIngestionMethod::Flat,
|
||||
.hash = hash,
|
||||
};
|
||||
auto source = StringSource { *sink.s };
|
||||
store->addToStore(info, source, NoRepair, NoCheckSigs);
|
||||
info.ca = makeFixedOutputCA(false, hash);
|
||||
store->addToStore(info, sink.s, NoRepair, NoCheckSigs);
|
||||
storePath = std::move(info.path);
|
||||
}
|
||||
|
||||
@@ -145,7 +141,7 @@ Tree downloadTarball(
|
||||
throw nix::Error("tarball '%s' contains an unexpected number of top-level files", url);
|
||||
auto topDir = tmpDir + "/" + members.begin()->name;
|
||||
lastModified = lstat(topDir).st_mtime;
|
||||
unpackedStorePath = store->addToStore(name, topDir, FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, NoRepair);
|
||||
unpackedStorePath = store->addToStore(name, topDir, true, htSHA256, defaultPathFilter, NoRepair);
|
||||
}
|
||||
|
||||
Attrs infoAttrs({
|
||||
@@ -199,9 +195,9 @@ struct TarballInput : Input
|
||||
// NAR hashes are preferred over file hashes since tar/zip files
|
||||
// don't have a canonical representation.
|
||||
if (narHash)
|
||||
url2.query.insert_or_assign("narHash", narHash->to_string(SRI, true));
|
||||
url2.query.insert_or_assign("narHash", narHash->to_string(SRI));
|
||||
else if (hash)
|
||||
url2.query.insert_or_assign("hash", hash->to_string(SRI, true));
|
||||
url2.query.insert_or_assign("hash", hash->to_string(SRI));
|
||||
return url2;
|
||||
}
|
||||
|
||||
@@ -210,7 +206,7 @@ struct TarballInput : Input
|
||||
Attrs attrs;
|
||||
attrs.emplace("url", url.to_string());
|
||||
if (hash)
|
||||
attrs.emplace("hash", hash->to_string(SRI, true));
|
||||
attrs.emplace("hash", hash->to_string(SRI));
|
||||
return attrs;
|
||||
}
|
||||
|
||||
@@ -267,7 +263,8 @@ struct TarballInputScheme : InputScheme
|
||||
|
||||
auto input = std::make_unique<TarballInput>(parseURL(getStrAttr(attrs, "url")));
|
||||
if (auto hash = maybeGetStrAttr(attrs, "hash"))
|
||||
input->hash = newHashAllowEmpty(*hash, {});
|
||||
// FIXME: require SRI hash.
|
||||
input->hash = Hash(*hash);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace nix::fetchers {
|
||||
StorePath TreeInfo::computeStorePath(Store & store) const
|
||||
{
|
||||
assert(narHash);
|
||||
return store.makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, "source");
|
||||
return store.makeFixedOutputPath(true, narHash, "source");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "common-args.hh"
|
||||
#include "globals.hh"
|
||||
#include "loggers.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -39,14 +38,6 @@ MixCommonArgs::MixCommonArgs(const string & programName)
|
||||
}},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "log-format",
|
||||
.description = "format of log output; \"raw\", \"internal-json\", \"bar\" "
|
||||
"or \"bar-with-logs\"",
|
||||
.labels = {"format"},
|
||||
.handler = {[](std::string format) { setLogFormat(format); }},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "max-jobs",
|
||||
.shortName = 'j',
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#include "loggers.hh"
|
||||
#include "progress-bar.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
LogFormat defaultLogFormat = LogFormat::raw;
|
||||
|
||||
LogFormat parseLogFormat(const std::string & logFormatStr) {
|
||||
if (logFormatStr == "raw")
|
||||
return LogFormat::raw;
|
||||
else if (logFormatStr == "raw-with-logs")
|
||||
return LogFormat::rawWithLogs;
|
||||
else if (logFormatStr == "internal-json")
|
||||
return LogFormat::internalJson;
|
||||
else if (logFormatStr == "bar")
|
||||
return LogFormat::bar;
|
||||
else if (logFormatStr == "bar-with-logs")
|
||||
return LogFormat::barWithLogs;
|
||||
throw Error("option 'log-format' has an invalid value '%s'", logFormatStr);
|
||||
}
|
||||
|
||||
Logger * makeDefaultLogger() {
|
||||
switch (defaultLogFormat) {
|
||||
case LogFormat::raw:
|
||||
return makeSimpleLogger(false);
|
||||
case LogFormat::rawWithLogs:
|
||||
return makeSimpleLogger(true);
|
||||
case LogFormat::internalJson:
|
||||
return makeJSONLogger(*makeSimpleLogger(true));
|
||||
case LogFormat::bar:
|
||||
return makeProgressBar();
|
||||
case LogFormat::barWithLogs:
|
||||
return makeProgressBar(true);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void setLogFormat(const std::string & logFormatStr) {
|
||||
setLogFormat(parseLogFormat(logFormatStr));
|
||||
}
|
||||
|
||||
void setLogFormat(const LogFormat & logFormat) {
|
||||
defaultLogFormat = logFormat;
|
||||
createDefaultLogger();
|
||||
}
|
||||
|
||||
void createDefaultLogger() {
|
||||
logger = makeDefaultLogger();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
enum class LogFormat {
|
||||
raw,
|
||||
rawWithLogs,
|
||||
internalJson,
|
||||
bar,
|
||||
barWithLogs,
|
||||
};
|
||||
|
||||
void setLogFormat(const std::string & logFormatStr);
|
||||
void setLogFormat(const LogFormat & logFormat);
|
||||
|
||||
void createDefaultLogger();
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "shared.hh"
|
||||
#include "store-api.hh"
|
||||
#include "util.hh"
|
||||
#include "loggers.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
@@ -48,10 +47,7 @@ void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
||||
unsigned long long downloadSize, unsigned long long narSize, Verbosity lvl)
|
||||
{
|
||||
if (!willBuild.empty()) {
|
||||
if (willBuild.size() == 1)
|
||||
printMsg(lvl, fmt("this derivation will be built:"));
|
||||
else
|
||||
printMsg(lvl, fmt("these %d derivations will be built:", willBuild.size()));
|
||||
printMsg(lvl, "these derivations will be built:");
|
||||
auto sorted = store->topoSortPaths(willBuild);
|
||||
reverse(sorted.begin(), sorted.end());
|
||||
for (auto & i : sorted)
|
||||
@@ -59,18 +55,9 @@ void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
||||
}
|
||||
|
||||
if (!willSubstitute.empty()) {
|
||||
const float downloadSizeMiB = downloadSize / (1024.f * 1024.f);
|
||||
const float narSizeMiB = narSize / (1024.f * 1024.f);
|
||||
if (willSubstitute.size() == 1) {
|
||||
printMsg(lvl, fmt("this path will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||
downloadSizeMiB,
|
||||
narSizeMiB));
|
||||
} else {
|
||||
printMsg(lvl, fmt("these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||
willSubstitute.size(),
|
||||
downloadSizeMiB,
|
||||
narSizeMiB));
|
||||
}
|
||||
printMsg(lvl, fmt("these paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||
downloadSize / (1024.0 * 1024.0),
|
||||
narSize / (1024.0 * 1024.0)));
|
||||
for (auto & i : willSubstitute)
|
||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
||||
}
|
||||
@@ -88,7 +75,7 @@ string getArg(const string & opt,
|
||||
Strings::iterator & i, const Strings::iterator & end)
|
||||
{
|
||||
++i;
|
||||
if (i == end) throw UsageError("'%1%' requires an argument", opt);
|
||||
if (i == end) throw UsageError(format("'%1%' requires an argument") % opt);
|
||||
return *i;
|
||||
}
|
||||
|
||||
@@ -182,7 +169,7 @@ LegacyArgs::LegacyArgs(const std::string & programName,
|
||||
.longName = "no-build-output",
|
||||
.shortName = 'Q',
|
||||
.description = "do not show build output",
|
||||
.handler = {[&]() {setLogFormat(LogFormat::raw); }},
|
||||
.handler = {&settings.verboseBuild, false},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
@@ -247,7 +234,7 @@ bool LegacyArgs::processArgs(const Strings & args, bool finish)
|
||||
Strings ss(args);
|
||||
auto pos = ss.begin();
|
||||
if (!parseArg(pos, ss.end()))
|
||||
throw UsageError("unexpected argument '%1%'", args.front());
|
||||
throw UsageError(format("unexpected argument '%1%'") % args.front());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -294,7 +281,7 @@ void showManPage(const string & name)
|
||||
restoreSignals();
|
||||
setenv("MANPATH", settings.nixManDir.c_str(), 1);
|
||||
execlp("man", "man", name.c_str(), nullptr);
|
||||
throw SysError("command 'man %1%' failed", name.c_str());
|
||||
throw SysError(format("command 'man %1%' failed") % name.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -302,8 +289,6 @@ int handleExceptions(const string & programName, std::function<void()> fun)
|
||||
{
|
||||
ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this
|
||||
|
||||
ErrorInfo::programName = baseNameOf(programName);
|
||||
|
||||
string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
||||
try {
|
||||
try {
|
||||
@@ -319,12 +304,13 @@ int handleExceptions(const string & programName, std::function<void()> fun)
|
||||
} catch (Exit & e) {
|
||||
return e.status;
|
||||
} catch (UsageError & e) {
|
||||
logError(e.info());
|
||||
printError("Try '%1% --help' for more information.", programName);
|
||||
printError(
|
||||
format(error + "%1%\nTry '%2% --help' for more information.")
|
||||
% e.what() % programName);
|
||||
return 1;
|
||||
} catch (BaseError & e) {
|
||||
logError(e.info());
|
||||
if (e.hasTrace() && !loggerSettings.showTrace.get())
|
||||
printError(format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
|
||||
if (e.prefix() != "" && !settings.showTrace)
|
||||
printError("(use '--show-trace' to show detailed location information)");
|
||||
return e.status;
|
||||
} catch (std::bad_alloc & e) {
|
||||
@@ -360,7 +346,7 @@ RunPager::RunPager()
|
||||
execlp("pager", "pager", nullptr);
|
||||
execlp("less", "less", nullptr);
|
||||
execlp("more", "more", nullptr);
|
||||
throw SysError("executing '%1%'", pager);
|
||||
throw SysError(format("executing '%1%'") % pager);
|
||||
});
|
||||
|
||||
pid.setKillSignal(SIGINT);
|
||||
|
||||
@@ -56,7 +56,7 @@ template<class N> N getIntArg(const string & opt,
|
||||
Strings::iterator & i, const Strings::iterator & end, bool allowUnit)
|
||||
{
|
||||
++i;
|
||||
if (i == end) throw UsageError("'%1%' requires an argument", opt);
|
||||
if (i == end) throw UsageError(format("'%1%' requires an argument") % opt);
|
||||
string s = *i;
|
||||
N multiplier = 1;
|
||||
if (allowUnit && !s.empty()) {
|
||||
@@ -66,13 +66,13 @@ template<class N> N getIntArg(const string & opt,
|
||||
else if (u == 'M') multiplier = 1ULL << 20;
|
||||
else if (u == 'G') multiplier = 1ULL << 30;
|
||||
else if (u == 'T') multiplier = 1ULL << 40;
|
||||
else throw UsageError("invalid unit specifier '%1%'", u);
|
||||
else throw UsageError(format("invalid unit specifier '%1%'") % u);
|
||||
s.resize(s.size() - 1);
|
||||
}
|
||||
}
|
||||
N n;
|
||||
if (!string2Int(s, n))
|
||||
throw UsageError("'%1%' requires an integer argument", opt);
|
||||
throw UsageError(format("'%1%' requires an integer argument") % opt);
|
||||
return n * multiplier;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "error.hh"
|
||||
#include "types.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <regex>
|
||||
#include <fstream>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -41,14 +40,14 @@ void BinaryCacheStore::init()
|
||||
upsertFile(cacheInfoFile, "StoreDir: " + storeDir + "\n", "text/x-nix-cache-info");
|
||||
} else {
|
||||
for (auto & line : tokenizeString<Strings>(*cacheInfo, "\n")) {
|
||||
size_t colon= line.find(':');
|
||||
if (colon ==std::string::npos) continue;
|
||||
size_t colon = line.find(':');
|
||||
if (colon == std::string::npos) continue;
|
||||
auto name = line.substr(0, colon);
|
||||
auto value = trim(line.substr(colon + 1, std::string::npos));
|
||||
if (name == "StoreDir") {
|
||||
if (value != storeDir)
|
||||
throw Error("binary cache '%s' is for Nix stores with prefix '%s', not '%s'",
|
||||
getUri(), value, storeDir);
|
||||
throw Error(format("binary cache '%s' is for Nix stores with prefix '%s', not '%s'")
|
||||
% getUri() % value % storeDir);
|
||||
} else if (name == "WantMassQuery") {
|
||||
wantMassQuery.setDefault(value == "1" ? "true" : "false");
|
||||
} else if (name == "Priority") {
|
||||
@@ -58,13 +57,6 @@ void BinaryCacheStore::init()
|
||||
}
|
||||
}
|
||||
|
||||
void BinaryCacheStore::upsertFile(const std::string & path,
|
||||
std::string && data,
|
||||
const std::string & mimeType)
|
||||
{
|
||||
upsertFile(path, std::make_shared<std::stringstream>(std::move(data)), mimeType);
|
||||
}
|
||||
|
||||
void BinaryCacheStore::getFile(const std::string & path,
|
||||
Callback<std::shared_ptr<std::string>> callback) noexcept
|
||||
{
|
||||
@@ -101,7 +93,7 @@ std::shared_ptr<std::string> BinaryCacheStore::getFile(const std::string & path)
|
||||
|
||||
std::string BinaryCacheStore::narInfoFileFor(const StorePath & storePath)
|
||||
{
|
||||
return std::string(storePath.hashPart()) + ".narinfo";
|
||||
return storePathToHash(printStorePath(storePath)) + ".narinfo";
|
||||
}
|
||||
|
||||
void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
|
||||
@@ -110,7 +102,7 @@ void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
|
||||
|
||||
upsertFile(narInfoFile, narInfo->to_string(*this), "text/x-nix-narinfo");
|
||||
|
||||
std::string hashPart(narInfo->path.hashPart());
|
||||
auto hashPart = storePathToHash(printStorePath(narInfo->path));
|
||||
|
||||
{
|
||||
auto state_(state.lock());
|
||||
@@ -121,74 +113,10 @@ void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
|
||||
diskCache->upsertNarInfo(getUri(), hashPart, std::shared_ptr<NarInfo>(narInfo));
|
||||
}
|
||||
|
||||
AutoCloseFD openFile(const Path & path)
|
||||
void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor)
|
||||
{
|
||||
auto fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
return fd;
|
||||
}
|
||||
|
||||
struct FileSource : FdSource
|
||||
{
|
||||
AutoCloseFD fd2;
|
||||
|
||||
FileSource(const Path & path)
|
||||
: fd2(openFile(path))
|
||||
{
|
||||
fd = fd2.get();
|
||||
}
|
||||
};
|
||||
|
||||
void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs)
|
||||
{
|
||||
assert(info.narHash && info.narSize);
|
||||
|
||||
if (!repair && isValidPath(info.path)) {
|
||||
// FIXME: copyNAR -> null sink
|
||||
narSource.drain();
|
||||
return;
|
||||
}
|
||||
|
||||
auto [fdTemp, fnTemp] = createTempFile();
|
||||
|
||||
auto now1 = std::chrono::steady_clock::now();
|
||||
|
||||
/* Read the NAR simultaneously into a CompressionSink+FileSink (to
|
||||
write the compressed NAR to disk), into a HashSink (to get the
|
||||
NAR hash), and into a NarAccessor (to get the NAR listing). */
|
||||
HashSink fileHashSink(htSHA256);
|
||||
std::shared_ptr<FSAccessor> narAccessor;
|
||||
{
|
||||
FdSink fileSink(fdTemp.get());
|
||||
TeeSink teeSink(fileSink, fileHashSink);
|
||||
auto compressionSink = makeCompressionSink(compression, teeSink);
|
||||
TeeSource teeSource(narSource, *compressionSink);
|
||||
narAccessor = makeNarAccessor(teeSource);
|
||||
compressionSink->finish();
|
||||
}
|
||||
|
||||
auto now2 = std::chrono::steady_clock::now();
|
||||
|
||||
auto narInfo = make_ref<NarInfo>(info);
|
||||
narInfo->narSize = info.narSize;
|
||||
narInfo->narHash = info.narHash;
|
||||
narInfo->compression = compression;
|
||||
auto [fileHash, fileSize] = fileHashSink.finish();
|
||||
narInfo->fileHash = fileHash;
|
||||
narInfo->fileSize = fileSize;
|
||||
narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar"
|
||||
+ (compression == "xz" ? ".xz" :
|
||||
compression == "bzip2" ? ".bz2" :
|
||||
compression == "br" ? ".br" :
|
||||
"");
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
|
||||
printMsg(lvlTalkative, "copying path '%1%' (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache",
|
||||
printStorePath(narInfo->path), info.narSize,
|
||||
((1.0 - (double) fileSize / info.narSize) * 100.0),
|
||||
duration);
|
||||
if (!repair && isValidPath(info.path)) return;
|
||||
|
||||
/* Verify that all references are valid. This may do some .narinfo
|
||||
reads, but typically they'll already be cached. */
|
||||
@@ -201,6 +129,23 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||
printStorePath(info.path), printStorePath(ref));
|
||||
}
|
||||
|
||||
assert(nar->compare(0, narMagic.size(), narMagic) == 0);
|
||||
|
||||
auto narInfo = make_ref<NarInfo>(info);
|
||||
|
||||
narInfo->narSize = nar->size();
|
||||
narInfo->narHash = hashString(htSHA256, *nar);
|
||||
|
||||
if (info.narHash && info.narHash != narInfo->narHash)
|
||||
throw Error("refusing to copy corrupted path '%1%' to binary cache", printStorePath(info.path));
|
||||
|
||||
auto accessor_ = std::dynamic_pointer_cast<RemoteFSAccessor>(accessor);
|
||||
|
||||
auto narAccessor = makeNarAccessor(nar);
|
||||
|
||||
if (accessor_)
|
||||
accessor_->addToCache(printStorePath(info.path), *nar, narAccessor);
|
||||
|
||||
/* Optionally write a JSON file containing a listing of the
|
||||
contents of the NAR. */
|
||||
if (writeNARListing) {
|
||||
@@ -212,13 +157,33 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||
|
||||
{
|
||||
auto res = jsonRoot.placeholder("root");
|
||||
listNar(res, ref<FSAccessor>(narAccessor), "", true);
|
||||
listNar(res, narAccessor, "", true);
|
||||
}
|
||||
}
|
||||
|
||||
upsertFile(std::string(info.path.to_string()) + ".ls", jsonOut.str(), "application/json");
|
||||
upsertFile(storePathToHash(printStorePath(info.path)) + ".ls", jsonOut.str(), "application/json");
|
||||
}
|
||||
|
||||
/* Compress the NAR. */
|
||||
narInfo->compression = compression;
|
||||
auto now1 = std::chrono::steady_clock::now();
|
||||
auto narCompressed = compress(compression, *nar, parallelCompression);
|
||||
auto now2 = std::chrono::steady_clock::now();
|
||||
narInfo->fileHash = hashString(htSHA256, *narCompressed);
|
||||
narInfo->fileSize = narCompressed->size();
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
|
||||
printMsg(lvlTalkative, "copying path '%1%' (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache",
|
||||
printStorePath(narInfo->path), narInfo->narSize,
|
||||
((1.0 - (double) narCompressed->size() / nar->size()) * 100.0),
|
||||
duration);
|
||||
|
||||
narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar"
|
||||
+ (compression == "xz" ? ".xz" :
|
||||
compression == "bzip2" ? ".bz2" :
|
||||
compression == "br" ? ".br" :
|
||||
"");
|
||||
|
||||
/* Optionally maintain an index of DWARF debug info files
|
||||
consisting of JSON files named 'debuginfo/<build-id>' that
|
||||
specify the NAR file and member containing the debug info. */
|
||||
@@ -279,14 +244,12 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||
/* Atomically write the NAR file. */
|
||||
if (repair || !fileExists(narInfo->url)) {
|
||||
stats.narWrite++;
|
||||
upsertFile(narInfo->url,
|
||||
std::make_shared<std::fstream>(fnTemp, std::ios_base::in),
|
||||
"application/x-nix-nar");
|
||||
upsertFile(narInfo->url, *narCompressed, "application/x-nix-nar");
|
||||
} else
|
||||
stats.narWriteAverted++;
|
||||
|
||||
stats.narWriteBytes += info.narSize;
|
||||
stats.narWriteCompressedBytes += fileSize;
|
||||
stats.narWriteBytes += nar->size();
|
||||
stats.narWriteCompressedBytes += narCompressed->size();
|
||||
stats.narWriteCompressionTimeMs += duration;
|
||||
|
||||
/* Atomically write the NAR info file.*/
|
||||
@@ -321,7 +284,7 @@ void BinaryCacheStore::narFromPath(const StorePath & storePath, Sink & sink)
|
||||
try {
|
||||
getFile(info->url, *decompressor);
|
||||
} catch (NoSuchBinaryCacheFile & e) {
|
||||
throw SubstituteGone(e.info());
|
||||
throw SubstituteGone(e.what());
|
||||
}
|
||||
|
||||
decompressor->finish();
|
||||
@@ -364,7 +327,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
|
||||
}
|
||||
|
||||
StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath,
|
||||
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
||||
{
|
||||
// FIXME: some cut&paste from LocalStore::addToStore().
|
||||
|
||||
@@ -373,7 +336,7 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
|
||||
small files. */
|
||||
StringSink sink;
|
||||
Hash h;
|
||||
if (method == FileIngestionMethod::Recursive) {
|
||||
if (recursive) {
|
||||
dumpPath(srcPath, sink, filter);
|
||||
h = hashString(hashAlgo, *sink.s);
|
||||
} else {
|
||||
@@ -382,10 +345,9 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
|
||||
h = hashString(hashAlgo, s);
|
||||
}
|
||||
|
||||
ValidPathInfo info(makeFixedOutputPath(method, h, name));
|
||||
ValidPathInfo info(makeFixedOutputPath(recursive, h, name));
|
||||
|
||||
auto source = StringSource { *sink.s };
|
||||
addToStore(info, source, repair, CheckSigs);
|
||||
addToStore(info, sink.s, repair, CheckSigs, nullptr);
|
||||
|
||||
return std::move(info.path);
|
||||
}
|
||||
@@ -394,13 +356,12 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
|
||||
const StorePathSet & references, RepairFlag repair)
|
||||
{
|
||||
ValidPathInfo info(computeStorePathForText(name, s, references));
|
||||
info.references = references;
|
||||
info.references = cloneStorePathSet(references);
|
||||
|
||||
if (repair || !isValidPath(info.path)) {
|
||||
StringSink sink;
|
||||
dumpString(s, sink);
|
||||
auto source = StringSource { *sink.s };
|
||||
addToStore(info, source, repair, CheckSigs);
|
||||
addToStore(info, sink.s, repair, CheckSigs, nullptr);
|
||||
}
|
||||
|
||||
return std::move(info.path);
|
||||
@@ -422,19 +383,21 @@ void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSe
|
||||
|
||||
narInfo->sigs.insert(sigs.begin(), sigs.end());
|
||||
|
||||
auto narInfoFile = narInfoFileFor(narInfo->path);
|
||||
|
||||
writeNarInfo(narInfo);
|
||||
}
|
||||
|
||||
std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
|
||||
{
|
||||
auto drvPath = path;
|
||||
auto drvPath = path.clone();
|
||||
|
||||
if (!path.isDerivation()) {
|
||||
try {
|
||||
auto info = queryPathInfo(path);
|
||||
// FIXME: add a "Log" field to .narinfo
|
||||
if (!info->deriver) return nullptr;
|
||||
drvPath = *info->deriver;
|
||||
drvPath = info->deriver->clone();
|
||||
} catch (InvalidPath &) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -36,13 +36,9 @@ public:
|
||||
virtual bool fileExists(const std::string & path) = 0;
|
||||
|
||||
virtual void upsertFile(const std::string & path,
|
||||
std::shared_ptr<std::basic_iostream<char>> istream,
|
||||
const std::string & data,
|
||||
const std::string & mimeType) = 0;
|
||||
|
||||
void upsertFile(const std::string & path,
|
||||
std::string && data,
|
||||
const std::string & mimeType);
|
||||
|
||||
/* Note: subclasses must implement at least one of the two
|
||||
following getFile() methods. */
|
||||
|
||||
@@ -78,11 +74,12 @@ public:
|
||||
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
|
||||
{ unsupported("queryPathFromHashPart"); }
|
||||
|
||||
void addToStore(const ValidPathInfo & info, Source & narSource,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
||||
void addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs,
|
||||
std::shared_ptr<FSAccessor> accessor) override;
|
||||
|
||||
StorePath addToStore(const string & name, const Path & srcPath,
|
||||
FileIngestionMethod method, HashType hashAlgo,
|
||||
bool recursive, HashType hashAlgo,
|
||||
PathFilter & filter, RepairFlag repair) override;
|
||||
|
||||
StorePath addTextToStore(const string & name, const string & s,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,10 +22,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
srcFiles = readDirectory(srcDir);
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo == ENOTDIR) {
|
||||
logWarning({
|
||||
.name = "Create links - directory",
|
||||
.hint = hintfmt("not including '%s' in the user environment because it's not a directory", srcDir)
|
||||
});
|
||||
printError("warning: not including '%s' in the user environment because it's not a directory", srcDir);
|
||||
return;
|
||||
}
|
||||
throw;
|
||||
@@ -44,10 +41,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
throw SysError("getting status of '%1%'", srcFile);
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo == ENOENT || e.errNo == ENOTDIR) {
|
||||
logWarning({
|
||||
.name = "Create links - skipping symlink",
|
||||
.hint = hintfmt("skipping dangling symlink '%s'", dstFile)
|
||||
});
|
||||
printError("warning: skipping dangling symlink '%s'", dstFile);
|
||||
continue;
|
||||
}
|
||||
throw;
|
||||
@@ -78,15 +72,15 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
if (!S_ISDIR(lstat(target).st_mode))
|
||||
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
|
||||
if (unlink(dstFile.c_str()) == -1)
|
||||
throw SysError("unlinking '%1%'", dstFile);
|
||||
throw SysError(format("unlinking '%1%'") % dstFile);
|
||||
if (mkdir(dstFile.c_str(), 0755) == -1)
|
||||
throw SysError("creating directory '%1%'", dstFile);
|
||||
throw SysError(format("creating directory '%1%'"));
|
||||
createLinks(state, target, dstFile, state.priorities[dstFile]);
|
||||
createLinks(state, srcFile, dstFile, priority);
|
||||
continue;
|
||||
}
|
||||
} else if (errno != ENOENT)
|
||||
throw SysError("getting status of '%1%'", dstFile);
|
||||
throw SysError(format("getting status of '%1%'") % dstFile);
|
||||
}
|
||||
|
||||
else {
|
||||
@@ -105,11 +99,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
if (prevPriority < priority)
|
||||
continue;
|
||||
if (unlink(dstFile.c_str()) == -1)
|
||||
throw SysError("unlinking '%1%'", dstFile);
|
||||
throw SysError(format("unlinking '%1%'") % dstFile);
|
||||
} else if (S_ISDIR(dstSt.st_mode))
|
||||
throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile);
|
||||
} else if (errno != ENOENT)
|
||||
throw SysError("getting status of '%1%'", dstFile);
|
||||
throw SysError(format("getting status of '%1%'") % dstFile);
|
||||
}
|
||||
|
||||
createSymlink(srcFile, dstFile);
|
||||
|
||||
@@ -18,7 +18,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||
|
||||
auto getAttr = [&](const string & name) {
|
||||
auto i = drv.env.find(name);
|
||||
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
|
||||
if (i == drv.env.end()) throw Error(format("attribute '%s' missing") % name);
|
||||
return i->second;
|
||||
};
|
||||
|
||||
@@ -54,21 +54,18 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||
auto executable = drv.env.find("executable");
|
||||
if (executable != drv.env.end() && executable->second == "1") {
|
||||
if (chmod(storePath.c_str(), 0755) == -1)
|
||||
throw SysError("making '%1%' executable", storePath);
|
||||
throw SysError(format("making '%1%' executable") % storePath);
|
||||
}
|
||||
};
|
||||
|
||||
/* We always have one output, and if it's a fixed-output derivation (as
|
||||
checked below) it must be the only output */
|
||||
auto & output = drv.outputs.begin()->second;
|
||||
|
||||
/* Try the hashed mirrors first. */
|
||||
if (output.hash && output.hash->method == FileIngestionMethod::Flat)
|
||||
if (getAttr("outputHashMode") == "flat")
|
||||
for (auto hashedMirror : settings.hashedMirrors.get())
|
||||
try {
|
||||
if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/';
|
||||
auto & h = output.hash->hash;
|
||||
fetch(hashedMirror + printHashType(*h.type) + "/" + h.to_string(Base16, false));
|
||||
auto ht = parseHashType(getAttr("outputHashAlgo"));
|
||||
auto h = Hash(getAttr("outputHash"), ht);
|
||||
fetch(hashedMirror + printHashType(h.type) + "/" + h.to_string(Base16, false));
|
||||
return;
|
||||
} catch (Error & e) {
|
||||
debug(e.what());
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
#include "content-address.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::string FixedOutputHash::printMethodAlgo() const {
|
||||
return makeFileIngestionPrefix(method) + printHashType(*hash.type);
|
||||
}
|
||||
|
||||
std::string makeFileIngestionPrefix(const FileIngestionMethod m) {
|
||||
switch (m) {
|
||||
case FileIngestionMethod::Flat:
|
||||
return "";
|
||||
case FileIngestionMethod::Recursive:
|
||||
return "r:";
|
||||
default:
|
||||
throw Error("impossible, caught both cases");
|
||||
}
|
||||
}
|
||||
|
||||
std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash)
|
||||
{
|
||||
return "fixed:"
|
||||
+ makeFileIngestionPrefix(method)
|
||||
+ hash.to_string(Base32, true);
|
||||
}
|
||||
|
||||
// FIXME Put this somewhere?
|
||||
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
||||
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
std::string renderContentAddress(ContentAddress ca) {
|
||||
return std::visit(overloaded {
|
||||
[](TextHash th) {
|
||||
return "text:" + th.hash.to_string(Base32, true);
|
||||
},
|
||||
[](FixedOutputHash fsh) {
|
||||
return makeFixedOutputCA(fsh.method, fsh.hash);
|
||||
}
|
||||
}, ca);
|
||||
}
|
||||
|
||||
ContentAddress parseContentAddress(std::string_view rawCa) {
|
||||
auto prefixSeparator = rawCa.find(':');
|
||||
if (prefixSeparator != string::npos) {
|
||||
auto prefix = string(rawCa, 0, prefixSeparator);
|
||||
if (prefix == "text") {
|
||||
auto hashTypeAndHash = rawCa.substr(prefixSeparator+1, string::npos);
|
||||
Hash hash = Hash(string(hashTypeAndHash));
|
||||
if (*hash.type != htSHA256) {
|
||||
throw Error("parseContentAddress: the text hash should have type SHA256");
|
||||
}
|
||||
return TextHash { hash };
|
||||
} else if (prefix == "fixed") {
|
||||
// This has to be an inverse of makeFixedOutputCA
|
||||
auto methodAndHash = rawCa.substr(prefixSeparator+1, string::npos);
|
||||
if (methodAndHash.substr(0,2) == "r:") {
|
||||
std::string_view hashRaw = methodAndHash.substr(2,string::npos);
|
||||
return FixedOutputHash {
|
||||
.method = FileIngestionMethod::Recursive,
|
||||
.hash = Hash(string(hashRaw)),
|
||||
};
|
||||
} else {
|
||||
std::string_view hashRaw = methodAndHash;
|
||||
return FixedOutputHash {
|
||||
.method = FileIngestionMethod::Flat,
|
||||
.hash = Hash(string(hashRaw)),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
throw Error("parseContentAddress: format not recognized; has to be text or fixed");
|
||||
}
|
||||
} else {
|
||||
throw Error("Not a content address because it lacks an appropriate prefix");
|
||||
}
|
||||
};
|
||||
|
||||
std::optional<ContentAddress> parseContentAddressOpt(std::string_view rawCaOpt) {
|
||||
return rawCaOpt == "" ? std::optional<ContentAddress> {} : parseContentAddress(rawCaOpt);
|
||||
};
|
||||
|
||||
std::string renderContentAddress(std::optional<ContentAddress> ca) {
|
||||
return ca ? renderContentAddress(*ca) : "";
|
||||
}
|
||||
|
||||
Hash getContentAddressHash(const ContentAddress & ca)
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[](TextHash th) {
|
||||
return th.hash;
|
||||
},
|
||||
[](FixedOutputHash fsh) {
|
||||
return fsh.hash;
|
||||
}
|
||||
}, ca);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
#include "hash.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
enum struct FileIngestionMethod : uint8_t {
|
||||
Flat = false,
|
||||
Recursive = true
|
||||
};
|
||||
|
||||
struct TextHash {
|
||||
Hash hash;
|
||||
};
|
||||
|
||||
/// Pair of a hash, and how the file system was ingested
|
||||
struct FixedOutputHash {
|
||||
FileIngestionMethod method;
|
||||
Hash hash;
|
||||
std::string printMethodAlgo() const;
|
||||
};
|
||||
|
||||
/*
|
||||
We've accumulated several types of content-addressed paths over the years;
|
||||
fixed-output derivations support multiple hash algorithms and serialisation
|
||||
methods (flat file vs NAR). Thus, ‘ca’ has one of the following forms:
|
||||
|
||||
* ‘text:sha256:<sha256 hash of file contents>’: For paths
|
||||
computed by makeTextPath() / addTextToStore().
|
||||
|
||||
* ‘fixed:<r?>:<ht>:<h>’: For paths computed by
|
||||
makeFixedOutputPath() / addToStore().
|
||||
*/
|
||||
typedef std::variant<
|
||||
TextHash, // for paths computed by makeTextPath() / addTextToStore
|
||||
FixedOutputHash // for path computed by makeFixedOutputPath
|
||||
> ContentAddress;
|
||||
|
||||
/* Compute the prefix to the hash algorithm which indicates how the files were
|
||||
ingested. */
|
||||
std::string makeFileIngestionPrefix(const FileIngestionMethod m);
|
||||
|
||||
/* Compute the content-addressability assertion (ValidPathInfo::ca)
|
||||
for paths created by makeFixedOutputPath() / addToStore(). */
|
||||
std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash);
|
||||
|
||||
std::string renderContentAddress(ContentAddress ca);
|
||||
|
||||
std::string renderContentAddress(std::optional<ContentAddress> ca);
|
||||
|
||||
ContentAddress parseContentAddress(std::string_view rawCa);
|
||||
|
||||
std::optional<ContentAddress> parseContentAddressOpt(std::string_view rawCaOpt);
|
||||
|
||||
Hash getContentAddressHash(const ContentAddress & ca);
|
||||
|
||||
}
|
||||
@@ -73,18 +73,6 @@ struct TunnelLogger : public Logger
|
||||
enqueueMsg(*buf.s);
|
||||
}
|
||||
|
||||
void logEI(const ErrorInfo & ei) override
|
||||
{
|
||||
if (ei.level > verbosity) return;
|
||||
|
||||
std::stringstream oss;
|
||||
showErrorInfo(oss, ei, false);
|
||||
|
||||
StringSink buf;
|
||||
buf << STDERR_NEXT << oss.str();
|
||||
enqueueMsg(*buf.s);
|
||||
}
|
||||
|
||||
/* startWork() means that we're starting an operation for which we
|
||||
want to send out stderr to the client. */
|
||||
void startWork()
|
||||
@@ -293,7 +281,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
auto path = store->parseStorePath(readString(from));
|
||||
logger->startWork();
|
||||
StorePathSet paths; // FIXME
|
||||
paths.insert(path);
|
||||
paths.insert(path.clone());
|
||||
auto res = store->querySubstitutablePaths(paths);
|
||||
logger->stopWork();
|
||||
to << (res.count(path) != 0);
|
||||
@@ -327,7 +315,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
StorePathSet paths;
|
||||
if (op == wopQueryReferences)
|
||||
for (auto & i : store->queryPathInfo(path)->references)
|
||||
paths.insert(i);
|
||||
paths.insert(i.clone());
|
||||
else if (op == wopQueryReferrers)
|
||||
store->queryReferrers(path, paths);
|
||||
else if (op == wopQueryValidDerivers)
|
||||
@@ -341,21 +329,13 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
case wopQueryDerivationOutputNames: {
|
||||
auto path = store->parseStorePath(readString(from));
|
||||
logger->startWork();
|
||||
auto names = store->readDerivation(path).outputNames();
|
||||
StringSet names;
|
||||
names = store->queryDerivationOutputNames(path);
|
||||
logger->stopWork();
|
||||
to << names;
|
||||
break;
|
||||
}
|
||||
|
||||
case wopQueryDerivationOutputMap: {
|
||||
auto path = store->parseStorePath(readString(from));
|
||||
logger->startWork();
|
||||
OutputPathMap outputs = store->queryDerivationOutputMap(path);
|
||||
logger->stopWork();
|
||||
writeOutputPathMap(*store, to, outputs);
|
||||
break;
|
||||
}
|
||||
|
||||
case wopQueryDeriver: {
|
||||
auto path = store->parseStorePath(readString(from));
|
||||
logger->startWork();
|
||||
@@ -375,43 +355,32 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
}
|
||||
|
||||
case wopAddToStore: {
|
||||
bool fixed, recursive;
|
||||
std::string s, baseName;
|
||||
FileIngestionMethod method;
|
||||
{
|
||||
bool fixed; uint8_t recursive;
|
||||
from >> baseName >> fixed /* obsolete */ >> recursive >> s;
|
||||
if (recursive > (uint8_t) FileIngestionMethod::Recursive)
|
||||
throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive);
|
||||
method = FileIngestionMethod { recursive };
|
||||
/* Compatibility hack. */
|
||||
if (!fixed) {
|
||||
s = "sha256";
|
||||
method = FileIngestionMethod::Recursive;
|
||||
}
|
||||
from >> baseName >> fixed /* obsolete */ >> recursive >> s;
|
||||
/* Compatibility hack. */
|
||||
if (!fixed) {
|
||||
s = "sha256";
|
||||
recursive = true;
|
||||
}
|
||||
HashType hashAlgo = parseHashType(s);
|
||||
|
||||
StringSink savedNAR;
|
||||
TeeSource savedNARSource(from, savedNAR);
|
||||
TeeSource savedNAR(from);
|
||||
RetrieveRegularNARSink savedRegular;
|
||||
|
||||
if (method == FileIngestionMethod::Recursive) {
|
||||
if (recursive) {
|
||||
/* Get the entire NAR dump from the client and save it to
|
||||
a string so that we can pass it to
|
||||
addToStoreFromDump(). */
|
||||
ParseSink sink; /* null sink; just parse the NAR */
|
||||
parseDump(sink, savedNARSource);
|
||||
parseDump(sink, savedNAR);
|
||||
} else
|
||||
parseDump(savedRegular, from);
|
||||
|
||||
logger->startWork();
|
||||
if (!savedRegular.regular) throw Error("regular file expected");
|
||||
|
||||
auto path = store->addToStoreFromDump(
|
||||
method == FileIngestionMethod::Recursive ? *savedNAR.s : savedRegular.s,
|
||||
baseName,
|
||||
method,
|
||||
hashAlgo);
|
||||
auto path = store->addToStoreFromDump(recursive ? *savedNAR.data : savedRegular.s, baseName, recursive, hashAlgo);
|
||||
logger->stopWork();
|
||||
|
||||
to << store->printStorePath(path);
|
||||
@@ -443,7 +412,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
case wopImportPaths: {
|
||||
logger->startWork();
|
||||
TunnelSource source(from, to);
|
||||
auto paths = store->importPaths(source,
|
||||
auto paths = store->importPaths(source, nullptr,
|
||||
trusted ? NoCheckSigs : CheckSigs);
|
||||
logger->stopWork();
|
||||
Strings paths2;
|
||||
@@ -603,7 +572,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
auto path = store->parseStorePath(readString(from));
|
||||
logger->startWork();
|
||||
SubstitutablePathInfos infos;
|
||||
store->querySubstitutablePathInfos({path}, infos);
|
||||
StorePathSet paths;
|
||||
paths.insert(path.clone()); // FIXME
|
||||
store->querySubstitutablePathInfos(paths, infos);
|
||||
logger->stopWork();
|
||||
auto i = infos.find(path);
|
||||
if (i == infos.end())
|
||||
@@ -662,7 +633,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
|
||||
to << info->ultimate
|
||||
<< info->sigs
|
||||
<< renderContentAddress(info->ca);
|
||||
<< info->ca;
|
||||
}
|
||||
} else {
|
||||
assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
|
||||
@@ -720,8 +691,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
info.references = readStorePaths<StorePathSet>(*store, from);
|
||||
from >> info.registrationTime >> info.narSize >> info.ultimate;
|
||||
info.sigs = readStrings<StringSet>(from);
|
||||
info.ca = parseContentAddressOpt(readString(from));
|
||||
from >> repair >> dontCheckSigs;
|
||||
from >> info.ca >> repair >> dontCheckSigs;
|
||||
if (!trusted && dontCheckSigs)
|
||||
dontCheckSigs = false;
|
||||
if (!trusted)
|
||||
@@ -732,9 +702,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
|
||||
source = std::make_unique<TunnelSource>(from, to);
|
||||
else {
|
||||
TeeParseSink tee(from);
|
||||
TeeSink tee(from);
|
||||
parseDump(tee, tee.source);
|
||||
saved = std::move(*tee.saved.s);
|
||||
saved = std::move(*tee.source.data);
|
||||
source = std::make_unique<StringSource>(saved);
|
||||
}
|
||||
|
||||
@@ -742,7 +712,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
|
||||
// FIXME: race if addToStore doesn't read source?
|
||||
store->addToStore(info, *source, (RepairFlag) repair,
|
||||
dontCheckSigs ? NoCheckSigs : CheckSigs);
|
||||
dontCheckSigs ? NoCheckSigs : CheckSigs, nullptr);
|
||||
|
||||
logger->stopWork();
|
||||
break;
|
||||
@@ -765,7 +735,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||
}
|
||||
|
||||
default:
|
||||
throw Error("invalid operation %1%", op);
|
||||
throw Error(format("invalid operation %1%") % op);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,51 @@
|
||||
#include "util.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "fs-accessor.hh"
|
||||
#include "istringstream_nocopy.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
void DerivationOutput::parseHashInfo(bool & recursive, Hash & hash) const
|
||||
{
|
||||
recursive = false;
|
||||
string algo = hashAlgo;
|
||||
|
||||
if (string(algo, 0, 2) == "r:") {
|
||||
recursive = true;
|
||||
algo = string(algo, 2);
|
||||
}
|
||||
|
||||
HashType hashType = parseHashType(algo);
|
||||
if (hashType == htUnknown)
|
||||
throw Error("unknown hash algorithm '%s'", algo);
|
||||
|
||||
hash = Hash(this->hash, hashType);
|
||||
}
|
||||
|
||||
|
||||
BasicDerivation::BasicDerivation(const BasicDerivation & other)
|
||||
: platform(other.platform)
|
||||
, builder(other.builder)
|
||||
, args(other.args)
|
||||
, env(other.env)
|
||||
{
|
||||
for (auto & i : other.outputs)
|
||||
outputs.insert_or_assign(i.first,
|
||||
DerivationOutput(i.second.path.clone(), std::string(i.second.hashAlgo), std::string(i.second.hash)));
|
||||
for (auto & i : other.inputSrcs)
|
||||
inputSrcs.insert(i.clone());
|
||||
}
|
||||
|
||||
|
||||
Derivation::Derivation(const Derivation & other)
|
||||
: BasicDerivation(other)
|
||||
{
|
||||
for (auto & i : other.inputDrvs)
|
||||
inputDrvs.insert_or_assign(i.first.clone(), i.second);
|
||||
}
|
||||
|
||||
|
||||
const StorePath & BasicDerivation::findOutput(const string & id) const
|
||||
{
|
||||
auto i = outputs.find(id);
|
||||
@@ -25,9 +67,9 @@ bool BasicDerivation::isBuiltin() const
|
||||
StorePath writeDerivation(ref<Store> store,
|
||||
const Derivation & drv, std::string_view name, RepairFlag repair)
|
||||
{
|
||||
auto references = drv.inputSrcs;
|
||||
auto references = cloneStorePathSet(drv.inputSrcs);
|
||||
for (auto & i : drv.inputDrvs)
|
||||
references.insert(i.first);
|
||||
references.insert(i.first.clone());
|
||||
/* Note that the outputs of a derivation are *not* references
|
||||
(that can be missing (of course) and should not necessarily be
|
||||
held during a garbage collection). */
|
||||
@@ -45,7 +87,7 @@ static void expect(std::istream & str, const string & s)
|
||||
char s2[s.size()];
|
||||
str.read(s2, s.size());
|
||||
if (string(s2, s.size()) != s)
|
||||
throw FormatError("expected string '%1%'", s);
|
||||
throw FormatError(format("expected string '%1%'") % s);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +114,7 @@ static Path parsePath(std::istream & str)
|
||||
{
|
||||
string s = parseString(str);
|
||||
if (s.size() == 0 || s[0] != '/')
|
||||
throw FormatError("bad path '%1%' in derivation", s);
|
||||
throw FormatError(format("bad path '%1%' in derivation") % s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -100,45 +142,20 @@ static StringSet parseStrings(std::istream & str, bool arePaths)
|
||||
}
|
||||
|
||||
|
||||
static DerivationOutput parseDerivationOutput(const Store & store, std::istringstream & str)
|
||||
{
|
||||
expect(str, ","); auto path = store.parseStorePath(parsePath(str));
|
||||
expect(str, ","); auto hashAlgo = parseString(str);
|
||||
expect(str, ","); const auto hash = parseString(str);
|
||||
expect(str, ")");
|
||||
|
||||
std::optional<FixedOutputHash> fsh;
|
||||
if (hashAlgo != "") {
|
||||
auto method = FileIngestionMethod::Flat;
|
||||
if (string(hashAlgo, 0, 2) == "r:") {
|
||||
method = FileIngestionMethod::Recursive;
|
||||
hashAlgo = string(hashAlgo, 2);
|
||||
}
|
||||
const HashType hashType = parseHashType(hashAlgo);
|
||||
fsh = FixedOutputHash {
|
||||
.method = std::move(method),
|
||||
.hash = Hash(hash, hashType),
|
||||
};
|
||||
}
|
||||
|
||||
return DerivationOutput {
|
||||
.path = std::move(path),
|
||||
.hash = std::move(fsh),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
static Derivation parseDerivation(const Store & store, std::string && s)
|
||||
static Derivation parseDerivation(const Store & store, const string & s)
|
||||
{
|
||||
Derivation drv;
|
||||
std::istringstream str(std::move(s));
|
||||
istringstream_nocopy str(s);
|
||||
expect(str, "Derive([");
|
||||
|
||||
/* Parse the list of outputs. */
|
||||
while (!endOfList(str)) {
|
||||
expect(str, "("); std::string id = parseString(str);
|
||||
auto output = parseDerivationOutput(store, str);
|
||||
drv.outputs.emplace(std::move(id), std::move(output));
|
||||
expect(str, ","); auto path = store.parseStorePath(parsePath(str));
|
||||
expect(str, ","); auto hashAlgo = parseString(str);
|
||||
expect(str, ","); auto hash = parseString(str);
|
||||
expect(str, ")");
|
||||
drv.outputs.emplace(id, DerivationOutput(std::move(path), std::move(hashAlgo), std::move(hash)));
|
||||
}
|
||||
|
||||
/* Parse the list of input derivations. */
|
||||
@@ -179,7 +196,7 @@ Derivation readDerivation(const Store & store, const Path & drvPath)
|
||||
try {
|
||||
return parseDerivation(store, readFile(drvPath));
|
||||
} catch (FormatError & e) {
|
||||
throw Error("error parsing derivation '%1%': %2%", drvPath, e.msg());
|
||||
throw Error(format("error parsing derivation '%1%': %2%") % drvPath % e.msg());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,12 +204,6 @@ Derivation readDerivation(const Store & store, const Path & drvPath)
|
||||
Derivation Store::derivationFromPath(const StorePath & drvPath)
|
||||
{
|
||||
ensurePath(drvPath);
|
||||
return readDerivation(drvPath);
|
||||
}
|
||||
|
||||
|
||||
Derivation Store::readDerivation(const StorePath & drvPath)
|
||||
{
|
||||
auto accessor = getFSAccessor();
|
||||
try {
|
||||
return parseDerivation(*this, accessor->readFile(printStorePath(drvPath)));
|
||||
@@ -264,9 +275,8 @@ string Derivation::unparse(const Store & store, bool maskOutputs,
|
||||
if (first) first = false; else s += ',';
|
||||
s += '('; printUnquotedString(s, i.first);
|
||||
s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(i.second.path));
|
||||
s += ','; printUnquotedString(s, i.second.hash ? i.second.hash->printMethodAlgo() : "");
|
||||
s += ','; printUnquotedString(s,
|
||||
i.second.hash ? i.second.hash->hash.to_string(Base16, false) : "");
|
||||
s += ','; printUnquotedString(s, i.second.hashAlgo);
|
||||
s += ','; printUnquotedString(s, i.second.hash);
|
||||
s += ')';
|
||||
}
|
||||
|
||||
@@ -322,7 +332,7 @@ bool BasicDerivation::isFixedOutput() const
|
||||
{
|
||||
return outputs.size() == 1 &&
|
||||
outputs.begin()->first == "out" &&
|
||||
outputs.begin()->second.hash;
|
||||
outputs.begin()->second.hash != "";
|
||||
}
|
||||
|
||||
|
||||
@@ -355,8 +365,8 @@ Hash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutput
|
||||
if (drv.isFixedOutput()) {
|
||||
DerivationOutputs::const_iterator i = drv.outputs.begin();
|
||||
return hashString(htSHA256, "fixed:out:"
|
||||
+ i->second.hash->printMethodAlgo() + ":"
|
||||
+ i->second.hash->hash.to_string(Base16, false) + ":"
|
||||
+ i->second.hashAlgo + ":"
|
||||
+ i->second.hash + ":"
|
||||
+ store.printStorePath(i->second.path));
|
||||
}
|
||||
|
||||
@@ -367,8 +377,8 @@ Hash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutput
|
||||
auto h = drvHashes.find(i.first);
|
||||
if (h == drvHashes.end()) {
|
||||
assert(store.isValidPath(i.first));
|
||||
h = drvHashes.insert_or_assign(i.first, hashDerivationModulo(store,
|
||||
store.readDerivation(i.first), false)).first;
|
||||
h = drvHashes.insert_or_assign(i.first.clone(), hashDerivationModulo(store,
|
||||
readDerivation(store, store.toRealPath(i.first)), false)).first;
|
||||
}
|
||||
inputs2.insert_or_assign(h->second.to_string(Base16, false), i.second);
|
||||
}
|
||||
@@ -395,44 +405,10 @@ StorePathSet BasicDerivation::outputPaths() const
|
||||
{
|
||||
StorePathSet paths;
|
||||
for (auto & i : outputs)
|
||||
paths.insert(i.second.path);
|
||||
paths.insert(i.second.path.clone());
|
||||
return paths;
|
||||
}
|
||||
|
||||
static DerivationOutput readDerivationOutput(Source & in, const Store & store)
|
||||
{
|
||||
auto path = store.parseStorePath(readString(in));
|
||||
auto hashAlgo = readString(in);
|
||||
auto hash = readString(in);
|
||||
|
||||
std::optional<FixedOutputHash> fsh;
|
||||
if (hashAlgo != "") {
|
||||
auto method = FileIngestionMethod::Flat;
|
||||
if (string(hashAlgo, 0, 2) == "r:") {
|
||||
method = FileIngestionMethod::Recursive;
|
||||
hashAlgo = string(hashAlgo, 2);
|
||||
}
|
||||
auto hashType = parseHashType(hashAlgo);
|
||||
fsh = FixedOutputHash {
|
||||
.method = std::move(method),
|
||||
.hash = Hash(hash, hashType),
|
||||
};
|
||||
}
|
||||
|
||||
return DerivationOutput {
|
||||
.path = std::move(path),
|
||||
.hash = std::move(fsh),
|
||||
};
|
||||
}
|
||||
|
||||
StringSet BasicDerivation::outputNames() const
|
||||
{
|
||||
StringSet names;
|
||||
for (auto & i : outputs)
|
||||
names.insert(i.first);
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv)
|
||||
{
|
||||
@@ -440,8 +416,10 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv)
|
||||
auto nr = readNum<size_t>(in);
|
||||
for (size_t n = 0; n < nr; n++) {
|
||||
auto name = readString(in);
|
||||
auto output = readDerivationOutput(in, store);
|
||||
drv.outputs.emplace(std::move(name), std::move(output));
|
||||
auto path = store.parseStorePath(readString(in));
|
||||
auto hashAlgo = readString(in);
|
||||
auto hash = readString(in);
|
||||
drv.outputs.emplace(name, DerivationOutput(std::move(path), std::move(hashAlgo), std::move(hash)));
|
||||
}
|
||||
|
||||
drv.inputSrcs = readStorePaths<StorePathSet>(store, in);
|
||||
@@ -462,16 +440,8 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv)
|
||||
void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv)
|
||||
{
|
||||
out << drv.outputs.size();
|
||||
for (auto & i : drv.outputs) {
|
||||
out << i.first
|
||||
<< store.printStorePath(i.second.path);
|
||||
if (i.second.hash) {
|
||||
out << i.second.hash->printMethodAlgo()
|
||||
<< i.second.hash->hash.to_string(Base16, false);
|
||||
} else {
|
||||
out << "" << "";
|
||||
}
|
||||
}
|
||||
for (auto & i : drv.outputs)
|
||||
out << i.first << store.printStorePath(i.second.path) << i.second.hashAlgo << i.second.hash;
|
||||
writeStorePaths(store, out, drv.inputSrcs);
|
||||
out << drv.platform << drv.builder << drv.args;
|
||||
out << drv.env.size();
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "path.hh"
|
||||
#include "types.hh"
|
||||
#include "hash.hh"
|
||||
#include "content-address.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -16,7 +15,14 @@ namespace nix {
|
||||
struct DerivationOutput
|
||||
{
|
||||
StorePath path;
|
||||
std::optional<FixedOutputHash> hash; /* hash used for expected hash computation */
|
||||
std::string hashAlgo; /* hash used for expected hash computation */
|
||||
std::string hash; /* expected hash, may be null */
|
||||
DerivationOutput(StorePath && path, std::string && hashAlgo, std::string && hash)
|
||||
: path(std::move(path))
|
||||
, hashAlgo(std::move(hashAlgo))
|
||||
, hash(std::move(hash))
|
||||
{ }
|
||||
void parseHashInfo(bool & recursive, Hash & hash) const;
|
||||
};
|
||||
|
||||
typedef std::map<string, DerivationOutput> DerivationOutputs;
|
||||
@@ -37,6 +43,7 @@ struct BasicDerivation
|
||||
StringPairs env;
|
||||
|
||||
BasicDerivation() { }
|
||||
explicit BasicDerivation(const BasicDerivation & other);
|
||||
virtual ~BasicDerivation() { };
|
||||
|
||||
/* Return the path corresponding to the output identifier `id' in
|
||||
@@ -51,8 +58,6 @@ struct BasicDerivation
|
||||
/* Return the output paths of a derivation. */
|
||||
StorePathSet outputPaths() const;
|
||||
|
||||
/* Return the output names of a derivation. */
|
||||
StringSet outputNames() const;
|
||||
};
|
||||
|
||||
struct Derivation : BasicDerivation
|
||||
@@ -64,12 +69,13 @@ struct Derivation : BasicDerivation
|
||||
std::map<std::string, StringSet> * actualInputs = nullptr) const;
|
||||
|
||||
Derivation() { }
|
||||
Derivation(Derivation && other) = default;
|
||||
explicit Derivation(const Derivation & other);
|
||||
};
|
||||
|
||||
|
||||
class Store;
|
||||
|
||||
enum RepairFlag : bool { NoRepair = false, Repair = true };
|
||||
|
||||
/* Write a derivation to the Nix store, and return its path. */
|
||||
StorePath writeDerivation(ref<Store> store,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "serialise.hh"
|
||||
#include "store-api.hh"
|
||||
#include "archive.hh"
|
||||
#include "worker-protocol.hh"
|
||||
@@ -7,6 +6,24 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct HashAndWriteSink : Sink
|
||||
{
|
||||
Sink & writeSink;
|
||||
HashSink hashSink;
|
||||
HashAndWriteSink(Sink & writeSink) : writeSink(writeSink), hashSink(htSHA256)
|
||||
{
|
||||
}
|
||||
virtual void operator () (const unsigned char * data, size_t len)
|
||||
{
|
||||
writeSink(data, len);
|
||||
hashSink(data, len);
|
||||
}
|
||||
Hash currentHash()
|
||||
{
|
||||
return hashSink.currentHash().first;
|
||||
}
|
||||
};
|
||||
|
||||
void Store::exportPaths(const StorePathSet & paths, Sink & sink)
|
||||
{
|
||||
auto sorted = topoSortPaths(paths);
|
||||
@@ -29,29 +46,28 @@ void Store::exportPath(const StorePath & path, Sink & sink)
|
||||
{
|
||||
auto info = queryPathInfo(path);
|
||||
|
||||
HashSink hashSink(htSHA256);
|
||||
TeeSink teeSink(sink, hashSink);
|
||||
HashAndWriteSink hashAndWriteSink(sink);
|
||||
|
||||
narFromPath(path, teeSink);
|
||||
narFromPath(path, hashAndWriteSink);
|
||||
|
||||
/* Refuse to export paths that have changed. This prevents
|
||||
filesystem corruption from spreading to other machines.
|
||||
Don't complain if the stored hash is zero (unknown). */
|
||||
Hash hash = hashSink.currentHash().first;
|
||||
if (hash != info->narHash && info->narHash != Hash(*info->narHash.type))
|
||||
Hash hash = hashAndWriteSink.currentHash();
|
||||
if (hash != info->narHash && info->narHash != Hash(info->narHash.type))
|
||||
throw Error("hash of path '%s' has changed from '%s' to '%s'!",
|
||||
printStorePath(path), info->narHash.to_string(Base32, true), hash.to_string(Base32, true));
|
||||
printStorePath(path), info->narHash.to_string(), hash.to_string());
|
||||
|
||||
teeSink
|
||||
hashAndWriteSink
|
||||
<< exportMagic
|
||||
<< printStorePath(path);
|
||||
writeStorePaths(*this, teeSink, info->references);
|
||||
teeSink
|
||||
writeStorePaths(*this, hashAndWriteSink, info->references);
|
||||
hashAndWriteSink
|
||||
<< (info->deriver ? printStorePath(*info->deriver) : "")
|
||||
<< 0;
|
||||
}
|
||||
|
||||
StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
|
||||
StorePaths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor, CheckSigsFlag checkSigs)
|
||||
{
|
||||
StorePaths res;
|
||||
while (true) {
|
||||
@@ -60,7 +76,7 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
|
||||
if (n != 1) throw Error("input doesn't look like something created by 'nix-store --export'");
|
||||
|
||||
/* Extract the NAR from the source. */
|
||||
TeeParseSink tee(source);
|
||||
TeeSink tee(source);
|
||||
parseDump(tee, tee.source);
|
||||
|
||||
uint32_t magic = readInt(source);
|
||||
@@ -77,18 +93,16 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
|
||||
if (deriver != "")
|
||||
info.deriver = parseStorePath(deriver);
|
||||
|
||||
info.narHash = hashString(htSHA256, *tee.saved.s);
|
||||
info.narSize = tee.saved.s->size();
|
||||
info.narHash = hashString(htSHA256, *tee.source.data);
|
||||
info.narSize = tee.source.data->size();
|
||||
|
||||
// Ignore optional legacy signature.
|
||||
if (readInt(source) == 1)
|
||||
readString(source);
|
||||
|
||||
// Can't use underlying source, which would have been exhausted
|
||||
auto source = StringSource { *tee.saved.s };
|
||||
addToStore(info, source, NoRepair, checkSigs);
|
||||
addToStore(info, tee.source.data, NoRepair, checkSigs, accessor);
|
||||
|
||||
res.push_back(info.path);
|
||||
res.push_back(info.path.clone());
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
@@ -72,18 +72,6 @@ struct curlFileTransfer : public FileTransfer
|
||||
|
||||
curl_off_t writtenToSink = 0;
|
||||
|
||||
inline static const std::set<long> successfulStatuses {200, 201, 204, 206, 304, 0 /* other protocol */};
|
||||
/* Get the HTTP status code, or 0 for other protocols. */
|
||||
long getHTTPStatus()
|
||||
{
|
||||
long httpStatus = 0;
|
||||
long protocol = 0;
|
||||
curl_easy_getinfo(req, CURLINFO_PROTOCOL, &protocol);
|
||||
if (protocol == CURLPROTO_HTTP || protocol == CURLPROTO_HTTPS)
|
||||
curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus);
|
||||
return httpStatus;
|
||||
}
|
||||
|
||||
TransferItem(curlFileTransfer & fileTransfer,
|
||||
const FileTransferRequest & request,
|
||||
Callback<FileTransferResult> && callback)
|
||||
@@ -95,11 +83,12 @@ struct curlFileTransfer : public FileTransfer
|
||||
, callback(std::move(callback))
|
||||
, finalSink([this](const unsigned char * data, size_t len) {
|
||||
if (this->request.dataCallback) {
|
||||
auto httpStatus = getHTTPStatus();
|
||||
long httpStatus = 0;
|
||||
curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus);
|
||||
|
||||
/* Only write data to the sink if this is a
|
||||
successful response. */
|
||||
if (successfulStatuses.count(httpStatus)) {
|
||||
if (httpStatus == 0 || httpStatus == 200 || httpStatus == 201 || httpStatus == 206) {
|
||||
writtenToSink += len;
|
||||
this->request.dataCallback((char *) data, len);
|
||||
}
|
||||
@@ -123,7 +112,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
if (requestHeaders) curl_slist_free_all(requestHeaders);
|
||||
try {
|
||||
if (!done)
|
||||
fail(FileTransferError(Interrupted, "download of '%s' was interrupted", request.uri));
|
||||
fail(FileTransferError(Interrupted, format("download of '%s' was interrupted") % request.uri));
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
@@ -327,7 +316,8 @@ struct curlFileTransfer : public FileTransfer
|
||||
|
||||
void finish(CURLcode code)
|
||||
{
|
||||
auto httpStatus = getHTTPStatus();
|
||||
long httpStatus = 0;
|
||||
curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus);
|
||||
|
||||
char * effectiveUriCStr;
|
||||
curl_easy_getinfo(req, CURLINFO_EFFECTIVE_URL, &effectiveUriCStr);
|
||||
@@ -353,7 +343,8 @@ struct curlFileTransfer : public FileTransfer
|
||||
if (writeException)
|
||||
failEx(writeException);
|
||||
|
||||
else if (code == CURLE_OK && successfulStatuses.count(httpStatus))
|
||||
else if (code == CURLE_OK &&
|
||||
(httpStatus == 200 || httpStatus == 201 || httpStatus == 204 || httpStatus == 206 || httpStatus == 304 || httpStatus == 226 /* FTP */ || httpStatus == 0 /* other protocol */))
|
||||
{
|
||||
result.cached = httpStatus == 304;
|
||||
act.progress(result.bodySize, result.bodySize);
|
||||
@@ -526,7 +517,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
int running;
|
||||
CURLMcode mc = curl_multi_perform(curlm, &running);
|
||||
if (mc != CURLM_OK)
|
||||
throw nix::Error("unexpected error from curl_multi_perform(): %s", curl_multi_strerror(mc));
|
||||
throw nix::Error(format("unexpected error from curl_multi_perform(): %s") % curl_multi_strerror(mc));
|
||||
|
||||
/* Set the promises of any finished requests. */
|
||||
CURLMsg * msg;
|
||||
@@ -556,7 +547,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
vomit("download thread waiting for %d ms", sleepTimeMs);
|
||||
mc = curl_multi_wait(curlm, extraFDs, 1, sleepTimeMs, &numfds);
|
||||
if (mc != CURLM_OK)
|
||||
throw nix::Error("unexpected error from curl_multi_wait(): %s", curl_multi_strerror(mc));
|
||||
throw nix::Error(format("unexpected error from curl_multi_wait(): %s") % curl_multi_strerror(mc));
|
||||
|
||||
nextWakeup = std::chrono::steady_clock::time_point();
|
||||
|
||||
@@ -608,11 +599,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
workerThreadMain();
|
||||
} catch (nix::Interrupted & e) {
|
||||
} catch (std::exception & e) {
|
||||
logError({
|
||||
.name = "File transfer",
|
||||
.hint = hintfmt("unexpected error in download thread: %s",
|
||||
e.what())
|
||||
});
|
||||
printError("unexpected error in download thread: %s", e.what());
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -103,9 +103,8 @@ class FileTransferError : public Error
|
||||
{
|
||||
public:
|
||||
FileTransfer::Error error;
|
||||
template<typename... Args>
|
||||
FileTransferError(FileTransfer::Error error, const Args & ... args)
|
||||
: Error(args...), error(error)
|
||||
FileTransferError(FileTransfer::Error error, const FormatOrString & fs)
|
||||
: Error(fs), error(error)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -38,10 +38,10 @@ AutoCloseFD LocalStore::openGCLock(LockType lockType)
|
||||
|
||||
AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
|
||||
if (!fdGCLock)
|
||||
throw SysError("opening global GC lock '%1%'", fnGCLock);
|
||||
throw SysError(format("opening global GC lock '%1%'") % fnGCLock);
|
||||
|
||||
if (!lockFile(fdGCLock.get(), lockType, false)) {
|
||||
printInfo("waiting for the big garbage collector lock...");
|
||||
printError(format("waiting for the big garbage collector lock..."));
|
||||
lockFile(fdGCLock.get(), lockType, true);
|
||||
}
|
||||
|
||||
@@ -65,8 +65,8 @@ static void makeSymlink(const Path & link, const Path & target)
|
||||
|
||||
/* Atomically replace the old one. */
|
||||
if (rename(tempLink.c_str(), link.c_str()) == -1)
|
||||
throw SysError("cannot rename '%1%' to '%2%'",
|
||||
tempLink , link);
|
||||
throw SysError(format("cannot rename '%1%' to '%2%'")
|
||||
% tempLink % link);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,15 +91,15 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath,
|
||||
Path gcRoot(canonPath(_gcRoot));
|
||||
|
||||
if (isInStore(gcRoot))
|
||||
throw Error(
|
||||
throw Error(format(
|
||||
"creating a garbage collector root (%1%) in the Nix store is forbidden "
|
||||
"(are you running nix-build inside the store?)", gcRoot);
|
||||
"(are you running nix-build inside the store?)") % gcRoot);
|
||||
|
||||
if (indirect) {
|
||||
/* Don't clobber the link if it already exists and doesn't
|
||||
point to the Nix store. */
|
||||
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
|
||||
throw Error("cannot create symlink '%1%'; already exists", gcRoot);
|
||||
throw Error(format("cannot create symlink '%1%'; already exists") % gcRoot);
|
||||
makeSymlink(gcRoot, printStorePath(storePath));
|
||||
addIndirectRoot(gcRoot);
|
||||
}
|
||||
@@ -109,10 +109,10 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath,
|
||||
Path rootsDir = canonPath((format("%1%/%2%") % stateDir % gcRootsDir).str());
|
||||
|
||||
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
|
||||
throw Error(
|
||||
throw Error(format(
|
||||
"path '%1%' is not a valid garbage collector root; "
|
||||
"it's not in the directory '%2%'",
|
||||
gcRoot, rootsDir);
|
||||
"it's not in the directory '%2%'")
|
||||
% gcRoot % rootsDir);
|
||||
}
|
||||
|
||||
if (baseNameOf(gcRoot) == std::string(storePath.to_string()))
|
||||
@@ -128,13 +128,11 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath,
|
||||
gcroots directory. */
|
||||
if (settings.checkRootReachability) {
|
||||
auto roots = findRoots(false);
|
||||
if (roots[storePath].count(gcRoot) == 0)
|
||||
logWarning({
|
||||
.name = "GC root",
|
||||
.hint = hintfmt("warning: '%1%' is not in a directory where the garbage collector looks for roots; "
|
||||
"therefore, '%2%' might be removed by the garbage collector",
|
||||
gcRoot, printStorePath(storePath))
|
||||
});
|
||||
if (roots[storePath.clone()].count(gcRoot) == 0)
|
||||
printError(
|
||||
"warning: '%1%' is not in a directory where the garbage collector looks for roots; "
|
||||
"therefore, '%2%' might be removed by the garbage collector",
|
||||
gcRoot, printStorePath(storePath));
|
||||
}
|
||||
|
||||
/* Grab the global GC root, causing us to block while a GC is in
|
||||
@@ -172,7 +170,7 @@ void LocalStore::addTempRoot(const StorePath & path)
|
||||
way. */
|
||||
struct stat st;
|
||||
if (fstat(state->fdTempRoots.get(), &st) == -1)
|
||||
throw SysError("statting '%1%'", fnTempRoots);
|
||||
throw SysError(format("statting '%1%'") % fnTempRoots);
|
||||
if (st.st_size == 0) break;
|
||||
|
||||
/* The garbage collector deleted this file before we could
|
||||
@@ -218,7 +216,7 @@ void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
|
||||
if (!*fd) {
|
||||
/* It's okay if the file has disappeared. */
|
||||
if (errno == ENOENT) continue;
|
||||
throw SysError("opening temporary roots file '%1%'", path);
|
||||
throw SysError(format("opening temporary roots file '%1%'") % path);
|
||||
}
|
||||
|
||||
/* This should work, but doesn't, for some reason. */
|
||||
@@ -229,7 +227,7 @@ void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
|
||||
only succeed if the owning process has died. In that case
|
||||
we don't care about its temporary roots. */
|
||||
if (lockFile(fd->get(), ltWrite, false)) {
|
||||
printInfo("removing stale temporary roots file '%1%'", path);
|
||||
printError(format("removing stale temporary roots file '%1%'") % path);
|
||||
unlink(path.c_str());
|
||||
writeFull(fd->get(), "d");
|
||||
continue;
|
||||
@@ -262,13 +260,11 @@ void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
|
||||
void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
|
||||
{
|
||||
auto foundRoot = [&](const Path & path, const Path & target) {
|
||||
try {
|
||||
auto storePath = toStorePath(target).first;
|
||||
if (isValidPath(storePath))
|
||||
roots[std::move(storePath)].emplace(path);
|
||||
else
|
||||
printInfo("skipping invalid root from '%1%' to '%2%'", path, target);
|
||||
} catch (BadStorePath &) { }
|
||||
auto storePath = maybeParseStorePath(toStorePath(target));
|
||||
if (storePath && isValidPath(*storePath))
|
||||
roots[std::move(*storePath)].emplace(path);
|
||||
else
|
||||
printInfo("skipping invalid root from '%1%' to '%2%'", path, target);
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -407,7 +403,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||
if (!fdDir) {
|
||||
if (errno == ENOENT || errno == EACCES)
|
||||
continue;
|
||||
throw SysError("opening %1%", fdStr);
|
||||
throw SysError(format("opening %1%") % fdStr);
|
||||
}
|
||||
struct dirent * fd_ent;
|
||||
while (errno = 0, fd_ent = readdir(fdDir.get())) {
|
||||
@@ -417,7 +413,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||
if (errno) {
|
||||
if (errno == ESRCH)
|
||||
continue;
|
||||
throw SysError("iterating /proc/%1%/fd", ent->d_name);
|
||||
throw SysError(format("iterating /proc/%1%/fd") % ent->d_name);
|
||||
}
|
||||
fdDir.reset();
|
||||
|
||||
@@ -474,15 +470,15 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||
|
||||
for (auto & [target, links] : unchecked) {
|
||||
if (!isInStore(target)) continue;
|
||||
try {
|
||||
auto path = toStorePath(target).first;
|
||||
if (!isValidPath(path)) continue;
|
||||
debug("got additional root '%1%'", printStorePath(path));
|
||||
if (censor)
|
||||
roots[path].insert(censored);
|
||||
else
|
||||
roots[path].insert(links.begin(), links.end());
|
||||
} catch (BadStorePath &) { }
|
||||
Path pathS = toStorePath(target);
|
||||
if (!isStorePath(pathS)) continue;
|
||||
auto path = parseStorePath(pathS);
|
||||
if (!isValidPath(path)) continue;
|
||||
debug("got additional root '%1%'", pathS);
|
||||
if (censor)
|
||||
roots[path.clone()].insert(censored);
|
||||
else
|
||||
roots[path.clone()].insert(links.begin(), links.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,7 +541,7 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
struct stat st;
|
||||
if (lstat(realPath.c_str(), &st)) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError("getting status of %1%", realPath);
|
||||
throw SysError(format("getting status of %1%") % realPath);
|
||||
}
|
||||
|
||||
printInfo(format("deleting '%1%'") % path);
|
||||
@@ -563,10 +559,10 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
// size.
|
||||
try {
|
||||
if (chmod(realPath.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError("making '%1%' writable", realPath);
|
||||
throw SysError(format("making '%1%' writable") % realPath);
|
||||
Path tmp = trashDir + "/" + std::string(baseNameOf(path));
|
||||
if (rename(realPath.c_str(), tmp.c_str()))
|
||||
throw SysError("unable to rename '%1%' to '%2%'", realPath, tmp);
|
||||
throw SysError(format("unable to rename '%1%' to '%2%'") % realPath % tmp);
|
||||
state.bytesInvalidated += size;
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo == ENOSPC) {
|
||||
@@ -594,11 +590,11 @@ bool LocalStore::canReachRoot(GCState & state, StorePathSet & visited, const Sto
|
||||
|
||||
if (state.roots.count(path)) {
|
||||
debug("cannot delete '%1%' because it's a root", printStorePath(path));
|
||||
state.alive.insert(path);
|
||||
state.alive.insert(path.clone());
|
||||
return true;
|
||||
}
|
||||
|
||||
visited.insert(path);
|
||||
visited.insert(path.clone());
|
||||
|
||||
if (!isValidPath(path)) return false;
|
||||
|
||||
@@ -612,7 +608,7 @@ bool LocalStore::canReachRoot(GCState & state, StorePathSet & visited, const Sto
|
||||
if (state.gcKeepDerivations && path.isDerivation()) {
|
||||
for (auto & i : queryDerivationOutputs(path))
|
||||
if (isValidPath(i) && queryPathInfo(i)->deriver == path)
|
||||
incoming.insert(i);
|
||||
incoming.insert(i.clone());
|
||||
}
|
||||
|
||||
/* If keep-outputs is set, then don't delete this path if there
|
||||
@@ -620,13 +616,13 @@ bool LocalStore::canReachRoot(GCState & state, StorePathSet & visited, const Sto
|
||||
if (state.gcKeepOutputs) {
|
||||
auto derivers = queryValidDerivers(path);
|
||||
for (auto & i : derivers)
|
||||
incoming.insert(i);
|
||||
incoming.insert(i.clone());
|
||||
}
|
||||
|
||||
for (auto & i : incoming)
|
||||
if (i != path)
|
||||
if (canReachRoot(state, visited, i)) {
|
||||
state.alive.insert(path);
|
||||
state.alive.insert(path.clone());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -670,7 +666,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
‘nix-store --delete’ doesn't have the unexpected effect of
|
||||
recursing into derivations and outputs. */
|
||||
for (auto & i : visited)
|
||||
state.dead.insert(i);
|
||||
state.dead.insert(i.clone());
|
||||
if (state.shouldDelete)
|
||||
deletePathRecursive(state, path);
|
||||
}
|
||||
@@ -685,7 +681,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
void LocalStore::removeUnusedLinks(const GCState & state)
|
||||
{
|
||||
AutoCloseDir dir(opendir(linksDir.c_str()));
|
||||
if (!dir) throw SysError("opening directory '%1%'", linksDir);
|
||||
if (!dir) throw SysError(format("opening directory '%1%'") % linksDir);
|
||||
|
||||
long long actualSize = 0, unsharedSize = 0;
|
||||
|
||||
@@ -698,7 +694,7 @@ void LocalStore::removeUnusedLinks(const GCState & state)
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1)
|
||||
throw SysError("statting '%1%'", path);
|
||||
throw SysError(format("statting '%1%'") % path);
|
||||
|
||||
if (st.st_nlink != 1) {
|
||||
actualSize += st.st_size;
|
||||
@@ -709,14 +705,14 @@ void LocalStore::removeUnusedLinks(const GCState & state)
|
||||
printMsg(lvlTalkative, format("deleting unused link '%1%'") % path);
|
||||
|
||||
if (unlink(path.c_str()) == -1)
|
||||
throw SysError("deleting '%1%'", path);
|
||||
throw SysError(format("deleting '%1%'") % path);
|
||||
|
||||
state.results.bytesFreed += st.st_size;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(linksDir.c_str(), &st) == -1)
|
||||
throw SysError("statting '%1%'", linksDir);
|
||||
throw SysError(format("statting '%1%'") % linksDir);
|
||||
long long overhead = st.st_blocks * 512ULL;
|
||||
|
||||
printInfo(format("note: currently hard linking saves %.2f MiB")
|
||||
@@ -751,12 +747,12 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
|
||||
/* Find the roots. Since we've grabbed the GC lock, the set of
|
||||
permanent roots cannot increase now. */
|
||||
printInfo("finding garbage collector roots...");
|
||||
printError("finding garbage collector roots...");
|
||||
Roots rootMap;
|
||||
if (!options.ignoreLiveness)
|
||||
findRootsNoTemp(rootMap, true);
|
||||
|
||||
for (auto & i : rootMap) state.roots.insert(i.first);
|
||||
for (auto & i : rootMap) state.roots.insert(i.first.clone());
|
||||
|
||||
/* Read the temporary roots. This acquires read locks on all
|
||||
per-process temporary root files. So after this point no paths
|
||||
@@ -765,8 +761,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
Roots tempRoots;
|
||||
findTempRoots(fds, tempRoots, true);
|
||||
for (auto & root : tempRoots) {
|
||||
state.tempRoots.insert(root.first);
|
||||
state.roots.insert(root.first);
|
||||
state.tempRoots.insert(root.first.clone());
|
||||
state.roots.insert(root.first.clone());
|
||||
}
|
||||
|
||||
/* After this point the set of roots or temporary roots cannot
|
||||
@@ -803,14 +799,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
} else if (options.maxFreed > 0) {
|
||||
|
||||
if (state.shouldDelete)
|
||||
printInfo("deleting garbage...");
|
||||
printError("deleting garbage...");
|
||||
else
|
||||
printInfo("determining live/dead paths...");
|
||||
printError("determining live/dead paths...");
|
||||
|
||||
try {
|
||||
|
||||
AutoCloseDir dir(opendir(realStoreDir.c_str()));
|
||||
if (!dir) throw SysError("opening directory '%1%'", realStoreDir);
|
||||
if (!dir) throw SysError(format("opening directory '%1%'") % realStoreDir);
|
||||
|
||||
/* Read the store and immediately delete all paths that
|
||||
aren't valid. When using --max-freed etc., deleting
|
||||
@@ -872,7 +868,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
|
||||
/* Clean up the links directory. */
|
||||
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
|
||||
printInfo("deleting unused links...");
|
||||
printError("deleting unused links...");
|
||||
removeUnusedLinks(state);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ Settings::Settings()
|
||||
, nixLibexecDir(canonPath(getEnv("NIX_LIBEXEC_DIR").value_or(NIX_LIBEXEC_DIR)))
|
||||
, nixBinDir(canonPath(getEnv("NIX_BIN_DIR").value_or(NIX_BIN_DIR)))
|
||||
, nixManDir(canonPath(NIX_MAN_DIR))
|
||||
, nixDaemonSocketFile(canonPath(getEnv("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
||||
, nixDaemonSocketFile(canonPath(nixStateDir + DEFAULT_SOCKET_PATH))
|
||||
{
|
||||
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
|
||||
lockCPU = getEnv("NIX_AFFINITY_HACK") == "1";
|
||||
|
||||
@@ -196,6 +196,10 @@ public:
|
||||
/* Whether to lock the Nix client and worker to the same CPU. */
|
||||
bool lockCPU;
|
||||
|
||||
/* Whether to show a stack trace if Nix evaluation fails. */
|
||||
Setting<bool> showTrace{this, false, "show-trace",
|
||||
"Whether to show a stack trace on evaluation errors."};
|
||||
|
||||
Setting<SandboxMode> sandboxMode{this,
|
||||
#if __linux__
|
||||
smEnabled
|
||||
@@ -267,7 +271,7 @@ public:
|
||||
"listed in 'trusted-public-keys'."};
|
||||
|
||||
Setting<StringSet> extraPlatforms{this,
|
||||
std::string{SYSTEM} == "x86_64-linux" && !isWSL1() ? StringSet{"i686-linux"} : StringSet{},
|
||||
std::string{SYSTEM} == "x86_64-linux" ? StringSet{"i686-linux"} : StringSet{},
|
||||
"extra-platforms",
|
||||
"Additional platforms that can be built on the local system. "
|
||||
"These may be supported natively (e.g. armv7 on some aarch64 CPUs "
|
||||
@@ -365,9 +369,6 @@ public:
|
||||
|
||||
Setting<bool> warnDirty{this, true, "warn-dirty",
|
||||
"Whether to warn about dirty Git/Mercurial trees."};
|
||||
|
||||
Setting<size_t> narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size",
|
||||
"Maximum size of NARs before spilling them to disk."};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -100,11 +100,11 @@ protected:
|
||||
}
|
||||
|
||||
void upsertFile(const std::string & path,
|
||||
std::shared_ptr<std::basic_iostream<char>> istream,
|
||||
const std::string & data,
|
||||
const std::string & mimeType) override
|
||||
{
|
||||
auto req = FileTransferRequest(cacheUri + "/" + path);
|
||||
req.data = std::make_shared<string>(StreamToSourceAdapter(istream).drain());
|
||||
req.data = std::make_shared<string>(data); // FIXME: inefficient
|
||||
req.mimeType = mimeType;
|
||||
try {
|
||||
getFileTransfer()->upload(req);
|
||||
|
||||
@@ -114,7 +114,7 @@ struct LegacySSHStore : public Store
|
||||
if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 4) {
|
||||
auto s = readString(conn->from);
|
||||
info->narHash = s.empty() ? Hash() : Hash(s);
|
||||
info->ca = parseContentAddressOpt(readString(conn->from));
|
||||
conn->from >> info->ca;
|
||||
info->sigs = readStrings<StringSet>(conn->from);
|
||||
}
|
||||
|
||||
@@ -126,7 +126,8 @@ struct LegacySSHStore : public Store
|
||||
}
|
||||
|
||||
void addToStore(const ValidPathInfo & info, Source & source,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs) override
|
||||
RepairFlag repair, CheckSigsFlag checkSigs,
|
||||
std::shared_ptr<FSAccessor> accessor) override
|
||||
{
|
||||
debug("adding path '%s' to remote host '%s'", printStorePath(info.path), host);
|
||||
|
||||
@@ -145,7 +146,7 @@ struct LegacySSHStore : public Store
|
||||
<< info.narSize
|
||||
<< info.ultimate
|
||||
<< info.sigs
|
||||
<< renderContentAddress(info.ca);
|
||||
<< info.ca;
|
||||
try {
|
||||
copyNAR(source, conn->to);
|
||||
} catch (...) {
|
||||
@@ -194,7 +195,7 @@ struct LegacySSHStore : public Store
|
||||
{ unsupported("queryPathFromHashPart"); }
|
||||
|
||||
StorePath addToStore(const string & name, const Path & srcPath,
|
||||
FileIngestionMethod method, HashType hashAlgo,
|
||||
bool recursive, HashType hashAlgo,
|
||||
PathFilter & filter, RepairFlag repair) override
|
||||
{ unsupported("addToStore"); }
|
||||
|
||||
@@ -255,7 +256,7 @@ struct LegacySSHStore : public Store
|
||||
conn->to.flush();
|
||||
|
||||
for (auto & i : readStorePaths<StorePathSet>(*this, conn->from))
|
||||
out.insert(i);
|
||||
out.insert(i.clone());
|
||||
}
|
||||
|
||||
StorePathSet queryValidPaths(const StorePathSet & paths,
|
||||
|
||||
@@ -31,18 +31,8 @@ protected:
|
||||
bool fileExists(const std::string & path) override;
|
||||
|
||||
void upsertFile(const std::string & path,
|
||||
std::shared_ptr<std::basic_iostream<char>> istream,
|
||||
const std::string & mimeType) override
|
||||
{
|
||||
auto path2 = binaryCacheDir + "/" + path;
|
||||
Path tmp = path2 + ".tmp." + std::to_string(getpid());
|
||||
AutoDelete del(tmp, false);
|
||||
StreamToSourceAdapter source(istream);
|
||||
writeFile(tmp, source);
|
||||
if (rename(tmp.c_str(), path2.c_str()))
|
||||
throw SysError("renaming '%1%' to '%2%'", tmp, path2);
|
||||
del.cancel();
|
||||
}
|
||||
const std::string & data,
|
||||
const std::string & mimeType) override;
|
||||
|
||||
void getFile(const std::string & path, Sink & sink) override
|
||||
{
|
||||
@@ -62,9 +52,7 @@ protected:
|
||||
if (entry.name.size() != 40 ||
|
||||
!hasSuffix(entry.name, ".narinfo"))
|
||||
continue;
|
||||
paths.insert(parseStorePath(
|
||||
storeDir + "/" + entry.name.substr(0, entry.name.size() - 8)
|
||||
+ "-" + MissingName));
|
||||
paths.insert(parseStorePath(storeDir + "/" + entry.name.substr(0, entry.name.size() - 8)));
|
||||
}
|
||||
|
||||
return paths;
|
||||
@@ -80,11 +68,28 @@ void LocalBinaryCacheStore::init()
|
||||
BinaryCacheStore::init();
|
||||
}
|
||||
|
||||
static void atomicWrite(const Path & path, const std::string & s)
|
||||
{
|
||||
Path tmp = path + ".tmp." + std::to_string(getpid());
|
||||
AutoDelete del(tmp, false);
|
||||
writeFile(tmp, s);
|
||||
if (rename(tmp.c_str(), path.c_str()))
|
||||
throw SysError(format("renaming '%1%' to '%2%'") % tmp % path);
|
||||
del.cancel();
|
||||
}
|
||||
|
||||
bool LocalBinaryCacheStore::fileExists(const std::string & path)
|
||||
{
|
||||
return pathExists(binaryCacheDir + "/" + path);
|
||||
}
|
||||
|
||||
void LocalBinaryCacheStore::upsertFile(const std::string & path,
|
||||
const std::string & data,
|
||||
const std::string & mimeType)
|
||||
{
|
||||
atomicWrite(binaryCacheDir + "/" + path, data);
|
||||
}
|
||||
|
||||
static RegisterStoreImplementation regStore([](
|
||||
const std::string & uri, const Store::Params & params)
|
||||
-> std::shared_ptr<Store>
|
||||
|
||||
@@ -20,9 +20,9 @@ struct LocalStoreAccessor : public FSAccessor
|
||||
|
||||
Path toRealPath(const Path & path)
|
||||
{
|
||||
auto storePath = store->toStorePath(path).first;
|
||||
if (!store->isValidPath(storePath))
|
||||
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
|
||||
Path storePath = store->toStorePath(path);
|
||||
if (!store->isValidPath(store->parseStorePath(storePath)))
|
||||
throw InvalidPath(format("path '%1%' is not a valid store path") % storePath);
|
||||
return store->getRealStoreDir() + std::string(path, store->storeDir.size());
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@ struct LocalStoreAccessor : public FSAccessor
|
||||
struct stat st;
|
||||
if (lstat(realPath.c_str(), &st)) {
|
||||
if (errno == ENOENT || errno == ENOTDIR) return {Type::tMissing, 0, false};
|
||||
throw SysError("getting status of '%1%'", path);
|
||||
throw SysError(format("getting status of '%1%'") % path);
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode))
|
||||
throw Error("file '%1%' has unsupported type", path);
|
||||
throw Error(format("file '%1%' has unsupported type") % path);
|
||||
|
||||
return {
|
||||
S_ISREG(st.st_mode) ? Type::tRegular :
|
||||
@@ -90,13 +90,13 @@ const string LocalFSStore::drvsLogDir = "drvs";
|
||||
|
||||
std::shared_ptr<std::string> LocalFSStore::getBuildLog(const StorePath & path_)
|
||||
{
|
||||
auto path = path_;
|
||||
auto path = path_.clone();
|
||||
|
||||
if (!path.isDerivation()) {
|
||||
try {
|
||||
auto info = queryPathInfo(path);
|
||||
if (!info->deriver) return nullptr;
|
||||
path = *info->deriver;
|
||||
path = info->deriver->clone();
|
||||
} catch (InvalidPath &) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -87,22 +87,18 @@ LocalStore::LocalStore(const Params & params)
|
||||
|
||||
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
|
||||
if (!gr)
|
||||
logError({
|
||||
.name = "'build-users-group' not found",
|
||||
.hint = hintfmt(
|
||||
"warning: the group '%1%' specified in 'build-users-group' does not exist",
|
||||
settings.buildUsersGroup)
|
||||
});
|
||||
printError(format("warning: the group '%1%' specified in 'build-users-group' does not exist")
|
||||
% settings.buildUsersGroup);
|
||||
else {
|
||||
struct stat st;
|
||||
if (stat(realStoreDir.c_str(), &st))
|
||||
throw SysError("getting attributes of path '%1%'", realStoreDir);
|
||||
throw SysError(format("getting attributes of path '%1%'") % realStoreDir);
|
||||
|
||||
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
|
||||
if (chown(realStoreDir.c_str(), 0, gr->gr_gid) == -1)
|
||||
throw SysError("changing ownership of path '%1%'", realStoreDir);
|
||||
throw SysError(format("changing ownership of path '%1%'") % realStoreDir);
|
||||
if (chmod(realStoreDir.c_str(), perm) == -1)
|
||||
throw SysError("changing permissions on path '%1%'", realStoreDir);
|
||||
throw SysError(format("changing permissions on path '%1%'") % realStoreDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,12 +109,12 @@ LocalStore::LocalStore(const Params & params)
|
||||
struct stat st;
|
||||
while (path != "/") {
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError("getting status of '%1%'", path);
|
||||
throw SysError(format("getting status of '%1%'") % path);
|
||||
if (S_ISLNK(st.st_mode))
|
||||
throw Error(
|
||||
throw Error(format(
|
||||
"the path '%1%' is a symlink; "
|
||||
"this is not allowed for the Nix store and its parent directories",
|
||||
path);
|
||||
"this is not allowed for the Nix store and its parent directories")
|
||||
% path);
|
||||
path = dirOf(path);
|
||||
}
|
||||
}
|
||||
@@ -151,7 +147,7 @@ LocalStore::LocalStore(const Params & params)
|
||||
globalLock = openLockFile(globalLockPath.c_str(), true);
|
||||
|
||||
if (!lockFile(globalLock.get(), ltRead, false)) {
|
||||
printInfo("waiting for the big Nix store lock...");
|
||||
printError("waiting for the big Nix store lock...");
|
||||
lockFile(globalLock.get(), ltRead, true);
|
||||
}
|
||||
|
||||
@@ -159,8 +155,8 @@ LocalStore::LocalStore(const Params & params)
|
||||
upgrade. */
|
||||
int curSchema = getSchema();
|
||||
if (curSchema > nixSchemaVersion)
|
||||
throw Error("current Nix store schema is version %1%, but I only support %2%",
|
||||
curSchema, nixSchemaVersion);
|
||||
throw Error(format("current Nix store schema is version %1%, but I only support %2%")
|
||||
% curSchema % nixSchemaVersion);
|
||||
|
||||
else if (curSchema == 0) { /* new store */
|
||||
curSchema = nixSchemaVersion;
|
||||
@@ -182,7 +178,7 @@ LocalStore::LocalStore(const Params & params)
|
||||
"please upgrade Nix to version 1.11 first.");
|
||||
|
||||
if (!lockFile(globalLock.get(), ltWrite, false)) {
|
||||
printInfo("waiting for exclusive access to the Nix store...");
|
||||
printError("waiting for exclusive access to the Nix store...");
|
||||
lockFile(globalLock.get(), ltWrite, true);
|
||||
}
|
||||
|
||||
@@ -260,7 +256,7 @@ LocalStore::~LocalStore()
|
||||
}
|
||||
|
||||
if (future.valid()) {
|
||||
printInfo("waiting for auto-GC to finish on exit...");
|
||||
printError("waiting for auto-GC to finish on exit...");
|
||||
future.get();
|
||||
}
|
||||
|
||||
@@ -288,7 +284,7 @@ int LocalStore::getSchema()
|
||||
if (pathExists(schemaPath)) {
|
||||
string s = readFile(schemaPath);
|
||||
if (!string2Int(s, curSchema))
|
||||
throw Error("'%1%' is corrupt", schemaPath);
|
||||
throw Error(format("'%1%' is corrupt") % schemaPath);
|
||||
}
|
||||
return curSchema;
|
||||
}
|
||||
@@ -297,7 +293,7 @@ int LocalStore::getSchema()
|
||||
void LocalStore::openDB(State & state, bool create)
|
||||
{
|
||||
if (access(dbDir.c_str(), R_OK | W_OK))
|
||||
throw SysError("Nix database directory '%1%' is not writable", dbDir);
|
||||
throw SysError(format("Nix database directory '%1%' is not writable") % dbDir);
|
||||
|
||||
/* Open the Nix database. */
|
||||
string dbPath = dbDir + "/db.sqlite";
|
||||
@@ -371,7 +367,7 @@ void LocalStore::makeStoreWritable()
|
||||
throw SysError("setting up a private mount namespace");
|
||||
|
||||
if (mount(0, realStoreDir.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
|
||||
throw SysError("remounting %1% writable", realStoreDir);
|
||||
throw SysError(format("remounting %1% writable") % realStoreDir);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -392,7 +388,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
|
||||
| 0444
|
||||
| (st.st_mode & S_IXUSR ? 0111 : 0);
|
||||
if (chmod(path.c_str(), mode) == -1)
|
||||
throw SysError("changing mode of '%1%' to %2$o", path, mode);
|
||||
throw SysError(format("changing mode of '%1%' to %2$o") % path % mode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -410,7 +406,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
|
||||
#else
|
||||
if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)
|
||||
#endif
|
||||
throw SysError("changing modification time of '%1%'", path);
|
||||
throw SysError(format("changing modification time of '%1%'") % path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,7 +415,7 @@ void canonicaliseTimestampAndPermissions(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError("getting attributes of path '%1%'", path);
|
||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
||||
canonicaliseTimestampAndPermissions(path, st);
|
||||
}
|
||||
|
||||
@@ -434,17 +430,17 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||
setattrlist() to remove other attributes as well. */
|
||||
if (lchflags(path.c_str(), 0)) {
|
||||
if (errno != ENOTSUP)
|
||||
throw SysError("clearing flags of path '%1%'", path);
|
||||
throw SysError(format("clearing flags of path '%1%'") % path);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError("getting attributes of path '%1%'", path);
|
||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
||||
|
||||
/* Really make sure that the path is of a supported type. */
|
||||
if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)))
|
||||
throw Error("file '%1%' has an unsupported type", path);
|
||||
throw Error(format("file '%1%' has an unsupported type") % path);
|
||||
|
||||
#if __linux__
|
||||
/* Remove extended attributes / ACLs. */
|
||||
@@ -478,7 +474,7 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||
if (fromUid != (uid_t) -1 && st.st_uid != fromUid) {
|
||||
assert(!S_ISDIR(st.st_mode));
|
||||
if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end())
|
||||
throw BuildError("invalid ownership on file '%1%'", path);
|
||||
throw BuildError(format("invalid ownership on file '%1%'") % path);
|
||||
mode_t mode = st.st_mode & ~S_IFMT;
|
||||
assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore));
|
||||
return;
|
||||
@@ -502,8 +498,8 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||
if (!S_ISLNK(st.st_mode) &&
|
||||
chown(path.c_str(), geteuid(), getegid()) == -1)
|
||||
#endif
|
||||
throw SysError("changing owner of '%1%' to %2%",
|
||||
path, geteuid());
|
||||
throw SysError(format("changing owner of '%1%' to %2%")
|
||||
% path % geteuid());
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
@@ -522,11 +518,11 @@ void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & ino
|
||||
be a symlink, since we can't change its ownership. */
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError("getting attributes of path '%1%'", path);
|
||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
||||
|
||||
if (st.st_uid != geteuid()) {
|
||||
assert(S_ISLNK(st.st_mode));
|
||||
throw Error("wrong ownership of top-level store path '%1%'", path);
|
||||
throw Error(format("wrong ownership of top-level store path '%1%'") % path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,12 +557,10 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
|
||||
if (out == drv.outputs.end())
|
||||
throw Error("derivation '%s' does not have an output named 'out'", printStorePath(drvPath));
|
||||
|
||||
check(
|
||||
makeFixedOutputPath(
|
||||
out->second.hash->method,
|
||||
out->second.hash->hash,
|
||||
drvName),
|
||||
out->second.path, "out");
|
||||
bool recursive; Hash h;
|
||||
out->second.parseHashInfo(recursive, h);
|
||||
|
||||
check(makeFixedOutputPath(recursive, h, drvName), out->second.path, "out");
|
||||
}
|
||||
|
||||
else {
|
||||
@@ -580,19 +574,19 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
|
||||
uint64_t LocalStore::addValidPath(State & state,
|
||||
const ValidPathInfo & info, bool checkOutputs)
|
||||
{
|
||||
if (info.ca.has_value() && !info.isContentAddressed(*this))
|
||||
if (info.ca != "" && !info.isContentAddressed(*this))
|
||||
throw Error("cannot add path '%s' to the Nix store because it claims to be content-addressed but isn't",
|
||||
printStorePath(info.path));
|
||||
|
||||
state.stmtRegisterValidPath.use()
|
||||
(printStorePath(info.path))
|
||||
(info.narHash.to_string(Base16, true))
|
||||
(info.narHash.to_string(Base16))
|
||||
(info.registrationTime == 0 ? time(0) : info.registrationTime)
|
||||
(info.deriver ? printStorePath(*info.deriver) : "", (bool) info.deriver)
|
||||
(info.narSize, info.narSize != 0)
|
||||
(info.ultimate ? 1 : 0, info.ultimate)
|
||||
(concatStringsSep(" ", info.sigs), !info.sigs.empty())
|
||||
(renderContentAddress(info.ca), (bool) info.ca)
|
||||
(info.ca, !info.ca.empty())
|
||||
.exec();
|
||||
uint64_t id = sqlite3_last_insert_rowid(state.db);
|
||||
|
||||
@@ -601,7 +595,7 @@ uint64_t LocalStore::addValidPath(State & state,
|
||||
efficiently query whether a path is an output of some
|
||||
derivation. */
|
||||
if (info.path.isDerivation()) {
|
||||
auto drv = readDerivation(info.path);
|
||||
auto drv = readDerivation(*this, realStoreDir + "/" + std::string(info.path.to_string()));
|
||||
|
||||
/* Verify that the output paths in the derivation are correct
|
||||
(i.e., follow the scheme for computing output paths from
|
||||
@@ -621,7 +615,7 @@ uint64_t LocalStore::addValidPath(State & state,
|
||||
|
||||
{
|
||||
auto state_(Store::state.lock());
|
||||
state_->pathInfoCache.upsert(std::string(info.path.hashPart()),
|
||||
state_->pathInfoCache.upsert(storePathToHash(printStorePath(info.path)),
|
||||
PathInfoCacheValue{ .value = std::make_shared<const ValidPathInfo>(info) });
|
||||
}
|
||||
|
||||
@@ -633,7 +627,7 @@ void LocalStore::queryPathInfoUncached(const StorePath & path,
|
||||
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
|
||||
{
|
||||
try {
|
||||
auto info = std::make_shared<ValidPathInfo>(path);
|
||||
auto info = std::make_shared<ValidPathInfo>(path.clone());
|
||||
|
||||
callback(retrySQLite<std::shared_ptr<ValidPathInfo>>([&]() {
|
||||
auto state(_state.lock());
|
||||
@@ -666,7 +660,7 @@ void LocalStore::queryPathInfoUncached(const StorePath & path,
|
||||
if (s) info->sigs = tokenizeString<StringSet>(s, " ");
|
||||
|
||||
s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 7);
|
||||
if (s) info->ca = parseContentAddressOpt(s);
|
||||
if (s) info->ca = s;
|
||||
|
||||
/* Get the references. */
|
||||
auto useQueryReferences(state->stmtQueryReferences.use()(info->id));
|
||||
@@ -686,10 +680,10 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info)
|
||||
{
|
||||
state.stmtUpdatePathInfo.use()
|
||||
(info.narSize, info.narSize != 0)
|
||||
(info.narHash.to_string(Base16, true))
|
||||
(info.narHash.to_string(Base16))
|
||||
(info.ultimate ? 1 : 0, info.ultimate)
|
||||
(concatStringsSep(" ", info.sigs), !info.sigs.empty())
|
||||
(renderContentAddress(info.ca), (bool) info.ca)
|
||||
(info.ca, !info.ca.empty())
|
||||
(printStorePath(info.path))
|
||||
.exec();
|
||||
}
|
||||
@@ -723,7 +717,7 @@ StorePathSet LocalStore::queryValidPaths(const StorePathSet & paths, SubstituteF
|
||||
{
|
||||
StorePathSet res;
|
||||
for (auto & i : paths)
|
||||
if (isValidPath(i)) res.insert(i);
|
||||
if (isValidPath(i)) res.insert(i.clone());
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -774,29 +768,43 @@ StorePathSet LocalStore::queryValidDerivers(const StorePath & path)
|
||||
}
|
||||
|
||||
|
||||
OutputPathMap LocalStore::queryDerivationOutputMap(const StorePath & path)
|
||||
StorePathSet LocalStore::queryDerivationOutputs(const StorePath & path)
|
||||
{
|
||||
return retrySQLite<OutputPathMap>([&]() {
|
||||
return retrySQLite<StorePathSet>([&]() {
|
||||
auto state(_state.lock());
|
||||
|
||||
auto useQueryDerivationOutputs(state->stmtQueryDerivationOutputs.use()
|
||||
(queryValidPathId(*state, path)));
|
||||
|
||||
OutputPathMap outputs;
|
||||
StorePathSet outputs;
|
||||
while (useQueryDerivationOutputs.next())
|
||||
outputs.emplace(
|
||||
useQueryDerivationOutputs.getStr(0),
|
||||
parseStorePath(useQueryDerivationOutputs.getStr(1))
|
||||
);
|
||||
outputs.insert(parseStorePath(useQueryDerivationOutputs.getStr(1)));
|
||||
|
||||
return outputs;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
StringSet LocalStore::queryDerivationOutputNames(const StorePath & path)
|
||||
{
|
||||
return retrySQLite<StringSet>([&]() {
|
||||
auto state(_state.lock());
|
||||
|
||||
auto useQueryDerivationOutputs(state->stmtQueryDerivationOutputs.use()
|
||||
(queryValidPathId(*state, path)));
|
||||
|
||||
StringSet outputNames;
|
||||
while (useQueryDerivationOutputs.next())
|
||||
outputNames.insert(useQueryDerivationOutputs.getStr(0));
|
||||
|
||||
return outputNames;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
std::optional<StorePath> LocalStore::queryPathFromHashPart(const std::string & hashPart)
|
||||
{
|
||||
if (hashPart.size() != StorePath::HashLen) throw Error("invalid hash part");
|
||||
if (hashPart.size() != storePathHashLen) throw Error("invalid hash part");
|
||||
|
||||
Path prefix = storeDir + "/" + hashPart;
|
||||
|
||||
@@ -821,7 +829,7 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths)
|
||||
|
||||
StorePathSet remaining;
|
||||
for (auto & i : paths)
|
||||
remaining.insert(i);
|
||||
remaining.insert(i.clone());
|
||||
|
||||
StorePathSet res;
|
||||
|
||||
@@ -835,9 +843,9 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths)
|
||||
StorePathSet remaining2;
|
||||
for (auto & path : remaining)
|
||||
if (valid.count(path))
|
||||
res.insert(path);
|
||||
res.insert(path.clone());
|
||||
else
|
||||
remaining2.insert(path);
|
||||
remaining2.insert(path.clone());
|
||||
|
||||
std::swap(remaining, remaining2);
|
||||
}
|
||||
@@ -859,16 +867,16 @@ void LocalStore::querySubstitutablePathInfos(const StorePathSet & paths,
|
||||
auto info = sub->queryPathInfo(path);
|
||||
auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
|
||||
std::shared_ptr<const ValidPathInfo>(info));
|
||||
infos.insert_or_assign(path, SubstitutablePathInfo{
|
||||
info->deriver,
|
||||
info->references,
|
||||
infos.insert_or_assign(path.clone(), SubstitutablePathInfo{
|
||||
info->deriver ? info->deriver->clone() : std::optional<StorePath>(),
|
||||
cloneStorePathSet(info->references),
|
||||
narInfo ? narInfo->fileSize : 0,
|
||||
info->narSize});
|
||||
} catch (InvalidPath &) {
|
||||
} catch (SubstituterDisabled &) {
|
||||
} catch (Error & e) {
|
||||
if (settings.tryFallback)
|
||||
logError(e.info());
|
||||
printError(e.what());
|
||||
else
|
||||
throw;
|
||||
}
|
||||
@@ -905,7 +913,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||
updatePathInfo(*state, i);
|
||||
else
|
||||
addValidPath(*state, i, false);
|
||||
paths.insert(i.path);
|
||||
paths.insert(i.path.clone());
|
||||
}
|
||||
|
||||
for (auto & i : infos) {
|
||||
@@ -920,7 +928,8 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||
for (auto & i : infos)
|
||||
if (i.path.isDerivation()) {
|
||||
// FIXME: inefficient; we already loaded the derivation in addValidPath().
|
||||
checkDerivationOutputs(i.path, readDerivation(i.path));
|
||||
checkDerivationOutputs(i.path,
|
||||
readDerivation(*this, realStoreDir + "/" + std::string(i.path.to_string())));
|
||||
}
|
||||
|
||||
/* Do a topological sort of the paths. This will throw an
|
||||
@@ -947,7 +956,7 @@ void LocalStore::invalidatePath(State & state, const StorePath & path)
|
||||
|
||||
{
|
||||
auto state_(Store::state.lock());
|
||||
state_->pathInfoCache.erase(std::string(path.hashPart()));
|
||||
state_->pathInfoCache.erase(storePathToHash(printStorePath(path)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -962,7 +971,7 @@ const PublicKeys & LocalStore::getPublicKeys()
|
||||
|
||||
|
||||
void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs)
|
||||
RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor)
|
||||
{
|
||||
if (!info.narHash)
|
||||
throw Error("cannot add path '%s' because it lacks a hash", printStorePath(info.path));
|
||||
@@ -976,7 +985,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||
|
||||
PathLocks outputLock;
|
||||
|
||||
auto realPath = Store::toRealPath(info.path);
|
||||
Path realPath = realStoreDir + "/" + std::string(info.path.to_string());
|
||||
|
||||
/* Lock the output path. But don't lock if we're being called
|
||||
from a build hook (whose parent process already acquired a
|
||||
@@ -988,18 +997,18 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||
|
||||
deletePath(realPath);
|
||||
|
||||
// text hashing has long been allowed to have non-self-references because it is used for drv files.
|
||||
bool refersToSelf = info.references.count(info.path) > 0;
|
||||
if (info.ca.has_value() && !info.references.empty() && !(std::holds_alternative<TextHash>(*info.ca) && !refersToSelf))
|
||||
if (info.ca != "" &&
|
||||
!((hasPrefix(info.ca, "text:") && !info.references.count(info.path))
|
||||
|| info.references.empty()))
|
||||
settings.requireExperimentalFeature("ca-references");
|
||||
|
||||
/* While restoring the path from the NAR, compute the hash
|
||||
of the NAR. */
|
||||
std::unique_ptr<AbstractHashSink> hashSink;
|
||||
if (!info.ca.has_value() || !info.references.count(info.path))
|
||||
if (info.ca == "" || !info.references.count(info.path))
|
||||
hashSink = std::make_unique<HashSink>(htSHA256);
|
||||
else
|
||||
hashSink = std::make_unique<HashModuloSink>(htSHA256, std::string(info.path.hashPart()));
|
||||
hashSink = std::make_unique<HashModuloSink>(htSHA256, storePathToHash(printStorePath(info.path)));
|
||||
|
||||
LambdaSource wrapperSource([&](unsigned char * data, size_t len) -> size_t {
|
||||
size_t n = source.read(data, len);
|
||||
@@ -1013,7 +1022,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||
|
||||
if (hashResult.first != info.narHash)
|
||||
throw Error("hash mismatch importing path '%s';\n wanted: %s\n got: %s",
|
||||
printStorePath(info.path), info.narHash.to_string(Base32, true), hashResult.first.to_string(Base32, true));
|
||||
printStorePath(info.path), info.narHash.to_string(), hashResult.first.to_string());
|
||||
|
||||
if (hashResult.second != info.narSize)
|
||||
throw Error("size mismatch importing path '%s';\n wanted: %s\n got: %s",
|
||||
@@ -1034,11 +1043,11 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||
|
||||
|
||||
StorePath LocalStore::addToStoreFromDump(const string & dump, const string & name,
|
||||
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair)
|
||||
bool recursive, HashType hashAlgo, RepairFlag repair)
|
||||
{
|
||||
Hash h = hashString(hashAlgo, dump);
|
||||
|
||||
auto dstPath = makeFixedOutputPath(method, h, name);
|
||||
auto dstPath = makeFixedOutputPath(recursive, h, name);
|
||||
|
||||
addTempRoot(dstPath);
|
||||
|
||||
@@ -1047,7 +1056,8 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||
/* The first check above is an optimisation to prevent
|
||||
unnecessary lock acquisition. */
|
||||
|
||||
auto realPath = Store::toRealPath(dstPath);
|
||||
Path realPath = realStoreDir + "/";
|
||||
realPath += dstPath.to_string();
|
||||
|
||||
PathLocks outputLock({realPath});
|
||||
|
||||
@@ -1057,7 +1067,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||
|
||||
autoGC();
|
||||
|
||||
if (method == FileIngestionMethod::Recursive) {
|
||||
if (recursive) {
|
||||
StringSource source(dump);
|
||||
restorePath(realPath, source);
|
||||
} else
|
||||
@@ -1070,7 +1080,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||
above (if called with recursive == true and hashAlgo ==
|
||||
sha256); otherwise, compute it here. */
|
||||
HashResult hash;
|
||||
if (method == FileIngestionMethod::Recursive) {
|
||||
if (recursive) {
|
||||
hash.first = hashAlgo == htSHA256 ? h : hashString(htSHA256, dump);
|
||||
hash.second = dump.size();
|
||||
} else
|
||||
@@ -1078,10 +1088,10 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||
|
||||
optimisePath(realPath); // FIXME: combine with hashPath()
|
||||
|
||||
ValidPathInfo info(dstPath);
|
||||
ValidPathInfo info(dstPath.clone());
|
||||
info.narHash = hash.first;
|
||||
info.narSize = hash.second;
|
||||
info.ca = FixedOutputHash { .method = method, .hash = h };
|
||||
info.ca = makeFixedOutputCA(recursive, h);
|
||||
registerValidPath(info);
|
||||
}
|
||||
|
||||
@@ -1093,123 +1103,20 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||
|
||||
|
||||
StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
|
||||
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
||||
{
|
||||
Path srcPath(absPath(_srcPath));
|
||||
|
||||
if (method != FileIngestionMethod::Recursive)
|
||||
return addToStoreFromDump(readFile(srcPath), name, method, hashAlgo, repair);
|
||||
/* Read the whole path into memory. This is not a very scalable
|
||||
method for very large paths, but `copyPath' is mainly used for
|
||||
small files. */
|
||||
StringSink sink;
|
||||
if (recursive)
|
||||
dumpPath(srcPath, sink, filter);
|
||||
else
|
||||
sink.s = make_ref<std::string>(readFile(srcPath));
|
||||
|
||||
/* For computing the NAR hash. */
|
||||
auto sha256Sink = std::make_unique<HashSink>(htSHA256);
|
||||
|
||||
/* For computing the store path. In recursive SHA-256 mode, this
|
||||
is the same as the NAR hash, so no need to do it again. */
|
||||
std::unique_ptr<HashSink> hashSink =
|
||||
hashAlgo == htSHA256
|
||||
? nullptr
|
||||
: std::make_unique<HashSink>(hashAlgo);
|
||||
|
||||
/* Read the source path into memory, but only if it's up to
|
||||
narBufferSize bytes. If it's larger, write it to a temporary
|
||||
location in the Nix store. If the subsequently computed
|
||||
destination store path is already valid, we just delete the
|
||||
temporary path. Otherwise, we move it to the destination store
|
||||
path. */
|
||||
bool inMemory = true;
|
||||
std::string nar;
|
||||
|
||||
auto source = sinkToSource([&](Sink & sink) {
|
||||
|
||||
LambdaSink sink2([&](const unsigned char * buf, size_t len) {
|
||||
(*sha256Sink)(buf, len);
|
||||
if (hashSink) (*hashSink)(buf, len);
|
||||
|
||||
if (inMemory) {
|
||||
if (nar.size() + len > settings.narBufferSize) {
|
||||
inMemory = false;
|
||||
sink << 1;
|
||||
sink((const unsigned char *) nar.data(), nar.size());
|
||||
nar.clear();
|
||||
} else {
|
||||
nar.append((const char *) buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
if (!inMemory) sink(buf, len);
|
||||
});
|
||||
|
||||
dumpPath(srcPath, sink2, filter);
|
||||
});
|
||||
|
||||
std::unique_ptr<AutoDelete> delTempDir;
|
||||
Path tempPath;
|
||||
|
||||
try {
|
||||
/* Wait for the source coroutine to give us some dummy
|
||||
data. This is so that we don't create the temporary
|
||||
directory if the NAR fits in memory. */
|
||||
readInt(*source);
|
||||
|
||||
auto tempDir = createTempDir(realStoreDir, "add");
|
||||
delTempDir = std::make_unique<AutoDelete>(tempDir);
|
||||
tempPath = tempDir + "/x";
|
||||
|
||||
restorePath(tempPath, *source);
|
||||
|
||||
} catch (EndOfFile &) {
|
||||
if (!inMemory) throw;
|
||||
/* The NAR fits in memory, so we didn't do restorePath(). */
|
||||
}
|
||||
|
||||
auto sha256 = sha256Sink->finish();
|
||||
|
||||
Hash hash = hashSink ? hashSink->finish().first : sha256.first;
|
||||
|
||||
auto dstPath = makeFixedOutputPath(method, hash, name);
|
||||
|
||||
addTempRoot(dstPath);
|
||||
|
||||
if (repair || !isValidPath(dstPath)) {
|
||||
|
||||
/* The first check above is an optimisation to prevent
|
||||
unnecessary lock acquisition. */
|
||||
|
||||
auto realPath = Store::toRealPath(dstPath);
|
||||
|
||||
PathLocks outputLock({realPath});
|
||||
|
||||
if (repair || !isValidPath(dstPath)) {
|
||||
|
||||
deletePath(realPath);
|
||||
|
||||
autoGC();
|
||||
|
||||
if (inMemory) {
|
||||
/* Restore from the NAR in memory. */
|
||||
StringSource source(nar);
|
||||
restorePath(realPath, source);
|
||||
} else {
|
||||
/* Move the temporary path we restored above. */
|
||||
if (rename(tempPath.c_str(), realPath.c_str()))
|
||||
throw Error("renaming '%s' to '%s'", tempPath, realPath);
|
||||
}
|
||||
|
||||
canonicalisePathMetaData(realPath, -1); // FIXME: merge into restorePath
|
||||
|
||||
optimisePath(realPath);
|
||||
|
||||
ValidPathInfo info(dstPath);
|
||||
info.narHash = sha256.first;
|
||||
info.narSize = sha256.second;
|
||||
info.ca = FixedOutputHash { .method = method, .hash = hash };
|
||||
registerValidPath(info);
|
||||
}
|
||||
|
||||
outputLock.setDeletion(true);
|
||||
}
|
||||
|
||||
return dstPath;
|
||||
return addToStoreFromDump(*sink.s, name, recursive, hashAlgo, repair);
|
||||
}
|
||||
|
||||
|
||||
@@ -1223,7 +1130,8 @@ StorePath LocalStore::addTextToStore(const string & name, const string & s,
|
||||
|
||||
if (repair || !isValidPath(dstPath)) {
|
||||
|
||||
auto realPath = Store::toRealPath(dstPath);
|
||||
Path realPath = realStoreDir + "/";
|
||||
realPath += dstPath.to_string();
|
||||
|
||||
PathLocks outputLock({realPath});
|
||||
|
||||
@@ -1243,11 +1151,11 @@ StorePath LocalStore::addTextToStore(const string & name, const string & s,
|
||||
|
||||
optimisePath(realPath);
|
||||
|
||||
ValidPathInfo info(dstPath);
|
||||
ValidPathInfo info(dstPath.clone());
|
||||
info.narHash = narHash;
|
||||
info.narSize = sink.s->size();
|
||||
info.references = references;
|
||||
info.ca = TextHash { .hash = hash };
|
||||
info.references = cloneStorePathSet(references);
|
||||
info.ca = "text:" + hash.to_string();
|
||||
registerValidPath(info);
|
||||
}
|
||||
|
||||
@@ -1297,7 +1205,7 @@ void LocalStore::invalidatePathChecked(const StorePath & path)
|
||||
|
||||
bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
{
|
||||
printInfo(format("reading the Nix store..."));
|
||||
printError(format("reading the Nix store..."));
|
||||
|
||||
bool errors = false;
|
||||
|
||||
@@ -1329,15 +1237,12 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
Path linkPath = linksDir + "/" + link.name;
|
||||
string hash = hashPath(htSHA256, linkPath).first.to_string(Base32, false);
|
||||
if (hash != link.name) {
|
||||
logError({
|
||||
.name = "Invalid hash",
|
||||
.hint = hintfmt(
|
||||
"link '%s' was modified! expected hash '%s', got '%s'",
|
||||
linkPath, link.name, hash)
|
||||
});
|
||||
printError(
|
||||
"link '%s' was modified! expected hash '%s', got '%s'",
|
||||
linkPath, link.name, hash);
|
||||
if (repair) {
|
||||
if (unlink(linkPath.c_str()) == 0)
|
||||
printInfo("removed link '%s'", linkPath);
|
||||
printError("removed link '%s'", linkPath);
|
||||
else
|
||||
throw SysError("removing corrupt link '%s'", linkPath);
|
||||
} else {
|
||||
@@ -1358,20 +1263,17 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
printMsg(lvlTalkative, "checking contents of '%s'", printStorePath(i));
|
||||
|
||||
std::unique_ptr<AbstractHashSink> hashSink;
|
||||
if (!info->ca || !info->references.count(info->path))
|
||||
hashSink = std::make_unique<HashSink>(*info->narHash.type);
|
||||
if (info->ca == "" || !info->references.count(info->path))
|
||||
hashSink = std::make_unique<HashSink>(info->narHash.type);
|
||||
else
|
||||
hashSink = std::make_unique<HashModuloSink>(*info->narHash.type, std::string(info->path.hashPart()));
|
||||
hashSink = std::make_unique<HashModuloSink>(info->narHash.type, storePathToHash(printStorePath(info->path)));
|
||||
|
||||
dumpPath(Store::toRealPath(i), *hashSink);
|
||||
auto current = hashSink->finish();
|
||||
|
||||
if (info->narHash != nullHash && info->narHash != current.first) {
|
||||
logError({
|
||||
.name = "Invalid hash - path modified",
|
||||
.hint = hintfmt("path '%s' was modified! expected hash '%s', got '%s'",
|
||||
printStorePath(i), info->narHash.to_string(Base32, true), current.first.to_string(Base32, true))
|
||||
});
|
||||
printError("path '%s' was modified! expected hash '%s', got '%s'",
|
||||
printStorePath(i), info->narHash.to_string(), current.first.to_string());
|
||||
if (repair) repairPath(i); else errors = true;
|
||||
} else {
|
||||
|
||||
@@ -1379,14 +1281,14 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
|
||||
/* Fill in missing hashes. */
|
||||
if (info->narHash == nullHash) {
|
||||
printInfo("fixing missing hash on '%s'", printStorePath(i));
|
||||
printError("fixing missing hash on '%s'", printStorePath(i));
|
||||
info->narHash = current.first;
|
||||
update = true;
|
||||
}
|
||||
|
||||
/* Fill in missing narSize fields (from old stores). */
|
||||
if (info->narSize == 0) {
|
||||
printInfo("updating size field on '%s' to %s", printStorePath(i), current.second);
|
||||
printError("updating size field on '%s' to %s", printStorePath(i), current.second);
|
||||
info->narSize = current.second;
|
||||
update = true;
|
||||
}
|
||||
@@ -1402,7 +1304,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
/* It's possible that the path got GC'ed, so ignore
|
||||
errors on invalid paths. */
|
||||
if (isValidPath(i))
|
||||
logError(e.info());
|
||||
printError("error: %s", e.msg());
|
||||
else
|
||||
warn(e.msg());
|
||||
errors = true;
|
||||
@@ -1422,10 +1324,7 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store,
|
||||
if (!done.insert(pathS).second) return;
|
||||
|
||||
if (!isStorePath(pathS)) {
|
||||
logError({
|
||||
.name = "Nix path not found",
|
||||
.hint = hintfmt("path '%s' is not in the Nix store", pathS)
|
||||
});
|
||||
printError("path '%s' is not in the Nix store", pathS);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1444,19 +1343,16 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store,
|
||||
}
|
||||
|
||||
if (canInvalidate) {
|
||||
printInfo("path '%s' disappeared, removing from database...", pathS);
|
||||
printError("path '%s' disappeared, removing from database...", pathS);
|
||||
auto state(_state.lock());
|
||||
invalidatePath(*state, path);
|
||||
} else {
|
||||
logError({
|
||||
.name = "Missing path with referrers",
|
||||
.hint = hintfmt("path '%s' disappeared, but it still has valid referrers!", pathS)
|
||||
});
|
||||
printError("path '%s' disappeared, but it still has valid referrers!", pathS);
|
||||
if (repair)
|
||||
try {
|
||||
repairPath(path);
|
||||
} catch (Error & e) {
|
||||
logWarning(e.info());
|
||||
warn(e.msg());
|
||||
errors = true;
|
||||
}
|
||||
else errors = true;
|
||||
@@ -1496,7 +1392,7 @@ static void makeMutable(const Path & path)
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
if (errno == ELOOP) return; // it's a symlink
|
||||
throw SysError("opening file '%1%'", path);
|
||||
throw SysError(format("opening file '%1%'") % path);
|
||||
}
|
||||
|
||||
unsigned int flags = 0, old;
|
||||
@@ -1514,7 +1410,7 @@ static void makeMutable(const Path & path)
|
||||
void LocalStore::upgradeStore7()
|
||||
{
|
||||
if (getuid() != 0) return;
|
||||
printInfo("removing immutable bits from the Nix store (this may take a while)...");
|
||||
printError("removing immutable bits from the Nix store (this may take a while)...");
|
||||
makeMutable(realStoreDir);
|
||||
}
|
||||
|
||||
|
||||
@@ -133,7 +133,9 @@ public:
|
||||
|
||||
StorePathSet queryValidDerivers(const StorePath & path) override;
|
||||
|
||||
OutputPathMap queryDerivationOutputMap(const StorePath & path) override;
|
||||
StorePathSet queryDerivationOutputs(const StorePath & path) override;
|
||||
|
||||
StringSet queryDerivationOutputNames(const StorePath & path) override;
|
||||
|
||||
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override;
|
||||
|
||||
@@ -143,10 +145,11 @@ public:
|
||||
SubstitutablePathInfos & infos) override;
|
||||
|
||||
void addToStore(const ValidPathInfo & info, Source & source,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
||||
RepairFlag repair, CheckSigsFlag checkSigs,
|
||||
std::shared_ptr<FSAccessor> accessor) override;
|
||||
|
||||
StorePath addToStore(const string & name, const Path & srcPath,
|
||||
FileIngestionMethod method, HashType hashAlgo,
|
||||
bool recursive, HashType hashAlgo,
|
||||
PathFilter & filter, RepairFlag repair) override;
|
||||
|
||||
/* Like addToStore(), but the contents of the path are contained
|
||||
@@ -154,7 +157,7 @@ public:
|
||||
true) or simply the contents of a regular file (if recursive ==
|
||||
false). */
|
||||
StorePath addToStoreFromDump(const string & dump, const string & name,
|
||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override;
|
||||
bool recursive = true, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override;
|
||||
|
||||
StorePath addTextToStore(const string & name, const string & s,
|
||||
const StorePathSet & references, RepairFlag repair) override;
|
||||
|
||||
@@ -6,7 +6,7 @@ libstore_DIR := $(d)
|
||||
|
||||
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc)
|
||||
|
||||
libstore_LIBS = libutil
|
||||
libstore_LIBS = libutil libnixrust
|
||||
|
||||
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
|
||||
ifneq ($(OS), FreeBSD)
|
||||
|
||||
@@ -103,7 +103,7 @@ void Store::computeFSClosure(const StorePath & startPath,
|
||||
StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers)
|
||||
{
|
||||
StorePathSet paths;
|
||||
paths.insert(startPath);
|
||||
paths.insert(startPath.clone());
|
||||
computeFSClosure(paths, paths_, flipDirection, includeOutputs, includeDerivers);
|
||||
}
|
||||
|
||||
@@ -141,11 +141,11 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||
auto mustBuildDrv = [&](const StorePath & drvPath, const Derivation & drv) {
|
||||
{
|
||||
auto state(state_.lock());
|
||||
state->willBuild.insert(drvPath);
|
||||
state->willBuild.insert(drvPath.clone());
|
||||
}
|
||||
|
||||
for (auto & i : drv.inputDrvs)
|
||||
pool.enqueue(std::bind(doPath, StorePathWithOutputs { i.first, i.second }));
|
||||
pool.enqueue(std::bind(doPath, StorePathWithOutputs(i.first, i.second)));
|
||||
};
|
||||
|
||||
auto checkOutput = [&](
|
||||
@@ -157,7 +157,9 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||
auto outPath = parseStorePath(outPathS);
|
||||
|
||||
SubstitutablePathInfos infos;
|
||||
querySubstitutablePathInfos({outPath}, infos);
|
||||
StorePathSet paths; // FIXME
|
||||
paths.insert(outPath.clone());
|
||||
querySubstitutablePathInfos(paths, infos);
|
||||
|
||||
if (infos.empty()) {
|
||||
drvState_->lock()->done = true;
|
||||
@@ -168,10 +170,10 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||
if (drvState->done) return;
|
||||
assert(drvState->left);
|
||||
drvState->left--;
|
||||
drvState->outPaths.insert(outPath);
|
||||
drvState->outPaths.insert(outPath.clone());
|
||||
if (!drvState->left) {
|
||||
for (auto & path : drvState->outPaths)
|
||||
pool.enqueue(std::bind(doPath, StorePathWithOutputs { path } ));
|
||||
pool.enqueue(std::bind(doPath, StorePathWithOutputs(path.clone())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,12 +190,12 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||
if (!isValidPath(path.path)) {
|
||||
// FIXME: we could try to substitute the derivation.
|
||||
auto state(state_.lock());
|
||||
state->unknown.insert(path.path);
|
||||
state->unknown.insert(path.path.clone());
|
||||
return;
|
||||
}
|
||||
|
||||
auto drv = make_ref<Derivation>(derivationFromPath(path.path));
|
||||
ParsedDerivation parsedDrv(StorePath(path.path), *drv);
|
||||
ParsedDerivation parsedDrv(path.path.clone(), *drv);
|
||||
|
||||
PathSet invalid;
|
||||
for (auto & j : drv->outputs)
|
||||
@@ -214,11 +216,13 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||
if (isValidPath(path.path)) return;
|
||||
|
||||
SubstitutablePathInfos infos;
|
||||
querySubstitutablePathInfos({path.path}, infos);
|
||||
StorePathSet paths; // FIXME
|
||||
paths.insert(path.path.clone());
|
||||
querySubstitutablePathInfos(paths, infos);
|
||||
|
||||
if (infos.empty()) {
|
||||
auto state(state_.lock());
|
||||
state->unknown.insert(path.path);
|
||||
state->unknown.insert(path.path.clone());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -227,13 +231,13 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||
|
||||
{
|
||||
auto state(state_.lock());
|
||||
state->willSubstitute.insert(path.path);
|
||||
state->willSubstitute.insert(path.path.clone());
|
||||
state->downloadSize += info->second.downloadSize;
|
||||
state->narSize += info->second.narSize;
|
||||
}
|
||||
|
||||
for (auto & ref : info->second.references)
|
||||
pool.enqueue(std::bind(doPath, StorePathWithOutputs { ref }));
|
||||
pool.enqueue(std::bind(doPath, StorePathWithOutputs(ref)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -256,12 +260,12 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths)
|
||||
throw BuildError("cycle detected in the references of '%s' from '%s'",
|
||||
printStorePath(path), printStorePath(*parent));
|
||||
|
||||
if (!visited.insert(path).second) return;
|
||||
parents.insert(path);
|
||||
if (!visited.insert(path.clone()).second) return;
|
||||
parents.insert(path.clone());
|
||||
|
||||
StorePathSet references;
|
||||
try {
|
||||
references = queryPathInfo(path)->references;
|
||||
references = cloneStorePathSet(queryPathInfo(path)->references);
|
||||
} catch (InvalidPath &) {
|
||||
}
|
||||
|
||||
@@ -271,7 +275,7 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths)
|
||||
if (i != path && paths.count(i))
|
||||
dfsVisit(i, &path);
|
||||
|
||||
sorted.push_back(path);
|
||||
sorted.push_back(path.clone());
|
||||
parents.erase(path);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ struct NarMember
|
||||
|
||||
/* If this is a regular file, position of the contents of this
|
||||
file in the NAR. */
|
||||
uint64_t start = 0, size = 0;
|
||||
size_t start = 0, size = 0;
|
||||
|
||||
std::string target;
|
||||
|
||||
@@ -34,19 +34,17 @@ struct NarAccessor : public FSAccessor
|
||||
|
||||
NarMember root;
|
||||
|
||||
struct NarIndexer : ParseSink, Source
|
||||
struct NarIndexer : ParseSink, StringSource
|
||||
{
|
||||
NarAccessor & acc;
|
||||
Source & source;
|
||||
|
||||
std::stack<NarMember *> parents;
|
||||
|
||||
std::string currentStart;
|
||||
bool isExec = false;
|
||||
|
||||
uint64_t pos = 0;
|
||||
|
||||
NarIndexer(NarAccessor & acc, Source & source)
|
||||
: acc(acc), source(source)
|
||||
NarIndexer(NarAccessor & acc, const std::string & nar)
|
||||
: StringSource(nar), acc(acc)
|
||||
{ }
|
||||
|
||||
void createMember(const Path & path, NarMember member) {
|
||||
@@ -81,38 +79,31 @@ struct NarAccessor : public FSAccessor
|
||||
|
||||
void preallocateContents(unsigned long long size) override
|
||||
{
|
||||
assert(size <= std::numeric_limits<uint64_t>::max());
|
||||
parents.top()->size = (uint64_t) size;
|
||||
currentStart = string(s, pos, 16);
|
||||
assert(size <= std::numeric_limits<size_t>::max());
|
||||
parents.top()->size = (size_t)size;
|
||||
parents.top()->start = pos;
|
||||
}
|
||||
|
||||
void receiveContents(unsigned char * data, unsigned int len) override
|
||||
{ }
|
||||
{
|
||||
// Sanity check
|
||||
if (!currentStart.empty()) {
|
||||
assert(len < 16 || currentStart == string((char *) data, 16));
|
||||
currentStart.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void createSymlink(const Path & path, const string & target) override
|
||||
{
|
||||
createMember(path,
|
||||
NarMember{FSAccessor::Type::tSymlink, false, 0, 0, target});
|
||||
}
|
||||
|
||||
size_t read(unsigned char * data, size_t len) override
|
||||
{
|
||||
auto n = source.read(data, len);
|
||||
pos += n;
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
NarAccessor(ref<const std::string> nar) : nar(nar)
|
||||
{
|
||||
StringSource source(*nar);
|
||||
NarIndexer indexer(*this, source);
|
||||
parseDump(indexer, indexer);
|
||||
}
|
||||
|
||||
NarAccessor(Source & source)
|
||||
{
|
||||
NarIndexer indexer(*this, source);
|
||||
NarIndexer indexer(*this, *nar);
|
||||
parseDump(indexer, indexer);
|
||||
}
|
||||
|
||||
@@ -193,7 +184,7 @@ struct NarAccessor : public FSAccessor
|
||||
auto i = get(path);
|
||||
|
||||
if (i.type != FSAccessor::Type::tDirectory)
|
||||
throw Error("path '%1%' inside NAR file is not a directory", path);
|
||||
throw Error(format("path '%1%' inside NAR file is not a directory") % path);
|
||||
|
||||
StringSet res;
|
||||
for (auto & child : i.children)
|
||||
@@ -206,7 +197,7 @@ struct NarAccessor : public FSAccessor
|
||||
{
|
||||
auto i = get(path);
|
||||
if (i.type != FSAccessor::Type::tRegular)
|
||||
throw Error("path '%1%' inside NAR file is not a regular file", path);
|
||||
throw Error(format("path '%1%' inside NAR file is not a regular file") % path);
|
||||
|
||||
if (getNarBytes) return getNarBytes(i.start, i.size);
|
||||
|
||||
@@ -218,7 +209,7 @@ struct NarAccessor : public FSAccessor
|
||||
{
|
||||
auto i = get(path);
|
||||
if (i.type != FSAccessor::Type::tSymlink)
|
||||
throw Error("path '%1%' inside NAR file is not a symlink", path);
|
||||
throw Error(format("path '%1%' inside NAR file is not a symlink") % path);
|
||||
return i.target;
|
||||
}
|
||||
};
|
||||
@@ -228,11 +219,6 @@ ref<FSAccessor> makeNarAccessor(ref<const std::string> nar)
|
||||
return make_ref<NarAccessor>(nar);
|
||||
}
|
||||
|
||||
ref<FSAccessor> makeNarAccessor(Source & source)
|
||||
{
|
||||
return make_ref<NarAccessor>(source);
|
||||
}
|
||||
|
||||
ref<FSAccessor> makeLazyNarAccessor(const std::string & listing,
|
||||
GetNarBytes getNarBytes)
|
||||
{
|
||||
|
||||
@@ -6,14 +6,10 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct Source;
|
||||
|
||||
/* Return an object that provides access to the contents of a NAR
|
||||
file. */
|
||||
ref<FSAccessor> makeNarAccessor(ref<const std::string> nar);
|
||||
|
||||
ref<FSAccessor> makeNarAccessor(Source & source);
|
||||
|
||||
/* Create a NAR accessor from a NAR listing (in the format produced by
|
||||
listNar()). The callback getNarBytes(offset, length) is used by the
|
||||
readFile() method of the accessor to get the contents of files
|
||||
|
||||
@@ -189,7 +189,7 @@ public:
|
||||
return {oInvalid, 0};
|
||||
|
||||
auto namePart = queryNAR.getStr(1);
|
||||
auto narInfo = make_ref<NarInfo>(StorePath(hashPart + "-" + namePart));
|
||||
auto narInfo = make_ref<NarInfo>(StorePath::fromBaseName(hashPart + "-" + namePart));
|
||||
narInfo->url = queryNAR.getStr(2);
|
||||
narInfo->compression = queryNAR.getStr(3);
|
||||
if (!queryNAR.isNull(4))
|
||||
@@ -198,12 +198,12 @@ public:
|
||||
narInfo->narHash = Hash(queryNAR.getStr(6));
|
||||
narInfo->narSize = queryNAR.getInt(7);
|
||||
for (auto & r : tokenizeString<Strings>(queryNAR.getStr(8), " "))
|
||||
narInfo->references.insert(StorePath(r));
|
||||
narInfo->references.insert(StorePath::fromBaseName(r));
|
||||
if (!queryNAR.isNull(9))
|
||||
narInfo->deriver = StorePath(queryNAR.getStr(9));
|
||||
narInfo->deriver = StorePath::fromBaseName(queryNAR.getStr(9));
|
||||
for (auto & sig : tokenizeString<Strings>(queryNAR.getStr(10), " "))
|
||||
narInfo->sigs.insert(sig);
|
||||
narInfo->ca = parseContentAddressOpt(queryNAR.getStr(11));
|
||||
narInfo->ca = queryNAR.getStr(11);
|
||||
|
||||
return {oValid, narInfo};
|
||||
});
|
||||
@@ -230,14 +230,14 @@ public:
|
||||
(std::string(info->path.name()))
|
||||
(narInfo ? narInfo->url : "", narInfo != 0)
|
||||
(narInfo ? narInfo->compression : "", narInfo != 0)
|
||||
(narInfo && narInfo->fileHash ? narInfo->fileHash.to_string(Base32, true) : "", narInfo && narInfo->fileHash)
|
||||
(narInfo && narInfo->fileHash ? narInfo->fileHash.to_string() : "", narInfo && narInfo->fileHash)
|
||||
(narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize)
|
||||
(info->narHash.to_string(Base32, true))
|
||||
(info->narHash.to_string())
|
||||
(info->narSize)
|
||||
(concatStringsSep(" ", info->shortRefs()))
|
||||
(info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver)
|
||||
(concatStringsSep(" ", info->sigs))
|
||||
(renderContentAddress(info->ca))
|
||||
(info->ca)
|
||||
(time(0)).exec();
|
||||
|
||||
} else {
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
namespace nix {
|
||||
|
||||
NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence)
|
||||
: ValidPathInfo(StorePath(StorePath::dummy)) // FIXME: hack
|
||||
: ValidPathInfo(StorePath::dummy.clone()) // FIXME: hack
|
||||
{
|
||||
auto corrupt = [&]() {
|
||||
throw Error("NAR info file '%1%' is corrupt", whence);
|
||||
throw Error(format("NAR info file '%1%' is corrupt") % whence);
|
||||
};
|
||||
|
||||
auto parseHashField = [&](const string & s) {
|
||||
@@ -56,20 +56,19 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
|
||||
auto refs = tokenizeString<Strings>(value, " ");
|
||||
if (!references.empty()) corrupt();
|
||||
for (auto & r : refs)
|
||||
references.insert(StorePath(r));
|
||||
references.insert(StorePath::fromBaseName(r));
|
||||
}
|
||||
else if (name == "Deriver") {
|
||||
if (value != "unknown-deriver")
|
||||
deriver = StorePath(value);
|
||||
deriver = StorePath::fromBaseName(value);
|
||||
}
|
||||
else if (name == "System")
|
||||
system = value;
|
||||
else if (name == "Sig")
|
||||
sigs.insert(value);
|
||||
else if (name == "CA") {
|
||||
if (ca) corrupt();
|
||||
// FIXME: allow blank ca or require skipping field?
|
||||
ca = parseContentAddressOpt(value);
|
||||
if (!ca.empty()) corrupt();
|
||||
ca = value;
|
||||
}
|
||||
|
||||
pos = eol + 1;
|
||||
@@ -88,10 +87,10 @@ std::string NarInfo::to_string(const Store & store) const
|
||||
assert(compression != "");
|
||||
res += "Compression: " + compression + "\n";
|
||||
assert(fileHash.type == htSHA256);
|
||||
res += "FileHash: " + fileHash.to_string(Base32, true) + "\n";
|
||||
res += "FileHash: " + fileHash.to_string(Base32) + "\n";
|
||||
res += "FileSize: " + std::to_string(fileSize) + "\n";
|
||||
assert(narHash.type == htSHA256);
|
||||
res += "NarHash: " + narHash.to_string(Base32, true) + "\n";
|
||||
res += "NarHash: " + narHash.to_string(Base32) + "\n";
|
||||
res += "NarSize: " + std::to_string(narSize) + "\n";
|
||||
|
||||
res += "References: " + concatStringsSep(" ", shortRefs()) + "\n";
|
||||
@@ -105,8 +104,8 @@ std::string NarInfo::to_string(const Store & store) const
|
||||
for (auto sig : sigs)
|
||||
res += "Sig: " + sig + "\n";
|
||||
|
||||
if (ca)
|
||||
res += "CA: " + renderContentAddress(*ca) + "\n";
|
||||
if (!ca.empty())
|
||||
res += "CA: " + ca + "\n";
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ static void makeWritable(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError("getting attributes of path '%1%'", path);
|
||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError("changing writability of '%1%'", path);
|
||||
throw SysError(format("changing writability of '%1%'") % path);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
|
||||
InodeHash inodeHash;
|
||||
|
||||
AutoCloseDir dir(opendir(linksDir.c_str()));
|
||||
if (!dir) throw SysError("opening directory '%1%'", linksDir);
|
||||
if (!dir) throw SysError(format("opening directory '%1%'") % linksDir);
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
|
||||
@@ -55,7 +55,7 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
|
||||
// We don't care if we hit non-hash files, anything goes
|
||||
inodeHash.insert(dirent->d_ino);
|
||||
}
|
||||
if (errno) throw SysError("reading directory '%1%'", linksDir);
|
||||
if (errno) throw SysError(format("reading directory '%1%'") % linksDir);
|
||||
|
||||
printMsg(lvlTalkative, format("loaded %1% hash inodes") % inodeHash.size());
|
||||
|
||||
@@ -68,7 +68,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
|
||||
Strings names;
|
||||
|
||||
AutoCloseDir dir(opendir(path.c_str()));
|
||||
if (!dir) throw SysError("opening directory '%1%'", path);
|
||||
if (!dir) throw SysError(format("opening directory '%1%'") % path);
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
|
||||
@@ -83,7 +83,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
|
||||
if (name == "." || name == "..") continue;
|
||||
names.push_back(name);
|
||||
}
|
||||
if (errno) throw SysError("reading directory '%1%'", path);
|
||||
if (errno) throw SysError(format("reading directory '%1%'") % path);
|
||||
|
||||
return names;
|
||||
}
|
||||
@@ -96,7 +96,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError("getting attributes of path '%1%'", path);
|
||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
||||
|
||||
#if __APPLE__
|
||||
/* HFS/macOS has some undocumented security feature disabling hardlinking for
|
||||
@@ -130,10 +130,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
NixOS (example: $fontconfig/var/cache being modified). Skip
|
||||
those files. FIXME: check the modification time. */
|
||||
if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
|
||||
logWarning({
|
||||
.name = "Suspicious file",
|
||||
.hint = hintfmt("skipping suspicious writable file '%1%'", path)
|
||||
});
|
||||
printError(format("skipping suspicious writable file '%1%'") % path);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -153,7 +150,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
contents of the symlink (i.e. the result of readlink()), not
|
||||
the contents of the target (which may not even exist). */
|
||||
Hash hash = hashPath(htSHA256, path).first;
|
||||
debug(format("'%1%' has hash '%2%'") % path % hash.to_string(Base32, true));
|
||||
debug(format("'%1%' has hash '%2%'") % path % hash.to_string());
|
||||
|
||||
/* Check if this is a known hash. */
|
||||
Path linkPath = linksDir + "/" + hash.to_string(Base32, false);
|
||||
@@ -189,7 +186,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
current file with a hard link to that file. */
|
||||
struct stat stLink;
|
||||
if (lstat(linkPath.c_str(), &stLink))
|
||||
throw SysError("getting attributes of path '%1%'", linkPath);
|
||||
throw SysError(format("getting attributes of path '%1%'") % linkPath);
|
||||
|
||||
if (st.st_ino == stLink.st_ino) {
|
||||
debug(format("'%1%' is already linked to '%2%'") % path % linkPath);
|
||||
@@ -197,10 +194,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
}
|
||||
|
||||
if (st.st_size != stLink.st_size) {
|
||||
logWarning({
|
||||
.name = "Corrupted link",
|
||||
.hint = hintfmt("removing corrupted link '%1%'", linkPath)
|
||||
});
|
||||
printError(format("removing corrupted link '%1%'") % linkPath);
|
||||
unlink(linkPath.c_str());
|
||||
goto retry;
|
||||
}
|
||||
@@ -235,10 +229,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
/* Atomically replace the old file with the new hard link. */
|
||||
if (rename(tempLink.c_str(), path.c_str()) == -1) {
|
||||
if (unlink(tempLink.c_str()) == -1)
|
||||
logError({
|
||||
.name = "Unlink error",
|
||||
.hint = hintfmt("unable to unlink '%1%'", tempLink)
|
||||
});
|
||||
printError(format("unable to unlink '%1%'") % tempLink);
|
||||
if (errno == EMLINK) {
|
||||
/* Some filesystems generate too many links on the rename,
|
||||
rather than on the original link. (Probably it
|
||||
@@ -247,7 +238,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
debug("'%s' has reached maximum number of links", linkPath);
|
||||
return;
|
||||
}
|
||||
throw SysError("cannot rename '%1%' to '%2%'", tempLink, path);
|
||||
throw SysError(format("cannot rename '%1%' to '%2%'") % tempLink % path);
|
||||
}
|
||||
|
||||
stats.filesLinked++;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
ParsedDerivation::ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv)
|
||||
: drvPath(drvPath), drv(drv)
|
||||
ParsedDerivation::ParsedDerivation(StorePath && drvPath, BasicDerivation & drv)
|
||||
: drvPath(std::move(drvPath)), drv(drv)
|
||||
{
|
||||
/* Parse the __json attribute, if any. */
|
||||
auto jsonAttr = drv.env.find("__json");
|
||||
@@ -117,9 +117,4 @@ bool ParsedDerivation::substitutesAllowed() const
|
||||
return getBoolAttr("allowSubstitutes", true);
|
||||
}
|
||||
|
||||
bool ParsedDerivation::contentAddressed() const
|
||||
{
|
||||
return getBoolAttr("__contentAddressed", false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "store-api.hh"
|
||||
#include "derivations.hh"
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
@@ -12,7 +12,7 @@ class ParsedDerivation
|
||||
|
||||
public:
|
||||
|
||||
ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv);
|
||||
ParsedDerivation(StorePath && drvPath, BasicDerivation & drv);
|
||||
|
||||
~ParsedDerivation();
|
||||
|
||||
@@ -34,8 +34,6 @@ public:
|
||||
bool willBuildLocally() const;
|
||||
|
||||
bool substitutesAllowed() const;
|
||||
|
||||
bool contentAddressed() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,36 +2,38 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
static void checkName(std::string_view path, std::string_view name)
|
||||
{
|
||||
if (name.empty())
|
||||
throw BadStorePath("store path '%s' has an empty name", path);
|
||||
if (name.size() > 211)
|
||||
throw BadStorePath("store path '%s' has a name longer than 211 characters", path);
|
||||
for (auto c : name)
|
||||
if (!((c >= '0' && c <= '9')
|
||||
|| (c >= 'a' && c <= 'z')
|
||||
|| (c >= 'A' && c <= 'Z')
|
||||
|| c == '+' || c == '-' || c == '.' || c == '_' || c == '?' || c == '='))
|
||||
throw BadStorePath("store path '%s' contains illegal character '%s'", path, c);
|
||||
extern "C" {
|
||||
rust::Result<StorePath> ffi_StorePath_new(rust::StringSlice path, rust::StringSlice storeDir);
|
||||
rust::Result<StorePath> ffi_StorePath_new2(unsigned char hash[20], rust::StringSlice storeDir);
|
||||
rust::Result<StorePath> ffi_StorePath_fromBaseName(rust::StringSlice baseName);
|
||||
rust::String ffi_StorePath_to_string(const StorePath & _this);
|
||||
StorePath ffi_StorePath_clone(const StorePath & _this);
|
||||
rust::StringSlice ffi_StorePath_name(const StorePath & _this);
|
||||
}
|
||||
|
||||
StorePath::StorePath(std::string_view _baseName)
|
||||
: baseName(_baseName)
|
||||
StorePath StorePath::make(std::string_view path, std::string_view storeDir)
|
||||
{
|
||||
if (baseName.size() < HashLen + 1)
|
||||
throw BadStorePath("'%s' is too short to be a valid store path", baseName);
|
||||
for (auto c : hashPart())
|
||||
if (c == 'e' || c == 'o' || c == 'u' || c == 't'
|
||||
|| !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')))
|
||||
throw BadStorePath("store path '%s' contains illegal base-32 character '%s'", baseName, c);
|
||||
checkName(baseName, name());
|
||||
return ffi_StorePath_new((rust::StringSlice) path, (rust::StringSlice) storeDir).unwrap();
|
||||
}
|
||||
|
||||
StorePath::StorePath(const Hash & hash, std::string_view _name)
|
||||
: baseName((hash.to_string(Base32, false) + "-").append(std::string(_name)))
|
||||
StorePath StorePath::make(unsigned char hash[20], std::string_view name)
|
||||
{
|
||||
checkName(baseName, name());
|
||||
return ffi_StorePath_new2(hash, (rust::StringSlice) name).unwrap();
|
||||
}
|
||||
|
||||
StorePath StorePath::fromBaseName(std::string_view baseName)
|
||||
{
|
||||
return ffi_StorePath_fromBaseName((rust::StringSlice) baseName).unwrap();
|
||||
}
|
||||
|
||||
rust::String StorePath::to_string() const
|
||||
{
|
||||
return ffi_StorePath_to_string(*this);
|
||||
}
|
||||
|
||||
StorePath StorePath::clone() const
|
||||
{
|
||||
return ffi_StorePath_clone(*this);
|
||||
}
|
||||
|
||||
bool StorePath::isDerivation() const
|
||||
@@ -39,14 +41,18 @@ bool StorePath::isDerivation() const
|
||||
return hasSuffix(name(), drvExtension);
|
||||
}
|
||||
|
||||
StorePath StorePath::dummy("ffffffffffffffffffffffffffffffff-x");
|
||||
std::string_view StorePath::name() const
|
||||
{
|
||||
return ffi_StorePath_name(*this);
|
||||
}
|
||||
|
||||
StorePath StorePath::dummy(
|
||||
StorePath::make(
|
||||
(unsigned char *) "xxxxxxxxxxxxxxxxxxxx", "x"));
|
||||
|
||||
StorePath Store::parseStorePath(std::string_view path) const
|
||||
{
|
||||
auto p = canonPath(std::string(path));
|
||||
if (dirOf(p) != storeDir)
|
||||
throw BadStorePath("path '%s' is not in the Nix store", p);
|
||||
return StorePath(baseNameOf(p));
|
||||
return StorePath::make(path, storeDir);
|
||||
}
|
||||
|
||||
std::optional<StorePath> Store::maybeParseStorePath(std::string_view path) const
|
||||
@@ -72,7 +78,9 @@ StorePathSet Store::parseStorePathSet(const PathSet & paths) const
|
||||
|
||||
std::string Store::printStorePath(const StorePath & path) const
|
||||
{
|
||||
return (storeDir + "/").append(path.to_string());
|
||||
auto s = storeDir + "/";
|
||||
s += (std::string_view) path.to_string();
|
||||
return s;
|
||||
}
|
||||
|
||||
PathSet Store::printStorePathSet(const StorePathSet & paths) const
|
||||
@@ -82,6 +90,29 @@ PathSet Store::printStorePathSet(const StorePathSet & paths) const
|
||||
return res;
|
||||
}
|
||||
|
||||
StorePathSet cloneStorePathSet(const StorePathSet & paths)
|
||||
{
|
||||
StorePathSet res;
|
||||
for (auto & p : paths)
|
||||
res.insert(p.clone());
|
||||
return res;
|
||||
}
|
||||
|
||||
StorePathSet storePathsToSet(const StorePaths & paths)
|
||||
{
|
||||
StorePathSet res;
|
||||
for (auto & p : paths)
|
||||
res.insert(p.clone());
|
||||
return res;
|
||||
}
|
||||
|
||||
StorePathSet singleton(const StorePath & path)
|
||||
{
|
||||
StorePathSet res;
|
||||
res.insert(path.clone());
|
||||
return res;
|
||||
}
|
||||
|
||||
std::pair<std::string_view, StringSet> parsePathWithOutputs(std::string_view s)
|
||||
{
|
||||
size_t n = s.find("!");
|
||||
|
||||
@@ -1,60 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "content-address.hh"
|
||||
#include "types.hh"
|
||||
#include "rust-ffi.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* See path.rs. */
|
||||
struct StorePath;
|
||||
|
||||
class Store;
|
||||
struct Hash;
|
||||
|
||||
class StorePath
|
||||
extern "C" {
|
||||
void ffi_StorePath_drop(void *);
|
||||
bool ffi_StorePath_less_than(const StorePath & a, const StorePath & b);
|
||||
bool ffi_StorePath_eq(const StorePath & a, const StorePath & b);
|
||||
unsigned char * ffi_StorePath_hash_data(const StorePath & p);
|
||||
}
|
||||
|
||||
struct StorePath : rust::Value<3 * sizeof(void *) + 24, ffi_StorePath_drop>
|
||||
{
|
||||
std::string baseName;
|
||||
|
||||
public:
|
||||
|
||||
/* Size of the hash part of store paths, in base-32 characters. */
|
||||
constexpr static size_t HashLen = 32; // i.e. 160 bits
|
||||
|
||||
StorePath() = delete;
|
||||
|
||||
StorePath(std::string_view baseName);
|
||||
static StorePath make(std::string_view path, std::string_view storeDir);
|
||||
|
||||
StorePath(const Hash & hash, std::string_view name);
|
||||
static StorePath make(unsigned char hash[20], std::string_view name);
|
||||
|
||||
std::string_view to_string() const
|
||||
{
|
||||
return baseName;
|
||||
}
|
||||
static StorePath fromBaseName(std::string_view baseName);
|
||||
|
||||
rust::String to_string() const;
|
||||
|
||||
bool operator < (const StorePath & other) const
|
||||
{
|
||||
return baseName < other.baseName;
|
||||
return ffi_StorePath_less_than(*this, other);
|
||||
}
|
||||
|
||||
bool operator == (const StorePath & other) const
|
||||
{
|
||||
return baseName == other.baseName;
|
||||
return ffi_StorePath_eq(*this, other);
|
||||
}
|
||||
|
||||
bool operator != (const StorePath & other) const
|
||||
{
|
||||
return baseName != other.baseName;
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
StorePath clone() const;
|
||||
|
||||
/* Check whether a file name ends with the extension for
|
||||
derivations. */
|
||||
bool isDerivation() const;
|
||||
|
||||
std::string_view name() const
|
||||
{
|
||||
return std::string_view(baseName).substr(HashLen + 1);
|
||||
}
|
||||
std::string_view name() const;
|
||||
|
||||
std::string_view hashPart() const
|
||||
unsigned char * hashData() const
|
||||
{
|
||||
return std::string_view(baseName).substr(0, HashLen);
|
||||
return ffi_StorePath_hash_data(*this);
|
||||
}
|
||||
|
||||
static StorePath dummy;
|
||||
@@ -62,7 +61,14 @@ public:
|
||||
|
||||
typedef std::set<StorePath> StorePathSet;
|
||||
typedef std::vector<StorePath> StorePaths;
|
||||
typedef std::map<string, StorePath> OutputPathMap;
|
||||
|
||||
StorePathSet cloneStorePathSet(const StorePathSet & paths);
|
||||
StorePathSet storePathsToSet(const StorePaths & paths);
|
||||
|
||||
StorePathSet singleton(const StorePath & path);
|
||||
|
||||
/* Size of the hash part of store paths, in base-32 characters. */
|
||||
const size_t storePathHashLen = 32; // i.e. 160 bits
|
||||
|
||||
/* Extension of derivations in the Nix store. */
|
||||
const std::string drvExtension = ".drv";
|
||||
@@ -72,6 +78,18 @@ struct StorePathWithOutputs
|
||||
StorePath path;
|
||||
std::set<std::string> outputs;
|
||||
|
||||
StorePathWithOutputs(const StorePath & path, const std::set<std::string> & outputs = {})
|
||||
: path(path.clone()), outputs(outputs)
|
||||
{ }
|
||||
|
||||
StorePathWithOutputs(StorePath && path, std::set<std::string> && outputs)
|
||||
: path(std::move(path)), outputs(std::move(outputs))
|
||||
{ }
|
||||
|
||||
StorePathWithOutputs(const StorePathWithOutputs & other)
|
||||
: path(other.path.clone()), outputs(other.outputs)
|
||||
{ }
|
||||
|
||||
std::string to_string(const Store & store) const;
|
||||
};
|
||||
|
||||
@@ -84,7 +102,7 @@ namespace std {
|
||||
template<> struct hash<nix::StorePath> {
|
||||
std::size_t operator()(const nix::StorePath & path) const noexcept
|
||||
{
|
||||
return * (std::size_t *) path.to_string().data();
|
||||
return * (std::size_t *) path.hashData();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ AutoCloseFD openLockFile(const Path & path, bool create)
|
||||
|
||||
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
||||
if (!fd && (create || errno != ENOENT))
|
||||
throw SysError("opening lock file '%1%'", path);
|
||||
throw SysError(format("opening lock file '%1%'") % path);
|
||||
|
||||
return fd;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ bool lockFile(int fd, LockType lockType, bool wait)
|
||||
while (flock(fd, type) != 0) {
|
||||
checkInterrupt();
|
||||
if (errno != EINTR)
|
||||
throw SysError("acquiring/releasing lock");
|
||||
throw SysError(format("acquiring/releasing lock"));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -60,7 +60,7 @@ bool lockFile(int fd, LockType lockType, bool wait)
|
||||
checkInterrupt();
|
||||
if (errno == EWOULDBLOCK) return false;
|
||||
if (errno != EINTR)
|
||||
throw SysError("acquiring/releasing lock");
|
||||
throw SysError(format("acquiring/releasing lock"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ bool PathLocks::lockPaths(const PathSet & paths,
|
||||
hasn't been unlinked). */
|
||||
struct stat st;
|
||||
if (fstat(fd.get(), &st) == -1)
|
||||
throw SysError("statting lock file '%1%'", lockPath);
|
||||
throw SysError(format("statting lock file '%1%'") % lockPath);
|
||||
if (st.st_size != 0)
|
||||
/* This lock file has been unlinked, so we're holding
|
||||
a lock on a deleted file. This means that other
|
||||
@@ -160,8 +160,7 @@ void PathLocks::unlock()
|
||||
|
||||
if (close(i.first) == -1)
|
||||
printError(
|
||||
"error (ignored): cannot close lock file on '%1%'",
|
||||
i.second);
|
||||
format("error (ignored): cannot close lock file on '%1%'") % i.second);
|
||||
|
||||
debug(format("lock released on '%1%'") % i.second);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ Generations findGenerations(Path profile, int & curGen)
|
||||
gen.number = n;
|
||||
struct stat st;
|
||||
if (lstat(gen.path.c_str(), &st) != 0)
|
||||
throw SysError("statting '%1%'", gen.path);
|
||||
throw SysError(format("statting '%1%'") % gen.path);
|
||||
gen.creationTime = st.st_mtime;
|
||||
gens.push_back(gen);
|
||||
}
|
||||
@@ -117,7 +117,7 @@ Path createGeneration(ref<LocalFSStore> store, Path profile, Path outPath)
|
||||
static void removeFile(const Path & path)
|
||||
{
|
||||
if (remove(path.c_str()) == -1)
|
||||
throw SysError("cannot unlink '%1%'", path);
|
||||
throw SysError(format("cannot unlink '%1%'") % path);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ void deleteGenerations(const Path & profile, const std::set<unsigned int> & gens
|
||||
Generations gens = findGenerations(profile, curGen);
|
||||
|
||||
if (gensToDelete.find(curGen) != gensToDelete.end())
|
||||
throw Error("cannot delete current generation of profile %1%'", profile);
|
||||
throw Error(format("cannot delete current generation of profile %1%'") % profile);
|
||||
|
||||
for (auto & i : gens) {
|
||||
if (gensToDelete.find(i.number) == gensToDelete.end()) continue;
|
||||
@@ -226,7 +226,7 @@ void deleteGenerationsOlderThan(const Path & profile, const string & timeSpec, b
|
||||
int days;
|
||||
|
||||
if (!string2Int(strDays, days) || days < 1)
|
||||
throw Error("invalid number of days specifier '%1%'", timeSpec);
|
||||
throw Error(format("invalid number of days specifier '%1%'") % timeSpec);
|
||||
|
||||
time_t oldTime = curTime - days * 24 * 3600;
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ PathSet scanForReferences(const string & path,
|
||||
auto baseName = std::string(baseNameOf(i));
|
||||
string::size_type pos = baseName.find('-');
|
||||
if (pos == string::npos)
|
||||
throw Error("bad reference '%1%'", i);
|
||||
throw Error(format("bad reference '%1%'") % i);
|
||||
string s = string(baseName, 0, pos);
|
||||
assert(s.size() == refLength);
|
||||
assert(backMap.find(s) == backMap.end());
|
||||
|
||||
@@ -16,26 +16,26 @@ RemoteFSAccessor::RemoteFSAccessor(ref<Store> store, const Path & cacheDir)
|
||||
createDirs(cacheDir);
|
||||
}
|
||||
|
||||
Path RemoteFSAccessor::makeCacheFile(std::string_view hashPart, const std::string & ext)
|
||||
Path RemoteFSAccessor::makeCacheFile(const Path & storePath, const std::string & ext)
|
||||
{
|
||||
assert(cacheDir != "");
|
||||
return fmt("%s/%s.%s", cacheDir, hashPart, ext);
|
||||
return fmt("%s/%s.%s", cacheDir, storePathToHash(storePath), ext);
|
||||
}
|
||||
|
||||
void RemoteFSAccessor::addToCache(std::string_view hashPart, const std::string & nar,
|
||||
void RemoteFSAccessor::addToCache(const Path & storePath, const std::string & nar,
|
||||
ref<FSAccessor> narAccessor)
|
||||
{
|
||||
nars.emplace(hashPart, narAccessor);
|
||||
nars.emplace(storePath, narAccessor);
|
||||
|
||||
if (cacheDir != "") {
|
||||
try {
|
||||
std::ostringstream str;
|
||||
JSONPlaceholder jsonRoot(str);
|
||||
listNar(jsonRoot, narAccessor, "", true);
|
||||
writeFile(makeCacheFile(hashPart, "ls"), str.str());
|
||||
writeFile(makeCacheFile(storePath, "ls"), str.str());
|
||||
|
||||
/* FIXME: do this asynchronously. */
|
||||
writeFile(makeCacheFile(hashPart, "nar"), nar);
|
||||
writeFile(makeCacheFile(storePath, "nar"), nar);
|
||||
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
@@ -47,22 +47,23 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
|
||||
{
|
||||
auto path = canonPath(path_);
|
||||
|
||||
auto [storePath, restPath] = store->toStorePath(path);
|
||||
auto storePath = store->toStorePath(path);
|
||||
std::string restPath = std::string(path, storePath.size());
|
||||
|
||||
if (!store->isValidPath(storePath))
|
||||
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
|
||||
if (!store->isValidPath(store->parseStorePath(storePath)))
|
||||
throw InvalidPath(format("path '%1%' is not a valid store path") % storePath);
|
||||
|
||||
auto i = nars.find(std::string(storePath.hashPart()));
|
||||
auto i = nars.find(storePath);
|
||||
if (i != nars.end()) return {i->second, restPath};
|
||||
|
||||
StringSink sink;
|
||||
std::string listing;
|
||||
Path cacheFile;
|
||||
|
||||
if (cacheDir != "" && pathExists(cacheFile = makeCacheFile(storePath.hashPart(), "nar"))) {
|
||||
if (cacheDir != "" && pathExists(cacheFile = makeCacheFile(storePath, "nar"))) {
|
||||
|
||||
try {
|
||||
listing = nix::readFile(makeCacheFile(storePath.hashPart(), "ls"));
|
||||
listing = nix::readFile(makeCacheFile(storePath, "ls"));
|
||||
|
||||
auto narAccessor = makeLazyNarAccessor(listing,
|
||||
[cacheFile](uint64_t offset, uint64_t length) {
|
||||
@@ -80,7 +81,7 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
|
||||
return buf;
|
||||
});
|
||||
|
||||
nars.emplace(storePath.hashPart(), narAccessor);
|
||||
nars.emplace(storePath, narAccessor);
|
||||
return {narAccessor, restPath};
|
||||
|
||||
} catch (SysError &) { }
|
||||
@@ -89,15 +90,15 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
|
||||
*sink.s = nix::readFile(cacheFile);
|
||||
|
||||
auto narAccessor = makeNarAccessor(sink.s);
|
||||
nars.emplace(storePath.hashPart(), narAccessor);
|
||||
nars.emplace(storePath, narAccessor);
|
||||
return {narAccessor, restPath};
|
||||
|
||||
} catch (SysError &) { }
|
||||
}
|
||||
|
||||
store->narFromPath(storePath, sink);
|
||||
store->narFromPath(store->parseStorePath(storePath), sink);
|
||||
auto narAccessor = makeNarAccessor(sink.s);
|
||||
addToCache(storePath.hashPart(), *sink.s, narAccessor);
|
||||
addToCache(storePath, *sink.s, narAccessor);
|
||||
return {narAccessor, restPath};
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user