forked from lix-project/lix
Merge branch 'validPathInfo-temp' into validPathInfo-ca-proper-datatype
This commit is contained in:
commit
3f8dcfe3fd
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -49,6 +49,9 @@ perl/Makefile.config
|
||||||
# /src/libstore/
|
# /src/libstore/
|
||||||
*.gen.*
|
*.gen.*
|
||||||
|
|
||||||
|
# /src/libutil/
|
||||||
|
/src/libutil/tests/libutil-tests
|
||||||
|
|
||||||
/src/nix/nix
|
/src/nix/nix
|
||||||
|
|
||||||
# /src/nix-env/
|
# /src/nix-env/
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -1,7 +1,6 @@
|
||||||
makefiles = \
|
makefiles = \
|
||||||
mk/precompiled-headers.mk \
|
mk/precompiled-headers.mk \
|
||||||
local.mk \
|
local.mk \
|
||||||
nix-rust/local.mk \
|
|
||||||
src/libutil/local.mk \
|
src/libutil/local.mk \
|
||||||
src/libutil/tests/local.mk \
|
src/libutil/tests/local.mk \
|
||||||
src/libstore/local.mk \
|
src/libstore/local.mk \
|
||||||
|
|
|
@ -70,7 +70,7 @@ path just built.</para>
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
$ nix-build ./deterministic.nix -A stable
|
$ nix-build ./deterministic.nix -A stable
|
||||||
these derivations will be built:
|
this derivation will be built:
|
||||||
/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv
|
/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv
|
||||||
building '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
|
building '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
|
||||||
/nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable
|
/nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable
|
||||||
|
@ -85,7 +85,7 @@ checking outputs of '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
$ nix-build ./deterministic.nix -A unstable
|
$ nix-build ./deterministic.nix -A unstable
|
||||||
these derivations will be built:
|
this derivation will be built:
|
||||||
/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv
|
/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv
|
||||||
building '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
|
building '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
|
||||||
/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable
|
/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable
|
||||||
|
@ -193,7 +193,7 @@ repeat = 1
|
||||||
An example output of this configuration:
|
An example output of this configuration:
|
||||||
<screen>
|
<screen>
|
||||||
$ nix-build ./test.nix -A unstable
|
$ nix-build ./test.nix -A unstable
|
||||||
these derivations will be built:
|
this derivation will be built:
|
||||||
/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv
|
/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv
|
||||||
building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 1/2)...
|
building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 1/2)...
|
||||||
building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 2/2)...
|
building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 2/2)...
|
||||||
|
|
|
@ -122,7 +122,7 @@ post-build-hook = /etc/nix/upload-to-cache.sh
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
$ nix-build -E '(import <nixpkgs> {}).writeText "example" (builtins.toString builtins.currentTime)'
|
$ nix-build -E '(import <nixpkgs> {}).writeText "example" (builtins.toString builtins.currentTime)'
|
||||||
these derivations will be built:
|
this derivation will be built:
|
||||||
/nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv
|
/nix/store/s4pnfbkalzy5qz57qs6yybna8wylkig6-example.drv
|
||||||
building '/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'...
|
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
|
$ nix-env -f '<nixpkgs>' -iA hello --dry-run
|
||||||
(dry run; not doing anything)
|
(dry run; not doing anything)
|
||||||
installing ‘hello-2.10’
|
installing ‘hello-2.10’
|
||||||
these paths will be fetched (0.04 MiB download, 0.19 MiB unpacked):
|
this path will be fetched (0.04 MiB download, 0.19 MiB unpacked):
|
||||||
/nix/store/wkhdf9jinag5750mqlax6z2zbwhqb76n-hello-2.10
|
/nix/store/wkhdf9jinag5750mqlax6z2zbwhqb76n-hello-2.10
|
||||||
<replaceable>...</replaceable></screen>
|
<replaceable>...</replaceable></screen>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<nop xmlns="http://docbook.org/ns/docbook">
|
<nop xmlns="http://docbook.org/ns/docbook">
|
||||||
|
|
||||||
<arg><option>--help</option></arg>
|
<arg><option>--help</option></arg>
|
||||||
<arg><option>--version</option></arg>
|
<arg><option>--version</option></arg>
|
||||||
<arg rep='repeat'>
|
<arg rep='repeat'>
|
||||||
|
@ -11,6 +11,10 @@
|
||||||
<arg>
|
<arg>
|
||||||
<arg choice='plain'><option>--quiet</option></arg>
|
<arg choice='plain'><option>--quiet</option></arg>
|
||||||
</arg>
|
</arg>
|
||||||
|
<arg>
|
||||||
|
<option>--log-format</option>
|
||||||
|
<replaceable>format</replaceable>
|
||||||
|
</arg>
|
||||||
<arg>
|
<arg>
|
||||||
<group choice='plain'>
|
<group choice='plain'>
|
||||||
<arg choice='plain'><option>--no-build-output</option></arg>
|
<arg choice='plain'><option>--no-build-output</option></arg>
|
||||||
|
|
|
@ -92,6 +92,37 @@
|
||||||
</varlistentry>
|
</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>
|
<varlistentry><term><option>--no-build-output</option> / <option>-Q</option></term>
|
||||||
|
|
||||||
<listitem><para>By default, output written by builders to standard
|
<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>
|
<step><para>Set the environment variable and install Nix</para>
|
||||||
<screen>
|
<screen>
|
||||||
$ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
|
$ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
|
||||||
$ sh <(curl https://nixos.org/nix/install)
|
$ sh <(curl -L https://nixos.org/nix/install)
|
||||||
</screen></step>
|
</screen></step>
|
||||||
|
|
||||||
<step><para>In the shell profile and rc files (for example,
|
<step><para>In the shell profile and rc files (for example,
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
$ sh <(curl https://nixos.org/nix/install)
|
$ sh <(curl -L https://nixos.org/nix/install)
|
||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
To explicitly select a single-user installation on your system:
|
To explicitly select a single-user installation on your system:
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
sh <(curl https://nixos.org/nix/install) --no-daemon
|
sh <(curl -L https://nixos.org/nix/install) --no-daemon
|
||||||
</screen>
|
</screen>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ to subsequent chapters.</para>
|
||||||
<step><para>Install single-user Nix by running the following:
|
<step><para>Install single-user Nix by running the following:
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
$ bash <(curl https://nixos.org/nix/install)
|
$ bash <(curl -L https://nixos.org/nix/install)
|
||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
This will install Nix in <filename>/nix</filename>. The install script
|
This will install Nix in <filename>/nix</filename>. The install script
|
||||||
|
|
|
@ -142,8 +142,12 @@ $oldName =~ s/"//g;
|
||||||
sub getStorePath {
|
sub getStorePath {
|
||||||
my ($jobName) = @_;
|
my ($jobName) = @_;
|
||||||
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
|
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
|
||||||
die unless $buildInfo->{buildproducts}->{1}->{type} eq "nix-build";
|
for my $product (values %{$buildInfo->{buildproducts}}) {
|
||||||
return $buildInfo->{buildproducts}->{1}->{path};
|
next unless $product->{type} eq "nix-build";
|
||||||
|
next if $product->{path} =~ /[a-z]+$/;
|
||||||
|
return $product->{path};
|
||||||
|
}
|
||||||
|
die;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
||||||
|
|
|
@ -125,7 +125,8 @@ define build-library
|
||||||
$(1)_PATH := $$(_d)/$$($(1)_NAME).a
|
$(1)_PATH := $$(_d)/$$($(1)_NAME).a
|
||||||
|
|
||||||
$$($(1)_PATH): $$($(1)_OBJS) | $$(_d)/
|
$$($(1)_PATH): $$($(1)_OBJS) | $$(_d)/
|
||||||
$(trace-ar) $(AR) crs $$@ $$?
|
$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$?
|
||||||
|
$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
|
||||||
|
|
||||||
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS)
|
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS)
|
||||||
|
|
||||||
|
|
|
@ -64,13 +64,6 @@ pub extern "C" fn ffi_StorePath_clone(self_: &StorePath) -> StorePath {
|
||||||
self_.clone()
|
self_.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn ffi_StorePath_clone_to(self_: &StorePath, other: *mut StorePath) {
|
|
||||||
unsafe {
|
|
||||||
core::ptr::write(other, self_.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn ffi_StorePath_name(self_: &StorePath) -> &str {
|
pub extern "C" fn ffi_StorePath_name(self_: &StorePath) -> &str {
|
||||||
self_.name.name()
|
self_.name.name()
|
||||||
|
|
|
@ -80,7 +80,7 @@ SV * queryReferences(char * path)
|
||||||
SV * queryPathHash(char * path)
|
SV * queryPathHash(char * path)
|
||||||
PPCODE:
|
PPCODE:
|
||||||
try {
|
try {
|
||||||
auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash.to_string();
|
auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash.to_string(Base32, true);
|
||||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
croak("%s", e.what());
|
croak("%s", e.what());
|
||||||
|
@ -106,7 +106,7 @@ SV * queryPathInfo(char * path, int base32)
|
||||||
XPUSHs(&PL_sv_undef);
|
XPUSHs(&PL_sv_undef);
|
||||||
else
|
else
|
||||||
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(*info->deriver).c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(*info->deriver).c_str(), 0)));
|
||||||
auto s = info->narHash.to_string(base32 ? Base::Base32 : Base::Base16);
|
auto s = info->narHash.to_string(base32 ? Base32 : Base16, true);
|
||||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||||
mXPUSHi(info->registrationTime);
|
mXPUSHi(info->registrationTime);
|
||||||
mXPUSHi(info->narSize);
|
mXPUSHi(info->narSize);
|
||||||
|
@ -192,7 +192,7 @@ SV * hashPath(char * algo, int base32, char * path)
|
||||||
PPCODE:
|
PPCODE:
|
||||||
try {
|
try {
|
||||||
Hash h = hashPath(parseHashType(algo), path).first;
|
Hash h = hashPath(parseHashType(algo), path).first;
|
||||||
auto s = h.to_string(base32 ? Base::Base32 : Base::Base16, false);
|
auto s = h.to_string(base32 ? Base32 : Base16, false);
|
||||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
croak("%s", e.what());
|
croak("%s", e.what());
|
||||||
|
@ -203,7 +203,7 @@ SV * hashFile(char * algo, int base32, char * path)
|
||||||
PPCODE:
|
PPCODE:
|
||||||
try {
|
try {
|
||||||
Hash h = hashFile(parseHashType(algo), path);
|
Hash h = hashFile(parseHashType(algo), path);
|
||||||
auto s = h.to_string(base32 ? Base::Base32 : Base::Base16, false);
|
auto s = h.to_string(base32 ? Base32 : Base16, false);
|
||||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
croak("%s", e.what());
|
croak("%s", e.what());
|
||||||
|
@ -214,7 +214,7 @@ SV * hashString(char * algo, int base32, char * s)
|
||||||
PPCODE:
|
PPCODE:
|
||||||
try {
|
try {
|
||||||
Hash h = hashString(parseHashType(algo), s);
|
Hash h = hashString(parseHashType(algo), s);
|
||||||
auto s = h.to_string(base32 ? Base::Base32 : Base::Base16, false);
|
auto s = h.to_string(base32 ? Base32 : Base16, false);
|
||||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
croak("%s", e.what());
|
croak("%s", e.what());
|
||||||
|
@ -225,7 +225,7 @@ SV * convertHash(char * algo, char * s, int toBase32)
|
||||||
PPCODE:
|
PPCODE:
|
||||||
try {
|
try {
|
||||||
Hash h(s, parseHashType(algo));
|
Hash h(s, parseHashType(algo));
|
||||||
string s = h.to_string(toBase32 ? Base::Base32 : Base::Base16, false);
|
string s = h.to_string(toBase32 ? Base32 : Base16, false);
|
||||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
croak("%s", e.what());
|
croak("%s", e.what());
|
||||||
|
|
|
@ -50,7 +50,6 @@ rec {
|
||||||
libarchive
|
libarchive
|
||||||
boost
|
boost
|
||||||
nlohmann_json
|
nlohmann_json
|
||||||
rustc cargo
|
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
git
|
git
|
||||||
|
|
63
release.nix
63
release.nix
|
@ -12,64 +12,8 @@ let
|
||||||
builtins.readFile ./.version
|
builtins.readFile ./.version
|
||||||
+ (if officialRelease then "" else "pre${toString nix.revCount}_${nix.shortRev}");
|
+ (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 {
|
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:
|
build = pkgs.lib.genAttrs systems (system:
|
||||||
|
|
||||||
let pkgs = import nixpkgs { inherit system; }; in
|
let pkgs = import nixpkgs { inherit system; }; in
|
||||||
|
@ -101,8 +45,6 @@ let
|
||||||
patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
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)
|
(cd perl; autoreconf --install --force --verbose)
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
@ -247,11 +189,6 @@ let
|
||||||
|
|
||||||
src = nix;
|
src = nix;
|
||||||
|
|
||||||
preConfigure =
|
|
||||||
''
|
|
||||||
ln -sfn ${vendoredCrates'}/vendor/ nix-rust/vendor
|
|
||||||
'';
|
|
||||||
|
|
||||||
enableParallelBuilding = true;
|
enableParallelBuilding = true;
|
||||||
|
|
||||||
buildInputs = buildDeps ++ propagatedDeps;
|
buildInputs = buildDeps ++ propagatedDeps;
|
||||||
|
|
|
@ -7,7 +7,7 @@ with import ./release-common.nix { inherit pkgs; };
|
||||||
(if useClang then clangStdenv else stdenv).mkDerivation {
|
(if useClang then clangStdenv else stdenv).mkDerivation {
|
||||||
name = "nix";
|
name = "nix";
|
||||||
|
|
||||||
buildInputs = buildDeps ++ propagatedDeps ++ perlDeps ++ [ pkgs.rustfmt ];
|
buildInputs = buildDeps ++ propagatedDeps ++ perlDeps;
|
||||||
|
|
||||||
inherit configureFlags;
|
inherit configureFlags;
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,7 @@ static int _main(int argc, char * * argv)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Activity act(*logger, Verbosity::Talkative, ActivityType::Unknown, fmt("connecting to '%s'", bestMachine->storeUri));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri));
|
||||||
|
|
||||||
Store::Params storeParams;
|
Store::Params storeParams;
|
||||||
if (hasPrefix(bestMachine->storeUri, "ssh://")) {
|
if (hasPrefix(bestMachine->storeUri, "ssh://")) {
|
||||||
|
@ -200,9 +200,12 @@ static int _main(int argc, char * * argv)
|
||||||
|
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
auto msg = chomp(drainFD(5, false));
|
auto msg = chomp(drainFD(5, false));
|
||||||
printError("cannot build on '%s': %s%s",
|
logError({
|
||||||
bestMachine->storeUri, e.what(),
|
.name = "Remote build",
|
||||||
(msg.empty() ? "" : ": " + msg));
|
.hint = hintfmt("cannot build on '%s': %s%s",
|
||||||
|
bestMachine->storeUri, e.what(),
|
||||||
|
(msg.empty() ? "" : ": " + msg))
|
||||||
|
});
|
||||||
bestMachine->enabled = false;
|
bestMachine->enabled = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -222,7 +225,7 @@ connected:
|
||||||
AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true);
|
AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true);
|
||||||
|
|
||||||
{
|
{
|
||||||
Activity act(*logger, Verbosity::Talkative, ActivityType::Unknown, fmt("waiting for the upload lock to '%s'", storeUri));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri));
|
||||||
|
|
||||||
auto old = signal(SIGALRM, handleAlarm);
|
auto old = signal(SIGALRM, handleAlarm);
|
||||||
alarm(15 * 60);
|
alarm(15 * 60);
|
||||||
|
@ -235,13 +238,13 @@ connected:
|
||||||
auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute;
|
auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute;
|
||||||
|
|
||||||
{
|
{
|
||||||
Activity act(*logger, Verbosity::Talkative, ActivityType::Unknown, fmt("copying dependencies to '%s'", storeUri));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri));
|
||||||
copyPaths(store, ref<Store>(sshStore), store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
|
copyPaths(store, ref<Store>(sshStore), store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadLock = -1;
|
uploadLock = -1;
|
||||||
|
|
||||||
BasicDerivation drv(readDerivation(*store, store->realStoreDir + "/" + std::string(drvPath->to_string())));
|
auto drv = store->readDerivation(*drvPath);
|
||||||
drv.inputSrcs = store->parseStorePathSet(inputs);
|
drv.inputSrcs = store->parseStorePathSet(inputs);
|
||||||
|
|
||||||
auto result = sshStore->buildDerivation(*drvPath, drv);
|
auto result = sshStore->buildDerivation(*drvPath, drv);
|
||||||
|
@ -254,7 +257,7 @@ connected:
|
||||||
if (!store->isValidPath(store->parseStorePath(path))) missing.insert(store->parseStorePath(path));
|
if (!store->isValidPath(store->parseStorePath(path))) missing.insert(store->parseStorePath(path));
|
||||||
|
|
||||||
if (!missing.empty()) {
|
if (!missing.empty()) {
|
||||||
Activity act(*logger, Verbosity::Talkative, ActivityType::Unknown, fmt("copying outputs from '%s'", storeUri));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri));
|
||||||
for (auto & i : missing)
|
for (auto & i : missing)
|
||||||
store->locksHeld.insert(store->printStorePath(i)); /* FIXME: ugly */
|
store->locksHeld.insert(store->printStorePath(i)); /* FIXME: ugly */
|
||||||
copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs, NoSubstitute);
|
copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs, NoSubstitute);
|
||||||
|
|
|
@ -51,7 +51,7 @@ using string_to_base_map
|
||||||
= std::unordered_map<std::string, std::shared_ptr<base>>;
|
= std::unordered_map<std::string, std::shared_ptr<base>>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// if defined, `base` will retain type information in form of an enum struct
|
// if defined, `base` will retain type information in form of an enum class
|
||||||
// such that static_cast can be used instead of dynamic_cast
|
// such that static_cast can be used instead of dynamic_cast
|
||||||
// #define CPPTOML_NO_RTTI
|
// #define CPPTOML_NO_RTTI
|
||||||
|
|
||||||
|
@ -405,7 +405,7 @@ inline std::shared_ptr<table_array> make_table_array(bool is_inline = false);
|
||||||
|
|
||||||
#if defined(CPPTOML_NO_RTTI)
|
#if defined(CPPTOML_NO_RTTI)
|
||||||
/// Base type used to store underlying data type explicitly if RTTI is disabled
|
/// Base type used to store underlying data type explicitly if RTTI is disabled
|
||||||
enum struct base_type
|
enum class base_type
|
||||||
{
|
{
|
||||||
NONE,
|
NONE,
|
||||||
STRING,
|
STRING,
|
||||||
|
@ -2268,7 +2268,7 @@ class parser
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum struct parse_type
|
enum class parse_type
|
||||||
{
|
{
|
||||||
STRING = 1,
|
STRING = 1,
|
||||||
LOCAL_TIME,
|
LOCAL_TIME,
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
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 {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
static Strings parseAttrPath(const string & s)
|
static Strings parseAttrPath(std::string_view s)
|
||||||
{
|
{
|
||||||
Strings res;
|
Strings res;
|
||||||
string cur;
|
string cur;
|
||||||
string::const_iterator i = s.begin();
|
auto i = s.begin();
|
||||||
while (i != s.end()) {
|
while (i != s.end()) {
|
||||||
if (*i == '.') {
|
if (*i == '.') {
|
||||||
res.push_back(cur);
|
res.push_back(cur);
|
||||||
|
@ -19,7 +19,7 @@ static Strings parseAttrPath(const string & s)
|
||||||
++i;
|
++i;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (i == s.end())
|
if (i == s.end())
|
||||||
throw Error(format("missing closing quote in selection path '%1%'") % s);
|
throw Error("missing closing quote in selection path '%1%'", s);
|
||||||
if (*i == '"') break;
|
if (*i == '"') break;
|
||||||
cur.push_back(*i++);
|
cur.push_back(*i++);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,15 @@ static Strings parseAttrPath(const string & 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,
|
std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||||
Bindings & autoArgs, Value & vIn)
|
Bindings & autoArgs, Value & vIn)
|
||||||
{
|
{
|
||||||
|
@ -60,11 +69,11 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||||
|
|
||||||
if (v->type != tAttrs)
|
if (v->type != tAttrs)
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
format("the expression selected by the selection path '%1%' should be a set but is %2%")
|
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||||
% attrPath % showType(*v));
|
attrPath,
|
||||||
|
showType(*v));
|
||||||
if (attr.empty())
|
if (attr.empty())
|
||||||
throw Error(format("empty attribute name in selection path '%1%'") % attrPath);
|
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||||
|
|
||||||
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
||||||
if (a == v->attrs->end())
|
if (a == v->attrs->end())
|
||||||
|
@ -77,9 +86,9 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||||
|
|
||||||
if (!v->isList())
|
if (!v->isList())
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
format("the expression selected by the selection path '%1%' should be a list but is %2%")
|
"the expression selected by the selection path '%1%' should be a list but is %2%",
|
||||||
% attrPath % showType(*v));
|
attrPath,
|
||||||
|
showType(*v));
|
||||||
if (attrIndex >= v->listSize())
|
if (attrIndex >= v->listSize())
|
||||||
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", attrIndex, attrPath);
|
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", attrIndex, attrPath);
|
||||||
|
|
||||||
|
|
|
@ -16,4 +16,6 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||||
/* Heuristic to find the filename and lineno or a nix value. */
|
/* Heuristic to find the filename and lineno or a nix value. */
|
||||||
Pos findDerivationFilename(EvalState & state, Value & v, std::string what);
|
Pos findDerivationFilename(EvalState & state, Value & v, std::string what);
|
||||||
|
|
||||||
|
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,11 @@ public:
|
||||||
{
|
{
|
||||||
auto a = get(name);
|
auto a = get(name);
|
||||||
if (!a)
|
if (!a)
|
||||||
throw Error("attribute '%s' missing, at %s", name, pos);
|
throw Error({
|
||||||
|
.hint = hintfmt("attribute '%s' missing", name),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
return *a;
|
return *a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,20 +7,26 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const Pos & pos))
|
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
|
||||||
{
|
{
|
||||||
throw EvalError(format(s) % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt(s),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
||||||
{
|
{
|
||||||
throw TypeError(format(s) % showType(v));
|
throw TypeError(s, showType(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v, const Pos & pos))
|
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
|
||||||
{
|
{
|
||||||
throw TypeError(format(s) % showType(v) % pos);
|
throw TypeError({
|
||||||
|
.hint = hintfmt(s, showType(v)),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,7 +49,7 @@ void EvalState::forceValue(Value & v, const Pos & pos)
|
||||||
else if (v.type == tApp)
|
else if (v.type == tApp)
|
||||||
callFunction(*v.app.left, *v.app.right, v, noPos);
|
callFunction(*v.app.left, *v.app.right, v, noPos);
|
||||||
else if (v.type == tBlackhole)
|
else if (v.type == tBlackhole)
|
||||||
throwEvalError("infinite recursion encountered, at %1%", pos);
|
throwEvalError(pos, "infinite recursion encountered");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,7 +65,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type != tAttrs)
|
if (v.type != tAttrs)
|
||||||
throwTypeError("value is %1% while a set was expected, at %2%", v, pos);
|
throwTypeError(pos, "value is %1% while a set was expected", v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,7 +81,7 @@ inline void EvalState::forceList(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (!v.isList())
|
if (!v.isList())
|
||||||
throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
|
throwTypeError(pos, "value is %1% while a list was expected", v);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note: Various places expect the allocated memory to be zeroed. */
|
/* Note: Various places expect the allocated memory to be zeroed. */
|
||||||
|
|
|
@ -161,12 +161,12 @@ const Value *getPrimOp(const Value &v) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string showType(const Value & v)
|
string showType(ValueType type)
|
||||||
{
|
{
|
||||||
switch (v.type) {
|
switch (type) {
|
||||||
case tInt: return "an integer";
|
case tInt: return "an integer";
|
||||||
case tBool: return "a boolean";
|
case tBool: return "a Boolean";
|
||||||
case tString: return v.string.context ? "a string with context" : "a string";
|
case tString: return "a string";
|
||||||
case tPath: return "a path";
|
case tPath: return "a path";
|
||||||
case tNull: return "null";
|
case tNull: return "null";
|
||||||
case tAttrs: return "a set";
|
case tAttrs: return "a set";
|
||||||
|
@ -175,14 +175,27 @@ string showType(const Value & v)
|
||||||
case tApp: return "a function application";
|
case tApp: return "a function application";
|
||||||
case tLambda: return "a function";
|
case tLambda: return "a function";
|
||||||
case tBlackhole: return "a black hole";
|
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:
|
case tPrimOp:
|
||||||
return fmt("the built-in function '%s'", string(v.primOp->name));
|
return fmt("the built-in function '%s'", string(v.primOp->name));
|
||||||
case tPrimOpApp:
|
case tPrimOpApp:
|
||||||
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
|
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
|
||||||
case tExternal: return v.external->showType();
|
case tExternal: return v.external->showType();
|
||||||
case tFloat: return "a float";
|
default:
|
||||||
|
return showType(v.type);
|
||||||
}
|
}
|
||||||
abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -323,6 +336,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||||
, sOutputHash(symbols.create("outputHash"))
|
, sOutputHash(symbols.create("outputHash"))
|
||||||
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
|
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
|
||||||
, sOutputHashMode(symbols.create("outputHashMode"))
|
, sOutputHashMode(symbols.create("outputHashMode"))
|
||||||
|
, sRecurseForDerivations(symbols.create("recurseForDerivations"))
|
||||||
, repair(NoRepair)
|
, repair(NoRepair)
|
||||||
, store(store)
|
, store(store)
|
||||||
, baseEnv(allocEnv(128))
|
, baseEnv(allocEnv(128))
|
||||||
|
@ -471,14 +485,21 @@ Value * EvalState::addConstant(const string & name, Value & v)
|
||||||
Value * EvalState::addPrimOp(const string & name,
|
Value * EvalState::addPrimOp(const string & name,
|
||||||
size_t arity, PrimOpFun primOp)
|
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) {
|
if (arity == 0) {
|
||||||
|
auto vPrimOp = allocValue();
|
||||||
|
vPrimOp->type = tPrimOp;
|
||||||
|
vPrimOp->primOp = new PrimOp(primOp, 1, sym);
|
||||||
Value v;
|
Value v;
|
||||||
primOp(*this, noPos, nullptr, v);
|
mkApp(v, *vPrimOp, *vPrimOp);
|
||||||
return addConstant(name, v);
|
return addConstant(name, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
|
|
||||||
Symbol sym = symbols.create(name2);
|
|
||||||
v->type = tPrimOp;
|
v->type = tPrimOp;
|
||||||
v->primOp = new PrimOp(primOp, arity, sym);
|
v->primOp = new PrimOp(primOp, arity, sym);
|
||||||
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
||||||
|
@ -501,52 +522,74 @@ Value & EvalState::getBuiltin(const string & name)
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
||||||
{
|
{
|
||||||
throw EvalError(format(s) % s2);
|
throw EvalError(s, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const Pos & pos))
|
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2))
|
||||||
{
|
{
|
||||||
throw EvalError(format(s) % s2 % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt(s, s2),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
|
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
|
||||||
{
|
{
|
||||||
throw EvalError(format(s) % s2 % s3);
|
throw EvalError(s, s2, s3);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3, const Pos & pos))
|
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3))
|
||||||
{
|
{
|
||||||
throw EvalError(format(s) % s2 % s3 % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt(s, s2, s3),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const Symbol & sym, const Pos & p1, const Pos & p2))
|
LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2))
|
||||||
{
|
{
|
||||||
throw EvalError(format(s) % sym % p1 % p2);
|
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
||||||
|
throw EvalError({
|
||||||
|
.hint = hintfmt(s, sym, p2),
|
||||||
|
.nixCode = NixCode { .errPos = p1 }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
|
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
|
||||||
{
|
{
|
||||||
throw TypeError(format(s) % pos);
|
throw TypeError({
|
||||||
|
.hint = hintfmt(s),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1))
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1))
|
||||||
{
|
{
|
||||||
throw TypeError(format(s) % s1);
|
throw TypeError(s, s1);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const ExprLambda & fun, const Symbol & s2, const Pos & pos))
|
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
|
||||||
{
|
{
|
||||||
throw TypeError(format(s) % fun.showNamePos() % s2 % pos);
|
throw TypeError({
|
||||||
|
.hint = hintfmt(s, fun.showNamePos(), s2),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s1, const Pos & pos))
|
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
|
||||||
{
|
{
|
||||||
throw AssertionError(format(s) % s1 % pos);
|
throw AssertionError({
|
||||||
|
.hint = hintfmt(s, s1),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwUndefinedVarError(const char * s, const string & s1, const Pos & pos))
|
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1))
|
||||||
{
|
{
|
||||||
throw UndefinedVarError(format(s) % s1 % pos);
|
throw UndefinedVarError({
|
||||||
|
.hint = hintfmt(s, s1),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
|
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
|
||||||
|
@ -614,7 +657,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||||
return j->value;
|
return j->value;
|
||||||
}
|
}
|
||||||
if (!env->prevWith)
|
if (!env->prevWith)
|
||||||
throwUndefinedVarError("undefined variable '%1%' at %2%", var.name, var.pos);
|
throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name);
|
||||||
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -812,7 +855,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
|
||||||
Value v;
|
Value v;
|
||||||
e->eval(*this, env, v);
|
e->eval(*this, env, v);
|
||||||
if (v.type != tBool)
|
if (v.type != tBool)
|
||||||
throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
|
throwTypeError(pos, "value is %1% while a Boolean was expected", v);
|
||||||
return v.boolean;
|
return v.boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -926,7 +969,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
||||||
Bindings::iterator j = v.attrs->find(nameSym);
|
Bindings::iterator j = v.attrs->find(nameSym);
|
||||||
if (j != v.attrs->end())
|
if (j != v.attrs->end())
|
||||||
throwEvalError("dynamic attribute '%1%' at %2% already defined at %3%", nameSym, i.pos, *j->pos);
|
throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos);
|
||||||
|
|
||||||
i.valueExpr->setName(nameSym);
|
i.valueExpr->setName(nameSym);
|
||||||
/* Keep sorted order so find can catch duplicates */
|
/* Keep sorted order so find can catch duplicates */
|
||||||
|
@ -1014,7 +1057,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
} else {
|
} else {
|
||||||
state.forceAttrs(*vAttrs, pos);
|
state.forceAttrs(*vAttrs, pos);
|
||||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||||
throwEvalError("attribute '%1%' missing, at %2%", name, pos);
|
throwEvalError(pos, "attribute '%1%' missing", name);
|
||||||
}
|
}
|
||||||
vAttrs = j->value;
|
vAttrs = j->value;
|
||||||
pos2 = j->pos;
|
pos2 = j->pos;
|
||||||
|
@ -1140,7 +1183,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fun.type != tLambda)
|
if (fun.type != tLambda)
|
||||||
throwTypeError("attempt to call something which is not a function but %1%, at %2%", fun, pos);
|
throwTypeError(pos, "attempt to call something which is not a function but %1%", fun);
|
||||||
|
|
||||||
ExprLambda & lambda(*fun.lambda.fun);
|
ExprLambda & lambda(*fun.lambda.fun);
|
||||||
|
|
||||||
|
@ -1168,8 +1211,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||||
for (auto & i : lambda.formals->formals) {
|
for (auto & i : lambda.formals->formals) {
|
||||||
Bindings::iterator j = arg.attrs->find(i.name);
|
Bindings::iterator j = arg.attrs->find(i.name);
|
||||||
if (j == arg.attrs->end()) {
|
if (j == arg.attrs->end()) {
|
||||||
if (!i.def) throwTypeError("%1% called without required argument '%2%', at %3%",
|
if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'",
|
||||||
lambda, i.name, pos);
|
lambda, i.name);
|
||||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||||
} else {
|
} else {
|
||||||
attrsUsed++;
|
attrsUsed++;
|
||||||
|
@ -1184,7 +1227,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||||
user. */
|
user. */
|
||||||
for (auto & i : *arg.attrs)
|
for (auto & i : *arg.attrs)
|
||||||
if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
|
if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
|
||||||
throwTypeError("%1% called with unexpected argument '%2%', at %3%", lambda, i.name, pos);
|
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
|
||||||
abort(); // can't happen
|
abort(); // can't happen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1273,7 +1316,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||||
if (!state.evalBool(env, cond, pos)) {
|
if (!state.evalBool(env, cond, pos)) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
cond->show(out);
|
cond->show(out);
|
||||||
throwAssertionError("assertion '%1%' failed at %2%", out.str(), pos);
|
throwAssertionError(pos, "assertion '%1%' failed at %2%", out.str());
|
||||||
}
|
}
|
||||||
body->eval(state, env, v);
|
body->eval(state, env, v);
|
||||||
}
|
}
|
||||||
|
@ -1425,14 +1468,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
nf = n;
|
nf = n;
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp), pos);
|
throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp));
|
||||||
} else if (firstType == tFloat) {
|
} else if (firstType == tFloat) {
|
||||||
if (vTmp.type == tInt) {
|
if (vTmp.type == tInt) {
|
||||||
nf += vTmp.integer;
|
nf += vTmp.integer;
|
||||||
} else if (vTmp.type == tFloat) {
|
} else if (vTmp.type == tFloat) {
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
throwEvalError("cannot add %1% to a float, at %2%", showType(vTmp), pos);
|
throwEvalError(pos, "cannot add %1% to a float", showType(vTmp));
|
||||||
} else
|
} else
|
||||||
s << state.coerceToString(pos, vTmp, context, false, firstType == tString);
|
s << state.coerceToString(pos, vTmp, context, false, firstType == tString);
|
||||||
}
|
}
|
||||||
|
@ -1443,7 +1486,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
mkFloat(v, nf);
|
mkFloat(v, nf);
|
||||||
else if (firstType == tPath) {
|
else if (firstType == tPath) {
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
throwEvalError("a string that refers to a store path cannot be appended to a path, at %1%", pos);
|
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
||||||
auto path = canonPath(s.str());
|
auto path = canonPath(s.str());
|
||||||
mkPath(v, path.c_str());
|
mkPath(v, path.c_str());
|
||||||
} else
|
} else
|
||||||
|
@ -1492,7 +1535,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type != tInt)
|
if (v.type != tInt)
|
||||||
throwTypeError("value is %1% while an integer was expected, at %2%", v, pos);
|
throwTypeError(pos, "value is %1% while an integer was expected", v);
|
||||||
return v.integer;
|
return v.integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1503,7 +1546,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
|
||||||
if (v.type == tInt)
|
if (v.type == tInt)
|
||||||
return v.integer;
|
return v.integer;
|
||||||
else if (v.type != tFloat)
|
else if (v.type != tFloat)
|
||||||
throwTypeError("value is %1% while a float was expected, at %2%", v, pos);
|
throwTypeError(pos, "value is %1% while a float was expected", v);
|
||||||
return v.fpoint;
|
return v.fpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1512,7 +1555,7 @@ bool EvalState::forceBool(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type != tBool)
|
if (v.type != tBool)
|
||||||
throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
|
throwTypeError(pos, "value is %1% while a Boolean was expected", v);
|
||||||
return v.boolean;
|
return v.boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1527,7 +1570,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp && !isFunctor(v))
|
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp && !isFunctor(v))
|
||||||
throwTypeError("value is %1% while a function was expected, at %2%", v, pos);
|
throwTypeError(pos, "value is %1% while a function was expected", v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1536,7 +1579,7 @@ string EvalState::forceString(Value & v, const Pos & pos)
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type != tString) {
|
if (v.type != tString) {
|
||||||
if (pos)
|
if (pos)
|
||||||
throwTypeError("value is %1% while a string was expected, at %2%", v, pos);
|
throwTypeError(pos, "value is %1% while a string was expected", v);
|
||||||
else
|
else
|
||||||
throwTypeError("value is %1% while a string was expected", v);
|
throwTypeError("value is %1% while a string was expected", v);
|
||||||
}
|
}
|
||||||
|
@ -1565,8 +1608,8 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
|
||||||
string s = forceString(v, pos);
|
string s = forceString(v, pos);
|
||||||
if (v.string.context) {
|
if (v.string.context) {
|
||||||
if (pos)
|
if (pos)
|
||||||
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%'), at %3%",
|
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||||
v.string.s, v.string.context[0], pos);
|
v.string.s, v.string.context[0]);
|
||||||
else
|
else
|
||||||
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||||
v.string.s, v.string.context[0]);
|
v.string.s, v.string.context[0]);
|
||||||
|
@ -1622,7 +1665,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||||
return *maybeString;
|
return *maybeString;
|
||||||
}
|
}
|
||||||
auto i = v.attrs->find(sOutPath);
|
auto i = v.attrs->find(sOutPath);
|
||||||
if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string, at %1%", pos);
|
if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
|
||||||
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1653,7 +1696,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throwTypeError("cannot coerce %1% to a string, at %2%", v, pos);
|
throwTypeError(pos, "cannot coerce %1% to a string", v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1669,10 +1712,10 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||||
else {
|
else {
|
||||||
auto p = settings.readOnlyMode
|
auto p = settings.readOnlyMode
|
||||||
? store->computeStorePathForPath(std::string(baseNameOf(path)), checkSourcePath(path)).first
|
? store->computeStorePathForPath(std::string(baseNameOf(path)), checkSourcePath(path)).first
|
||||||
: store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), FileIngestionMethod::Recursive, HashType::SHA256, defaultPathFilter, repair);
|
: store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair);
|
||||||
dstPath = store->printStorePath(p);
|
dstPath = store->printStorePath(p);
|
||||||
srcToStore.insert_or_assign(path, std::move(p));
|
srcToStore.insert_or_assign(path, std::move(p));
|
||||||
printMsg(Verbosity::Chatty, "copied source '%1%' -> '%2%'", path, dstPath);
|
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, dstPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.insert(dstPath);
|
context.insert(dstPath);
|
||||||
|
@ -1684,7 +1727,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
||||||
{
|
{
|
||||||
string path = coerceToString(pos, v, context, false, false);
|
string path = coerceToString(pos, v, context, false, false);
|
||||||
if (path == "" || path[0] != '/')
|
if (path == "" || path[0] != '/')
|
||||||
throwEvalError("string '%1%' doesn't represent an absolute path, at %2%", path, pos);
|
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1891,8 +1934,10 @@ void EvalState::printStats()
|
||||||
|
|
||||||
string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
|
string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
|
||||||
{
|
{
|
||||||
throw TypeError(format("cannot coerce %1% to a string, at %2%") %
|
throw TypeError({
|
||||||
showType() % pos);
|
.hint = hintfmt("cannot coerce %1% to a string", showType()),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace nix {
|
||||||
|
|
||||||
class Store;
|
class Store;
|
||||||
class EvalState;
|
class EvalState;
|
||||||
struct StorePath;
|
class StorePath;
|
||||||
enum RepairFlag : bool;
|
enum RepairFlag : bool;
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,7 +74,8 @@ public:
|
||||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||||
sFile, sLine, sColumn, sFunctor, sToString,
|
sFile, sLine, sColumn, sFunctor, sToString,
|
||||||
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
|
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
|
||||||
sOutputHash, sOutputHashAlgo, sOutputHashMode;
|
sOutputHash, sOutputHashAlgo, sOutputHashMode,
|
||||||
|
sRecurseForDerivations;
|
||||||
Symbol sDerivationNix;
|
Symbol sDerivationNix;
|
||||||
|
|
||||||
/* If set, force copying files to the Nix store even if they
|
/* If set, force copying files to the Nix store even if they
|
||||||
|
@ -324,6 +325,7 @@ private:
|
||||||
|
|
||||||
|
|
||||||
/* Return a string representing the type of the value `v'. */
|
/* Return a string representing the type of the value `v'. */
|
||||||
|
string showType(ValueType type);
|
||||||
string showType(const Value & v);
|
string showType(const Value & v);
|
||||||
|
|
||||||
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
||||||
|
|
|
@ -6,13 +6,13 @@ namespace nix {
|
||||||
FunctionCallTrace::FunctionCallTrace(const Pos & pos) : pos(pos) {
|
FunctionCallTrace::FunctionCallTrace(const Pos & pos) : pos(pos) {
|
||||||
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
|
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
|
||||||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
|
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
|
||||||
printMsg(Verbosity::Info, "function-trace entered %1% at %2%", pos, ns.count());
|
printMsg(lvlInfo, "function-trace entered %1% at %2%", pos, ns.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionCallTrace::~FunctionCallTrace() {
|
FunctionCallTrace::~FunctionCallTrace() {
|
||||||
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
|
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
|
||||||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
|
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
|
||||||
printMsg(Verbosity::Info, "function-trace exited %1% at %2%", pos, ns.count());
|
printMsg(lvlInfo, "function-trace exited %1% at %2%", pos, ns.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,7 +348,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||||
should we recurse into it? => Only if it has a
|
should we recurse into it? => Only if it has a
|
||||||
`recurseForDerivations = true' attribute. */
|
`recurseForDerivations = true' attribute. */
|
||||||
if (i->value->type == tAttrs) {
|
if (i->value->type == tAttrs) {
|
||||||
Bindings::iterator j = i->value->attrs->find(state.symbols.create("recurseForDerivations"));
|
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
||||||
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos))
|
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos))
|
||||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,14 +127,14 @@ or { return OR_KW; }
|
||||||
try {
|
try {
|
||||||
yylval->n = boost::lexical_cast<int64_t>(yytext);
|
yylval->n = boost::lexical_cast<int64_t>(yytext);
|
||||||
} catch (const boost::bad_lexical_cast &) {
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
throw ParseError(format("invalid integer '%1%'") % yytext);
|
throw ParseError("invalid integer '%1%'", yytext);
|
||||||
}
|
}
|
||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
{FLOAT} { errno = 0;
|
{FLOAT} { errno = 0;
|
||||||
yylval->nf = strtod(yytext, 0);
|
yylval->nf = strtod(yytext, 0);
|
||||||
if (errno != 0)
|
if (errno != 0)
|
||||||
throw ParseError(format("invalid float '%1%'") % yytext);
|
throw ParseError("invalid float '%1%'", yytext);
|
||||||
return FLOAT;
|
return FLOAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,4 +219,3 @@ 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_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libmain -I src/libexpr
|
||||||
|
|
||||||
libexpr_LIBS = libutil libstore libfetchers libnixrust
|
libexpr_LIBS = libutil libstore libfetchers
|
||||||
|
|
||||||
libexpr_LDFLAGS =
|
libexpr_LDFLAGS =
|
||||||
ifneq ($(OS), FreeBSD)
|
ifneq ($(OS), FreeBSD)
|
||||||
|
|
|
@ -267,8 +267,11 @@ void ExprVar::bindVars(const StaticEnv & env)
|
||||||
/* Otherwise, the variable must be obtained from the nearest
|
/* Otherwise, the variable must be obtained from the nearest
|
||||||
enclosing `with'. If there is no `with', then we can issue an
|
enclosing `with'. If there is no `with', then we can issue an
|
||||||
"undefined variable" error now. */
|
"undefined variable" error now. */
|
||||||
if (withLevel == -1) throw UndefinedVarError(format("undefined variable '%1%' at %2%") % name % pos);
|
if (withLevel == -1)
|
||||||
|
throw UndefinedVarError({
|
||||||
|
.hint = hintfmt("undefined variable '%1%'", name),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
fromWith = true;
|
fromWith = true;
|
||||||
this->level = withLevel;
|
this->level = withLevel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "value.hh"
|
#include "value.hh"
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
|
#include "error.hh"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
@ -235,8 +236,10 @@ struct ExprLambda : Expr
|
||||||
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
|
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
|
||||||
{
|
{
|
||||||
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
||||||
throw ParseError(format("duplicate formal function argument '%1%' at %2%")
|
throw ParseError({
|
||||||
% arg % pos);
|
.hint = hintfmt("duplicate formal function argument '%1%'", arg),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
};
|
};
|
||||||
void setName(Symbol & name);
|
void setName(Symbol & name);
|
||||||
string showNamePos() const;
|
string showNamePos() const;
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace nix {
|
||||||
Expr * result;
|
Expr * result;
|
||||||
Path basePath;
|
Path basePath;
|
||||||
Symbol path;
|
Symbol path;
|
||||||
string error;
|
ErrorInfo error;
|
||||||
Symbol sLetBody;
|
Symbol sLetBody;
|
||||||
ParseData(EvalState & state)
|
ParseData(EvalState & state)
|
||||||
: state(state)
|
: state(state)
|
||||||
|
@ -64,15 +64,20 @@ namespace nix {
|
||||||
|
|
||||||
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
|
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
|
||||||
{
|
{
|
||||||
throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
|
throw ParseError({
|
||||||
% showAttrPath(attrPath) % pos % prevPos);
|
.hint = hintfmt("attribute '%1%' already defined at %2%",
|
||||||
|
showAttrPath(attrPath), prevPos),
|
||||||
|
.nixCode = NixCode { .errPos = pos },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
||||||
{
|
{
|
||||||
throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
|
throw ParseError({
|
||||||
% attr % pos % prevPos);
|
.hint = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos),
|
||||||
|
.nixCode = NixCode { .errPos = pos },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,8 +145,11 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||||
{
|
{
|
||||||
if (!formals->argNames.insert(formal.name).second)
|
if (!formals->argNames.insert(formal.name).second)
|
||||||
throw ParseError(format("duplicate formal function argument '%1%' at %2%")
|
throw ParseError({
|
||||||
% formal.name % pos);
|
.hint = hintfmt("duplicate formal function argument '%1%'",
|
||||||
|
formal.name),
|
||||||
|
.nixCode = NixCode { .errPos = pos },
|
||||||
|
});
|
||||||
formals->formals.push_front(formal);
|
formals->formals.push_front(formal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,8 +257,10 @@ static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||||
|
|
||||||
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
|
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
|
||||||
{
|
{
|
||||||
data->error = (format("%1%, at %2%")
|
data->error = {
|
||||||
% error % makeCurPos(*loc, data)).str();
|
.hint = hintfmt(error),
|
||||||
|
.nixCode = NixCode { .errPos = makeCurPos(*loc, data) }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -327,8 +337,10 @@ expr_function
|
||||||
{ $$ = new ExprWith(CUR_POS, $2, $4); }
|
{ $$ = new ExprWith(CUR_POS, $2, $4); }
|
||||||
| LET binds IN expr_function
|
| LET binds IN expr_function
|
||||||
{ if (!$2->dynamicAttrs.empty())
|
{ if (!$2->dynamicAttrs.empty())
|
||||||
throw ParseError(format("dynamic attributes not allowed in let at %1%")
|
throw ParseError({
|
||||||
% CUR_POS);
|
.hint = hintfmt("dynamic attributes not allowed in let"),
|
||||||
|
.nixCode = NixCode { .errPos = CUR_POS },
|
||||||
|
});
|
||||||
$$ = new ExprLet($2, $4);
|
$$ = new ExprLet($2, $4);
|
||||||
}
|
}
|
||||||
| expr_if
|
| expr_if
|
||||||
|
@ -405,7 +417,10 @@ expr_simple
|
||||||
| URI {
|
| URI {
|
||||||
static bool noURLLiterals = settings.isExperimentalFeatureEnabled("no-url-literals");
|
static bool noURLLiterals = settings.isExperimentalFeatureEnabled("no-url-literals");
|
||||||
if (noURLLiterals)
|
if (noURLLiterals)
|
||||||
throw ParseError("URL literals are disabled, at %s", CUR_POS);
|
throw ParseError({
|
||||||
|
.hint = hintfmt("URL literals are disabled"),
|
||||||
|
.nixCode = NixCode { .errPos = CUR_POS }
|
||||||
|
});
|
||||||
$$ = new ExprString(data->symbols.create($1));
|
$$ = new ExprString(data->symbols.create($1));
|
||||||
}
|
}
|
||||||
| '(' expr ')' { $$ = $2; }
|
| '(' expr ')' { $$ = $2; }
|
||||||
|
@ -475,8 +490,10 @@ attrs
|
||||||
$$->push_back(AttrName(str->s));
|
$$->push_back(AttrName(str->s));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
throw ParseError(format("dynamic attributes not allowed in inherit at %1%")
|
throw ParseError({
|
||||||
% makeCurPos(@2, data));
|
.hint = hintfmt("dynamic attributes not allowed in inherit"),
|
||||||
|
.nixCode = NixCode { .errPos = makeCurPos(@2, data) },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
| { $$ = new AttrPath; }
|
| { $$ = new AttrPath; }
|
||||||
;
|
;
|
||||||
|
@ -626,7 +643,7 @@ Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath)
|
||||||
|
|
||||||
Expr * EvalState::parseStdin()
|
Expr * EvalState::parseStdin()
|
||||||
{
|
{
|
||||||
//Activity act(*logger, Verbosity::Talkative, format("parsing standard input"));
|
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
|
||||||
return parseExprFromString(drainFD(0), absPath("."));
|
return parseExprFromString(drainFD(0), absPath("."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,11 +688,13 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
|
||||||
Path res = r.second + suffix;
|
Path res = r.second + suffix;
|
||||||
if (pathExists(res)) return canonPath(res);
|
if (pathExists(res)) return canonPath(res);
|
||||||
}
|
}
|
||||||
format f = format(
|
throw ThrownError({
|
||||||
"file '%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)"
|
.hint = hintfmt(evalSettings.pureEval
|
||||||
+ string(pos ? ", at %2%" : ""));
|
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
||||||
f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
|
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
||||||
throw ThrownError(f % path % pos);
|
path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -691,7 +710,10 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
|
||||||
res = { true, store->toRealPath(fetchers::downloadTarball(
|
res = { true, store->toRealPath(fetchers::downloadTarball(
|
||||||
store, resolveUri(elem.second), "source", false).storePath) };
|
store, resolveUri(elem.second), "source", false).storePath) };
|
||||||
} catch (FileTransferError & e) {
|
} catch (FileTransferError & e) {
|
||||||
printError(format("warning: Nix search path entry '%1%' cannot be downloaded, ignoring") % elem.second);
|
logWarning({
|
||||||
|
.name = "Entry download",
|
||||||
|
.hint = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second)
|
||||||
|
});
|
||||||
res = { false, "" };
|
res = { false, "" };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -699,7 +721,10 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
|
||||||
if (pathExists(path))
|
if (pathExists(path))
|
||||||
res = { true, path };
|
res = { true, path };
|
||||||
else {
|
else {
|
||||||
printError(format("warning: Nix search path entry '%1%' does not exist, ignoring") % elem.second);
|
logWarning({
|
||||||
|
.name = "Entry not found",
|
||||||
|
.hint = hintfmt("warning: Nix search path entry '%1%' does not exist, ignoring", elem.second)
|
||||||
|
});
|
||||||
res = { false, "" };
|
res = { false, "" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,20 +50,20 @@ void EvalState::realiseContext(const PathSet & context)
|
||||||
std::vector<StorePathWithOutputs> drvs;
|
std::vector<StorePathWithOutputs> drvs;
|
||||||
|
|
||||||
for (auto & i : context) {
|
for (auto & i : context) {
|
||||||
std::pair<string, string> decoded = decodeContext(i);
|
auto [ctxS, outputName] = decodeContext(i);
|
||||||
auto ctx = store->parseStorePath(decoded.first);
|
auto ctx = store->parseStorePath(ctxS);
|
||||||
if (!store->isValidPath(ctx))
|
if (!store->isValidPath(ctx))
|
||||||
throw InvalidPathError(store->printStorePath(ctx));
|
throw InvalidPathError(store->printStorePath(ctx));
|
||||||
if (!decoded.second.empty() && ctx.isDerivation()) {
|
if (!outputName.empty() && ctx.isDerivation()) {
|
||||||
drvs.push_back(StorePathWithOutputs{ctx.clone(), {decoded.second}});
|
drvs.push_back(StorePathWithOutputs{ctx, {outputName}});
|
||||||
|
|
||||||
/* Add the output of this derivation to the allowed
|
/* Add the output of this derivation to the allowed
|
||||||
paths. */
|
paths. */
|
||||||
if (allowedPaths) {
|
if (allowedPaths) {
|
||||||
auto drv = store->derivationFromPath(store->parseStorePath(decoded.first));
|
auto drv = store->derivationFromPath(ctx);
|
||||||
DerivationOutputs::iterator i = drv.outputs.find(decoded.second);
|
DerivationOutputs::iterator i = drv.outputs.find(outputName);
|
||||||
if (i == drv.outputs.end())
|
if (i == drv.outputs.end())
|
||||||
throw Error("derivation '%s' does not have an output named '%s'", decoded.first, decoded.second);
|
throw Error("derivation '%s' does not have an output named '%s'", ctxS, outputName);
|
||||||
allowedPaths->insert(store->printStorePath(i->second.path));
|
allowedPaths->insert(store->printStorePath(i->second.path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ void EvalState::realiseContext(const PathSet & context)
|
||||||
StorePathSet willBuild, willSubstitute, unknown;
|
StorePathSet willBuild, willSubstitute, unknown;
|
||||||
unsigned long long downloadSize, narSize;
|
unsigned long long downloadSize, narSize;
|
||||||
store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||||
|
|
||||||
store->buildPaths(drvs);
|
store->buildPaths(drvs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,8 +94,10 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
state.realiseContext(context);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
|
throw EvalError({
|
||||||
% path % e.path % pos);
|
.hint = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
|
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
|
||||||
|
@ -170,8 +173,12 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
state.realiseContext(context);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
|
throw EvalError({
|
||||||
% path % e.path % pos);
|
.hint = hintfmt(
|
||||||
|
"cannot import '%1%', since path '%2%' is not valid",
|
||||||
|
path, e.path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
path = state.checkSourcePath(path);
|
path = state.checkSourcePath(path);
|
||||||
|
@ -180,17 +187,17 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
|
|
||||||
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||||
if (!handle)
|
if (!handle)
|
||||||
throw EvalError(format("could not open '%1%': %2%") % path % dlerror());
|
throw EvalError("could not open '%1%': %2%", path, dlerror());
|
||||||
|
|
||||||
dlerror();
|
dlerror();
|
||||||
ValueInitializer func = (ValueInitializer) dlsym(handle, sym.c_str());
|
ValueInitializer func = (ValueInitializer) dlsym(handle, sym.c_str());
|
||||||
if(!func) {
|
if(!func) {
|
||||||
char *message = dlerror();
|
char *message = dlerror();
|
||||||
if (message)
|
if (message)
|
||||||
throw EvalError(format("could not load symbol '%1%' from '%2%': %3%") % sym % path % message);
|
throw EvalError("could not load symbol '%1%' from '%2%': %3%", sym, path, message);
|
||||||
else
|
else
|
||||||
throw EvalError(format("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected")
|
throw EvalError("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected",
|
||||||
% sym % path);
|
sym, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
(func)(state, v);
|
(func)(state, v);
|
||||||
|
@ -206,7 +213,10 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
auto elems = args[0]->listElems();
|
auto elems = args[0]->listElems();
|
||||||
auto count = args[0]->listSize();
|
auto count = args[0]->listSize();
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
throw EvalError(format("at least one argument to 'exec' required, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("at least one argument to 'exec' required"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto program = state.coerceToString(pos, *elems[0], context, false, false);
|
auto program = state.coerceToString(pos, *elems[0], context, false, false);
|
||||||
|
@ -217,8 +227,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
state.realiseContext(context);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError(format("cannot execute '%1%', since path '%2%' is not valid, at %3%")
|
throw EvalError({
|
||||||
% program % e.path % pos);
|
.hint = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
|
||||||
|
program, e.path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto output = runProgram(program, true, commandArgs);
|
auto output = runProgram(program, true, commandArgs);
|
||||||
|
@ -226,13 +239,13 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
try {
|
try {
|
||||||
parsed = state.parseExprFromString(output, pos.file);
|
parsed = state.parseExprFromString(output, pos.file);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addPrefix(format("While parsing the output from '%1%', at %2%\n") % program % pos);
|
e.addPrefix(fmt("While parsing the output from '%1%', at %2%\n", program, pos));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
state.eval(parsed, v);
|
state.eval(parsed, v);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addPrefix(format("While evaluating the output from '%1%', at %2%\n") % program % pos);
|
e.addPrefix(fmt("While evaluating the output from '%1%', at %2%\n", program, pos));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,7 +351,7 @@ struct CompareValues
|
||||||
if (v1->type == tInt && v2->type == tFloat)
|
if (v1->type == tInt && v2->type == tFloat)
|
||||||
return v1->integer < v2->fpoint;
|
return v1->integer < v2->fpoint;
|
||||||
if (v1->type != v2->type)
|
if (v1->type != v2->type)
|
||||||
throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
|
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
|
||||||
switch (v1->type) {
|
switch (v1->type) {
|
||||||
case tInt:
|
case tInt:
|
||||||
return v1->integer < v2->integer;
|
return v1->integer < v2->integer;
|
||||||
|
@ -349,7 +362,7 @@ struct CompareValues
|
||||||
case tPath:
|
case tPath:
|
||||||
return strcmp(v1->path, v2->path) < 0;
|
return strcmp(v1->path, v2->path) < 0;
|
||||||
default:
|
default:
|
||||||
throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
|
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -370,7 +383,10 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||||
Bindings::iterator startSet =
|
Bindings::iterator startSet =
|
||||||
args[0]->attrs->find(state.symbols.create("startSet"));
|
args[0]->attrs->find(state.symbols.create("startSet"));
|
||||||
if (startSet == args[0]->attrs->end())
|
if (startSet == args[0]->attrs->end())
|
||||||
throw EvalError(format("attribute 'startSet' required, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("attribute 'startSet' required"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
state.forceList(*startSet->value, pos);
|
state.forceList(*startSet->value, pos);
|
||||||
|
|
||||||
ValueList workSet;
|
ValueList workSet;
|
||||||
|
@ -381,7 +397,10 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||||
Bindings::iterator op =
|
Bindings::iterator op =
|
||||||
args[0]->attrs->find(state.symbols.create("operator"));
|
args[0]->attrs->find(state.symbols.create("operator"));
|
||||||
if (op == args[0]->attrs->end())
|
if (op == args[0]->attrs->end())
|
||||||
throw EvalError(format("attribute 'operator' required, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("attribute 'operator' required"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
state.forceValue(*op->value, pos);
|
state.forceValue(*op->value, pos);
|
||||||
|
|
||||||
/* Construct the closure by applying the operator to element of
|
/* Construct the closure by applying the operator to element of
|
||||||
|
@ -400,7 +419,10 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||||
Bindings::iterator key =
|
Bindings::iterator key =
|
||||||
e->attrs->find(state.symbols.create("key"));
|
e->attrs->find(state.symbols.create("key"));
|
||||||
if (key == e->attrs->end())
|
if (key == e->attrs->end())
|
||||||
throw EvalError(format("attribute 'key' required, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("attribute 'key' required"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
state.forceValue(*key->value, pos);
|
state.forceValue(*key->value, pos);
|
||||||
|
|
||||||
if (!doneKeys.insert(key->value).second) continue;
|
if (!doneKeys.insert(key->value).second) continue;
|
||||||
|
@ -430,7 +452,7 @@ static void prim_abort(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string s = state.coerceToString(pos, *args[0], context);
|
string s = state.coerceToString(pos, *args[0], context);
|
||||||
throw Abort(format("evaluation aborted with the following error message: '%1%'") % s);
|
throw Abort("evaluation aborted with the following error message: '%1%'", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -505,9 +527,9 @@ static void prim_trace(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
{
|
{
|
||||||
state.forceValue(*args[0], pos);
|
state.forceValue(*args[0], pos);
|
||||||
if (args[0]->type == tString)
|
if (args[0]->type == tString)
|
||||||
printError(format("trace: %1%") % args[0]->string.s);
|
printError("trace: %1%", args[0]->string.s);
|
||||||
else
|
else
|
||||||
printError(format("trace: %1%") % *args[0]);
|
printError("trace: %1%", *args[0]);
|
||||||
state.forceValue(*args[1], pos);
|
state.forceValue(*args[1], pos);
|
||||||
v = *args[1];
|
v = *args[1];
|
||||||
}
|
}
|
||||||
|
@ -532,13 +554,16 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
/* Figure out the name first (for stack backtraces). */
|
/* Figure out the name first (for stack backtraces). */
|
||||||
Bindings::iterator attr = args[0]->attrs->find(state.sName);
|
Bindings::iterator attr = args[0]->attrs->find(state.sName);
|
||||||
if (attr == args[0]->attrs->end())
|
if (attr == args[0]->attrs->end())
|
||||||
throw EvalError(format("required attribute 'name' missing, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("required attribute 'name' missing"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
string drvName;
|
string drvName;
|
||||||
Pos & posDrvName(*attr->pos);
|
Pos & posDrvName(*attr->pos);
|
||||||
try {
|
try {
|
||||||
drvName = state.forceStringNoCtx(*attr->value, pos);
|
drvName = state.forceStringNoCtx(*attr->value, pos);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addPrefix(format("while evaluating the derivation attribute 'name' at %1%:\n") % posDrvName);
|
e.addPrefix(fmt("while evaluating the derivation attribute 'name' at %1%:\n", posDrvName));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,25 +600,38 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
auto handleHashMode = [&](const std::string & s) {
|
auto handleHashMode = [&](const std::string & s) {
|
||||||
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
|
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
|
||||||
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
|
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
|
||||||
else throw EvalError("invalid value '%s' for 'outputHashMode' attribute, at %s", s, posDrvName);
|
else
|
||||||
|
throw EvalError({
|
||||||
|
.hint = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
|
||||||
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
auto handleOutputs = [&](const Strings & ss) {
|
auto handleOutputs = [&](const Strings & ss) {
|
||||||
outputs.clear();
|
outputs.clear();
|
||||||
for (auto & j : ss) {
|
for (auto & j : ss) {
|
||||||
if (outputs.find(j) != outputs.end())
|
if (outputs.find(j) != outputs.end())
|
||||||
throw EvalError(format("duplicate derivation output '%1%', at %2%") % j % posDrvName);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("duplicate derivation output '%1%'", j),
|
||||||
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
|
});
|
||||||
/* !!! Check whether j is a valid attribute
|
/* !!! Check whether j is a valid attribute
|
||||||
name. */
|
name. */
|
||||||
/* Derivations cannot be named ‘drv’, because
|
/* Derivations cannot be named ‘drv’, because
|
||||||
then we'd have an attribute ‘drvPath’ in
|
then we'd have an attribute ‘drvPath’ in
|
||||||
the resulting set. */
|
the resulting set. */
|
||||||
if (j == "drv")
|
if (j == "drv")
|
||||||
throw EvalError(format("invalid derivation output name 'drv', at %1%") % posDrvName);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("invalid derivation output name 'drv'" ),
|
||||||
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
|
});
|
||||||
outputs.insert(j);
|
outputs.insert(j);
|
||||||
}
|
}
|
||||||
if (outputs.empty())
|
if (outputs.empty())
|
||||||
throw EvalError(format("derivation cannot have an empty set of outputs, at %1%") % posDrvName);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("derivation cannot have an empty set of outputs"),
|
||||||
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -686,9 +724,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
StorePathSet refs;
|
StorePathSet refs;
|
||||||
state.store->computeFSClosure(state.store->parseStorePath(std::string_view(path).substr(1)), refs);
|
state.store->computeFSClosure(state.store->parseStorePath(std::string_view(path).substr(1)), refs);
|
||||||
for (auto & j : refs) {
|
for (auto & j : refs) {
|
||||||
drv.inputSrcs.insert(j.clone());
|
drv.inputSrcs.insert(j);
|
||||||
if (j.isDerivation())
|
if (j.isDerivation())
|
||||||
drv.inputDrvs[j.clone()] = state.store->queryDerivationOutputNames(j);
|
drv.inputDrvs[j] = state.store->readDerivation(j).outputNames();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,27 +743,41 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
|
|
||||||
/* Do we have all required attributes? */
|
/* Do we have all required attributes? */
|
||||||
if (drv.builder == "")
|
if (drv.builder == "")
|
||||||
throw EvalError(format("required attribute 'builder' missing, at %1%") % posDrvName);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("required attribute 'builder' missing"),
|
||||||
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
|
});
|
||||||
|
|
||||||
if (drv.platform == "")
|
if (drv.platform == "")
|
||||||
throw EvalError(format("required attribute 'system' missing, at %1%") % posDrvName);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("required attribute 'system' missing"),
|
||||||
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
|
});
|
||||||
|
|
||||||
/* Check whether the derivation name is valid. */
|
/* Check whether the derivation name is valid. */
|
||||||
if (isDerivation(drvName))
|
if (isDerivation(drvName))
|
||||||
throw EvalError("derivation names are not allowed to end in '%s', at %s", drvExtension, posDrvName);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("derivation names are not allowed to end in '%s'", drvExtension),
|
||||||
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
|
});
|
||||||
|
|
||||||
if (outputHash) {
|
if (outputHash) {
|
||||||
/* Handle fixed-output derivations. */
|
/* Handle fixed-output derivations. */
|
||||||
if (outputs.size() != 1 || *(outputs.begin()) != "out")
|
if (outputs.size() != 1 || *(outputs.begin()) != "out")
|
||||||
throw Error(format("multiple outputs are not supported in fixed-output derivations, at %1%") % posDrvName);
|
throw Error({
|
||||||
|
.hint = hintfmt("multiple outputs are not supported in fixed-output derivations"),
|
||||||
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
|
});
|
||||||
|
|
||||||
std::optional<HashType> ht = parseHashTypeOpt(outputHashAlgo);
|
std::optional<HashType> ht = parseHashTypeOpt(outputHashAlgo);
|
||||||
Hash h(*outputHash, ht);
|
Hash h = newHashAllowEmpty(*outputHash, ht);
|
||||||
|
|
||||||
auto outPath = state.store->makeFixedOutputPath(ingestionMethod, h, drvName);
|
auto outPath = state.store->makeFixedOutputPath(ingestionMethod, h, drvName);
|
||||||
if (!jsonObject) drv.env["out"] = state.store->printStorePath(outPath);
|
if (!jsonObject) drv.env["out"] = state.store->printStorePath(outPath);
|
||||||
drv.outputs.insert_or_assign("out", DerivationOutput(
|
drv.outputs.insert_or_assign("out", DerivationOutput {
|
||||||
std::move(outPath),
|
.path = std::move(outPath),
|
||||||
FileSystemHash(ingestionMethod, std::move(h))));
|
.hash = FileSystemHash { ingestionMethod, std::move(h) },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -738,7 +790,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
for (auto & i : outputs) {
|
for (auto & i : outputs) {
|
||||||
if (!jsonObject) drv.env[i] = "";
|
if (!jsonObject) drv.env[i] = "";
|
||||||
drv.outputs.insert_or_assign(i,
|
drv.outputs.insert_or_assign(i,
|
||||||
DerivationOutput(StorePath::dummy.clone(), std::optional<FileSystemHash>()));
|
DerivationOutput {
|
||||||
|
.path = StorePath::dummy,
|
||||||
|
.hash = std::optional<FileSystemHash> {},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Hash h = hashDerivationModulo(*state.store, Derivation(drv), true);
|
Hash h = hashDerivationModulo(*state.store, Derivation(drv), true);
|
||||||
|
@ -747,7 +802,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
auto outPath = state.store->makeOutputPath(i, h, drvName);
|
auto outPath = state.store->makeOutputPath(i, h, drvName);
|
||||||
if (!jsonObject) drv.env[i] = state.store->printStorePath(outPath);
|
if (!jsonObject) drv.env[i] = state.store->printStorePath(outPath);
|
||||||
drv.outputs.insert_or_assign(i,
|
drv.outputs.insert_or_assign(i,
|
||||||
DerivationOutput(std::move(outPath), std::optional<FileSystemHash>()));
|
DerivationOutput {
|
||||||
|
.path = std::move(outPath),
|
||||||
|
.hash = std::optional<FileSystemHash>(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,12 +813,12 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
auto drvPath = writeDerivation(state.store, drv, drvName, state.repair);
|
auto drvPath = writeDerivation(state.store, drv, drvName, state.repair);
|
||||||
auto drvPathS = state.store->printStorePath(drvPath);
|
auto drvPathS = state.store->printStorePath(drvPath);
|
||||||
|
|
||||||
printMsg(Verbosity::Chatty, "instantiated '%1%' -> '%2%'", drvName, drvPathS);
|
printMsg(lvlChatty, "instantiated '%1%' -> '%2%'", drvName, drvPathS);
|
||||||
|
|
||||||
/* Optimisation, but required in read-only mode! because in that
|
/* Optimisation, but required in read-only mode! because in that
|
||||||
case we don't actually write store derivations, so we can't
|
case we don't actually write store derivations, so we can't
|
||||||
read them later. */
|
read them later. */
|
||||||
drvHashes.insert_or_assign(drvPath.clone(),
|
drvHashes.insert_or_assign(drvPath,
|
||||||
hashDerivationModulo(*state.store, Derivation(drv), false));
|
hashDerivationModulo(*state.store, Derivation(drv), false));
|
||||||
|
|
||||||
state.mkAttrs(v, 1 + drv.outputs.size());
|
state.mkAttrs(v, 1 + drv.outputs.size());
|
||||||
|
@ -817,7 +875,10 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
|
||||||
e.g. nix-push does the right thing. */
|
e.g. nix-push does the right thing. */
|
||||||
if (!state.store->isStorePath(path)) path = canonPath(path, true);
|
if (!state.store->isStorePath(path)) path = canonPath(path, true);
|
||||||
if (!state.store->isInStore(path))
|
if (!state.store->isInStore(path))
|
||||||
throw EvalError(format("path '%1%' is not in the Nix store, at %2%") % path % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("path '%1%' is not in the Nix store", path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
Path path2 = state.store->toStorePath(path);
|
Path path2 = state.store->toStorePath(path);
|
||||||
if (!settings.readOnlyMode)
|
if (!settings.readOnlyMode)
|
||||||
state.store->ensurePath(state.store->parseStorePath(path2));
|
state.store->ensurePath(state.store->parseStorePath(path2));
|
||||||
|
@ -833,9 +894,12 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
state.realiseContext(context);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError(format(
|
throw EvalError({
|
||||||
"cannot check the existence of '%1%', since path '%2%' is not valid, at %3%")
|
.hint = hintfmt(
|
||||||
% path % e.path % pos);
|
"cannot check the existence of '%1%', since path '%2%' is not valid",
|
||||||
|
path, e.path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -878,12 +942,14 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
state.realiseContext(context);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
|
throw EvalError({
|
||||||
% path % e.path % pos);
|
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
string s = readFile(state.checkSourcePath(state.toRealPath(path, context)));
|
string s = readFile(state.checkSourcePath(state.toRealPath(path, context)));
|
||||||
if (s.find((char) 0) != string::npos)
|
if (s.find((char) 0) != string::npos)
|
||||||
throw Error(format("the contents of the file '%1%' cannot be represented as a Nix string") % path);
|
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
|
||||||
mkString(v, s.c_str());
|
mkString(v, s.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,7 +973,10 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
|
|
||||||
i = v2.attrs->find(state.symbols.create("path"));
|
i = v2.attrs->find(state.symbols.create("path"));
|
||||||
if (i == v2.attrs->end())
|
if (i == v2.attrs->end())
|
||||||
throw EvalError(format("attribute 'path' missing, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("attribute 'path' missing"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string path = state.coerceToString(pos, *i->value, context, false, false);
|
string path = state.coerceToString(pos, *i->value, context, false, false);
|
||||||
|
@ -915,8 +984,10 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
state.realiseContext(context);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError(format("cannot find '%1%', since path '%2%' is not valid, at %3%")
|
throw EvalError({
|
||||||
% path % e.path % pos);
|
.hint = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
searchPath.emplace_back(prefix, path);
|
searchPath.emplace_back(prefix, path);
|
||||||
|
@ -933,12 +1004,15 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
string type = state.forceStringNoCtx(*args[0], pos);
|
string type = state.forceStringNoCtx(*args[0], pos);
|
||||||
std::optional<HashType> ht = parseHashType(type);
|
std::optional<HashType> ht = parseHashType(type);
|
||||||
if (!ht)
|
if (!ht)
|
||||||
throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
|
throw Error({
|
||||||
|
.hint = hintfmt("unknown hash type '%1%'", type),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
PathSet context; // discarded
|
PathSet context; // discarded
|
||||||
Path p = state.coerceToPath(pos, *args[1], context);
|
Path p = state.coerceToPath(pos, *args[1], context);
|
||||||
|
|
||||||
mkString(v, hashFile(*ht, state.checkSourcePath(p)).to_string(Base::Base16, false), context);
|
mkString(v, hashFile(*ht, state.checkSourcePath(p)).to_string(Base16, false), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read a directory (without . or ..) */
|
/* Read a directory (without . or ..) */
|
||||||
|
@ -949,8 +1023,10 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
|
||||||
try {
|
try {
|
||||||
state.realiseContext(ctx);
|
state.realiseContext(ctx);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
|
throw EvalError({
|
||||||
% path % e.path % pos);
|
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DirEntries entries = readDirectory(state.checkSourcePath(path));
|
DirEntries entries = readDirectory(state.checkSourcePath(path));
|
||||||
|
@ -1020,9 +1096,13 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||||
|
|
||||||
for (auto path : context) {
|
for (auto path : context) {
|
||||||
if (path.at(0) != '/')
|
if (path.at(0) != '/')
|
||||||
throw EvalError(format(
|
throw EvalError( {
|
||||||
"in 'toFile': the file named '%1%' must not contain a reference "
|
.hint = hintfmt(
|
||||||
"to a derivation but contains (%2%), at %3%") % name % path % pos);
|
"in 'toFile': the file named '%1%' must not contain a reference "
|
||||||
|
"to a derivation but contains (%2%)",
|
||||||
|
name, path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
refs.insert(state.store->parseStorePath(path));
|
refs.insert(state.store->parseStorePath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1074,8 +1154,8 @@ static void addPath(EvalState & state, const Pos & pos, const string & name, con
|
||||||
Path dstPath;
|
Path dstPath;
|
||||||
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
||||||
dstPath = state.store->printStorePath(settings.readOnlyMode
|
dstPath = state.store->printStorePath(settings.readOnlyMode
|
||||||
? state.store->computeStorePathForPath(name, path, method, HashType::SHA256, filter).first
|
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
|
||||||
: state.store->addToStore(name, path, method, HashType::SHA256, filter, state.repair));
|
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair));
|
||||||
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
|
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
|
||||||
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
|
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
|
||||||
} else
|
} else
|
||||||
|
@ -1090,11 +1170,19 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
||||||
PathSet context;
|
PathSet context;
|
||||||
Path path = state.coerceToPath(pos, *args[1], context);
|
Path path = state.coerceToPath(pos, *args[1], context);
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("string '%1%' cannot refer to other paths", path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
state.forceValue(*args[0], pos);
|
state.forceValue(*args[0], pos);
|
||||||
if (args[0]->type != tLambda)
|
if (args[0]->type != tLambda)
|
||||||
throw TypeError(format("first argument in call to 'filterSource' is not a function but %1%, at %2%") % showType(*args[0]) % pos);
|
throw TypeError({
|
||||||
|
.hint = hintfmt(
|
||||||
|
"first argument in call to 'filterSource' is not a function but %1%",
|
||||||
|
showType(*args[0])),
|
||||||
|
.nixCode = NixCode { .errPos = 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], FileIngestionMethod::Recursive, Hash(), v);
|
||||||
}
|
}
|
||||||
|
@ -1114,7 +1202,10 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
PathSet context;
|
PathSet context;
|
||||||
path = state.coerceToPath(*attr.pos, *attr.value, context);
|
path = state.coerceToPath(*attr.pos, *attr.value, context);
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % *attr.pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("string '%1%' cannot refer to other paths", path),
|
||||||
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
|
});
|
||||||
} else if (attr.name == state.sName)
|
} else if (attr.name == state.sName)
|
||||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||||
else if (n == "filter") {
|
else if (n == "filter") {
|
||||||
|
@ -1123,12 +1214,18 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
} else if (n == "recursive")
|
} else if (n == "recursive")
|
||||||
method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos) };
|
method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos) };
|
||||||
else if (n == "sha256")
|
else if (n == "sha256")
|
||||||
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), HashType::SHA256);
|
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||||
else
|
else
|
||||||
throw EvalError(format("unsupported argument '%1%' to 'addPath', at %2%") % attr.name % *attr.pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name),
|
||||||
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
throw EvalError(format("'path' required, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("'path' required"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
name = baseNameOf(path);
|
name = baseNameOf(path);
|
||||||
|
|
||||||
|
@ -1186,7 +1283,10 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
// !!! Should we create a symbol here or just do a lookup?
|
// !!! Should we create a symbol here or just do a lookup?
|
||||||
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
|
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
|
||||||
if (i == args[1]->attrs->end())
|
if (i == args[1]->attrs->end())
|
||||||
throw EvalError(format("attribute '%1%' missing, at %2%") % attr % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("attribute '%1%' missing", attr),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
// !!! add to stack trace?
|
// !!! add to stack trace?
|
||||||
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
|
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
|
||||||
state.forceValue(*i->value, pos);
|
state.forceValue(*i->value, pos);
|
||||||
|
@ -1266,15 +1366,20 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||||
|
|
||||||
Bindings::iterator j = v2.attrs->find(state.sName);
|
Bindings::iterator j = v2.attrs->find(state.sName);
|
||||||
if (j == v2.attrs->end())
|
if (j == v2.attrs->end())
|
||||||
throw TypeError(format("'name' attribute missing in a call to 'listToAttrs', at %1%") % pos);
|
throw TypeError({
|
||||||
|
.hint = hintfmt("'name' attribute missing in a call to 'listToAttrs'"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
string name = state.forceStringNoCtx(*j->value, pos);
|
string name = state.forceStringNoCtx(*j->value, pos);
|
||||||
|
|
||||||
Symbol sym = state.symbols.create(name);
|
Symbol sym = state.symbols.create(name);
|
||||||
if (seen.insert(sym).second) {
|
if (seen.insert(sym).second) {
|
||||||
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
|
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
|
||||||
if (j2 == v2.attrs->end())
|
if (j2 == v2.attrs->end())
|
||||||
throw TypeError(format("'value' attribute missing in a call to 'listToAttrs', at %1%") % pos);
|
throw TypeError({
|
||||||
|
.hint = hintfmt("'value' attribute missing in a call to 'listToAttrs'"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1347,7 +1452,10 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
|
||||||
{
|
{
|
||||||
state.forceValue(*args[0], pos);
|
state.forceValue(*args[0], pos);
|
||||||
if (args[0]->type != tLambda)
|
if (args[0]->type != tLambda)
|
||||||
throw TypeError(format("'functionArgs' requires a function, at %1%") % pos);
|
throw TypeError({
|
||||||
|
.hint = hintfmt("'functionArgs' requires a function"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
if (!args[0]->lambda.fun->matchAttrs) {
|
if (!args[0]->lambda.fun->matchAttrs) {
|
||||||
state.mkAttrs(v, 0);
|
state.mkAttrs(v, 0);
|
||||||
|
@ -1400,7 +1508,10 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu
|
||||||
{
|
{
|
||||||
state.forceList(list, pos);
|
state.forceList(list, pos);
|
||||||
if (n < 0 || (unsigned int) n >= list.listSize())
|
if (n < 0 || (unsigned int) n >= list.listSize())
|
||||||
throw Error(format("list index %1% is out of bounds, at %2%") % n % pos);
|
throw Error({
|
||||||
|
.hint = hintfmt("list index %1% is out of bounds", n),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
state.forceValue(*list.listElems()[n], pos);
|
state.forceValue(*list.listElems()[n], pos);
|
||||||
v = *list.listElems()[n];
|
v = *list.listElems()[n];
|
||||||
}
|
}
|
||||||
|
@ -1427,7 +1538,11 @@ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
{
|
{
|
||||||
state.forceList(*args[0], pos);
|
state.forceList(*args[0], pos);
|
||||||
if (args[0]->listSize() == 0)
|
if (args[0]->listSize() == 0)
|
||||||
throw Error(format("'tail' called on an empty list, at %1%") % pos);
|
throw Error({
|
||||||
|
.hint = hintfmt("'tail' called on an empty list"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
state.mkList(v, args[0]->listSize() - 1);
|
state.mkList(v, args[0]->listSize() - 1);
|
||||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||||
v.listElems()[n] = args[0]->listElems()[n + 1];
|
v.listElems()[n] = args[0]->listElems()[n + 1];
|
||||||
|
@ -1568,7 +1683,10 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val
|
||||||
auto len = state.forceInt(*args[1], pos);
|
auto len = state.forceInt(*args[1], pos);
|
||||||
|
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
throw EvalError(format("cannot create list of size %1%, at %2%") % len % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("cannot create list of size %1%", len),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
state.mkList(v, len);
|
state.mkList(v, len);
|
||||||
|
|
||||||
|
@ -1726,7 +1844,11 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
|
||||||
state.forceValue(*args[1], pos);
|
state.forceValue(*args[1], pos);
|
||||||
|
|
||||||
NixFloat f2 = state.forceFloat(*args[1], pos);
|
NixFloat f2 = state.forceFloat(*args[1], pos);
|
||||||
if (f2 == 0) throw EvalError(format("division by zero, at %1%") % pos);
|
if (f2 == 0)
|
||||||
|
throw EvalError({
|
||||||
|
.hint = hintfmt("division by zero"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
if (args[0]->type == tFloat || args[1]->type == tFloat) {
|
if (args[0]->type == tFloat || args[1]->type == tFloat) {
|
||||||
mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
|
mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
|
||||||
|
@ -1735,7 +1857,11 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
|
||||||
NixInt i2 = state.forceInt(*args[1], pos);
|
NixInt i2 = state.forceInt(*args[1], pos);
|
||||||
/* Avoid division overflow as it might raise SIGFPE. */
|
/* Avoid division overflow as it might raise SIGFPE. */
|
||||||
if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1)
|
if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1)
|
||||||
throw EvalError(format("overflow in integer division, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("overflow in integer division"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
mkInt(v, i1 / i2);
|
mkInt(v, i1 / i2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1791,7 +1917,11 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string s = state.coerceToString(pos, *args[2], context);
|
string s = state.coerceToString(pos, *args[2], context);
|
||||||
|
|
||||||
if (start < 0) throw EvalError(format("negative start position in 'substring', at %1%") % pos);
|
if (start < 0)
|
||||||
|
throw EvalError({
|
||||||
|
.hint = hintfmt("negative start position in 'substring'"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
mkString(v, (unsigned int) start >= s.size() ? "" : string(s, start, len), context);
|
mkString(v, (unsigned int) start >= s.size() ? "" : string(s, start, len), context);
|
||||||
}
|
}
|
||||||
|
@ -1811,12 +1941,15 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
|
||||||
string type = state.forceStringNoCtx(*args[0], pos);
|
string type = state.forceStringNoCtx(*args[0], pos);
|
||||||
std::optional<HashType> ht = parseHashType(type);
|
std::optional<HashType> ht = parseHashType(type);
|
||||||
if (!ht)
|
if (!ht)
|
||||||
throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
|
throw Error({
|
||||||
|
.hint = hintfmt("unknown hash type '%1%'", type),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
PathSet context; // discarded
|
PathSet context; // discarded
|
||||||
string s = state.forceString(*args[1], context, pos);
|
string s = state.forceString(*args[1], context, pos);
|
||||||
|
|
||||||
mkString(v, hashString(*ht, s).to_string(Base::Base16, false), context);
|
mkString(v, hashString(*ht, s).to_string(Base16, false), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1853,10 +1986,16 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
|
|
||||||
} catch (std::regex_error &e) {
|
} catch (std::regex_error &e) {
|
||||||
if (e.code() == std::regex_constants::error_space) {
|
if (e.code() == std::regex_constants::error_space) {
|
||||||
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
||||||
throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
throw EvalError("invalid regular expression '%s', at %s", re, pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("invalid regular expression '%s'", re),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1920,10 +2059,16 @@ static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
|
|
||||||
} catch (std::regex_error &e) {
|
} catch (std::regex_error &e) {
|
||||||
if (e.code() == std::regex_constants::error_space) {
|
if (e.code() == std::regex_constants::error_space) {
|
||||||
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
||||||
throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
throw EvalError("invalid regular expression '%s', at %s", re, pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("invalid regular expression '%s'", re),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1954,7 +2099,10 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
|
||||||
state.forceList(*args[0], pos);
|
state.forceList(*args[0], pos);
|
||||||
state.forceList(*args[1], pos);
|
state.forceList(*args[1], pos);
|
||||||
if (args[0]->listSize() != args[1]->listSize())
|
if (args[0]->listSize() != args[1]->listSize())
|
||||||
throw EvalError(format("'from' and 'to' arguments to 'replaceStrings' have different lengths, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
vector<string> from;
|
vector<string> from;
|
||||||
from.reserve(args[0]->listSize());
|
from.reserve(args[0]->listSize());
|
||||||
|
@ -2057,10 +2205,11 @@ static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args
|
||||||
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
|
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
|
||||||
|
|
||||||
|
|
||||||
RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun)
|
RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun,
|
||||||
|
std::optional<std::string> requiredFeature)
|
||||||
{
|
{
|
||||||
if (!primOps) primOps = new PrimOps;
|
if (!primOps) primOps = new PrimOps;
|
||||||
primOps->emplace_back(name, arity, fun);
|
primOps->push_back({name, arity, fun, requiredFeature});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2252,7 +2401,8 @@ void EvalState::createBaseEnv()
|
||||||
|
|
||||||
if (RegisterPrimOp::primOps)
|
if (RegisterPrimOp::primOps)
|
||||||
for (auto & primOp : *RegisterPrimOp::primOps)
|
for (auto & primOp : *RegisterPrimOp::primOps)
|
||||||
addPrimOp(std::get<0>(primOp), std::get<1>(primOp), std::get<2>(primOp));
|
if (!primOp.requiredFeature || settings.isExperimentalFeatureEnabled(*primOp.requiredFeature))
|
||||||
|
addPrimOp(primOp.name, primOp.arity, primOp.primOp);
|
||||||
|
|
||||||
/* Now that we've added all primops, sort the `builtins' set,
|
/* Now that we've added all primops, sort the `builtins' set,
|
||||||
because attribute lookups expect it to be sorted. */
|
because attribute lookups expect it to be sorted. */
|
||||||
|
|
|
@ -7,12 +7,25 @@ namespace nix {
|
||||||
|
|
||||||
struct RegisterPrimOp
|
struct RegisterPrimOp
|
||||||
{
|
{
|
||||||
typedef std::vector<std::tuple<std::string, size_t, PrimOpFun>> PrimOps;
|
struct Info
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
size_t arity;
|
||||||
|
PrimOpFun primOp;
|
||||||
|
std::optional<std::string> requiredFeature;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<Info> PrimOps;
|
||||||
static PrimOps * primOps;
|
static PrimOps * primOps;
|
||||||
|
|
||||||
/* You can register a constant by passing an arity of 0. fun
|
/* You can register a constant by passing an arity of 0. fun
|
||||||
will get called during EvalState initialization, so there
|
will get called during EvalState initialization, so there
|
||||||
may be primops not yet added and builtins is not yet sorted. */
|
may be primops not yet added and builtins is not yet sorted. */
|
||||||
RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun);
|
RegisterPrimOp(
|
||||||
|
std::string name,
|
||||||
|
size_t arity,
|
||||||
|
PrimOpFun fun,
|
||||||
|
std::optional<std::string> requiredFeature = {});
|
||||||
};
|
};
|
||||||
|
|
||||||
/* These primops are disabled without enableNativeCode, but plugins
|
/* These primops are disabled without enableNativeCode, but plugins
|
||||||
|
|
|
@ -146,7 +146,10 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||||
auto sAllOutputs = state.symbols.create("allOutputs");
|
auto sAllOutputs = state.symbols.create("allOutputs");
|
||||||
for (auto & i : *args[1]->attrs) {
|
for (auto & i : *args[1]->attrs) {
|
||||||
if (!state.store->isStorePath(i.name))
|
if (!state.store->isStorePath(i.name))
|
||||||
throw EvalError("Context key '%s' is not a store path, at %s", i.name, i.pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("Context key '%s' is not a store path", i.name),
|
||||||
|
.nixCode = NixCode { .errPos = *i.pos }
|
||||||
|
});
|
||||||
if (!settings.readOnlyMode)
|
if (!settings.readOnlyMode)
|
||||||
state.store->ensurePath(state.store->parseStorePath(i.name));
|
state.store->ensurePath(state.store->parseStorePath(i.name));
|
||||||
state.forceAttrs(*i.value, *i.pos);
|
state.forceAttrs(*i.value, *i.pos);
|
||||||
|
@ -160,7 +163,10 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
if (state.forceBool(*iter->value, *iter->pos)) {
|
if (state.forceBool(*iter->value, *iter->pos)) {
|
||||||
if (!isDerivation(i.name)) {
|
if (!isDerivation(i.name)) {
|
||||||
throw EvalError("Tried to add all-outputs context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
|
||||||
|
.nixCode = NixCode { .errPos = *i.pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
context.insert("=" + string(i.name));
|
context.insert("=" + string(i.name));
|
||||||
}
|
}
|
||||||
|
@ -170,7 +176,10 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
state.forceList(*iter->value, *iter->pos);
|
state.forceList(*iter->value, *iter->pos);
|
||||||
if (iter->value->listSize() && !isDerivation(i.name)) {
|
if (iter->value->listSize() && !isDerivation(i.name)) {
|
||||||
throw EvalError("Tried to add derivation output context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
|
||||||
|
.nixCode = NixCode { .errPos = *i.pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
|
for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
|
||||||
auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
|
auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
|
||||||
|
|
|
@ -29,17 +29,23 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
else if (n == "ref")
|
else if (n == "ref")
|
||||||
ref = state.forceStringNoCtx(*attr.value, *attr.pos);
|
ref = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||||
else if (n == "rev")
|
else if (n == "rev")
|
||||||
rev = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), HashType::SHA1);
|
rev = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA1);
|
||||||
else if (n == "name")
|
else if (n == "name")
|
||||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||||
else if (n == "submodules")
|
else if (n == "submodules")
|
||||||
fetchSubmodules = state.forceBool(*attr.value, *attr.pos);
|
fetchSubmodules = state.forceBool(*attr.value, *attr.pos);
|
||||||
else
|
else
|
||||||
throw EvalError("unsupported argument '%s' to 'fetchGit', at %s", attr.name, *attr.pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("unsupported argument '%s' to 'fetchGit'", attr.name),
|
||||||
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.empty())
|
if (url.empty())
|
||||||
throw EvalError(format("'url' argument required, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("'url' argument required"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
} else
|
} else
|
||||||
url = state.coerceToString(pos, *args[0], context, false, false);
|
url = state.coerceToString(pos, *args[0], context, false, false);
|
||||||
|
@ -67,7 +73,7 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath}));
|
mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath}));
|
||||||
// Backward compatibility: set 'rev' to
|
// Backward compatibility: set 'rev' to
|
||||||
// 0000000000000000000000000000000000000000 for a dirty tree.
|
// 0000000000000000000000000000000000000000 for a dirty tree.
|
||||||
auto rev2 = input2->getRev().value_or(Hash(HashType::SHA1));
|
auto rev2 = input2->getRev().value_or(Hash(htSHA1));
|
||||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev());
|
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev());
|
||||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev2.gitShortRev());
|
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev2.gitShortRev());
|
||||||
// Backward compatibility: set 'revCount' to 0 for a dirty tree.
|
// Backward compatibility: set 'revCount' to 0 for a dirty tree.
|
||||||
|
|
|
@ -31,18 +31,24 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
// be both a revision or a branch/tag name.
|
// be both a revision or a branch/tag name.
|
||||||
auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
|
auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||||
if (std::regex_match(value, revRegex))
|
if (std::regex_match(value, revRegex))
|
||||||
rev = Hash(value, HashType::SHA1);
|
rev = Hash(value, htSHA1);
|
||||||
else
|
else
|
||||||
ref = value;
|
ref = value;
|
||||||
}
|
}
|
||||||
else if (n == "name")
|
else if (n == "name")
|
||||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||||
else
|
else
|
||||||
throw EvalError("unsupported argument '%s' to 'fetchMercurial', at %s", attr.name, *attr.pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
|
||||||
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.empty())
|
if (url.empty())
|
||||||
throw EvalError(format("'url' argument required, at %1%") % pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("'url' argument required"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
} else
|
} else
|
||||||
url = state.coerceToString(pos, *args[0], context, false, false);
|
url = state.coerceToString(pos, *args[0], context, false, false);
|
||||||
|
@ -71,7 +77,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
mkString(*state.allocAttr(v, state.symbols.create("branch")), *input2->getRef());
|
mkString(*state.allocAttr(v, state.symbols.create("branch")), *input2->getRef());
|
||||||
// Backward compatibility: set 'rev' to
|
// Backward compatibility: set 'rev' to
|
||||||
// 0000000000000000000000000000000000000000 for a dirty tree.
|
// 0000000000000000000000000000000000000000 for a dirty tree.
|
||||||
auto rev2 = input2->getRev().value_or(Hash(HashType::SHA1));
|
auto rev2 = input2->getRev().value_or(Hash(htSHA1));
|
||||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev());
|
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev());
|
||||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(rev2.gitRev(), 0, 12));
|
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(rev2.gitRev(), 0, 12));
|
||||||
if (tree.info.revCount)
|
if (tree.info.revCount)
|
||||||
|
|
|
@ -23,7 +23,7 @@ void emitTreeAttrs(
|
||||||
|
|
||||||
assert(tree.info.narHash);
|
assert(tree.info.narHash);
|
||||||
mkString(*state.allocAttr(v, state.symbols.create("narHash")),
|
mkString(*state.allocAttr(v, state.symbols.create("narHash")),
|
||||||
tree.info.narHash.to_string(Base::SRI));
|
tree.info.narHash.to_string(SRI, true));
|
||||||
|
|
||||||
if (input->getRev()) {
|
if (input->getRev()) {
|
||||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), input->getRev()->gitRev());
|
mkString(*state.allocAttr(v, state.symbols.create("rev")), input->getRev()->gitRev());
|
||||||
|
@ -66,7 +66,10 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!attrs.count("type"))
|
if (!attrs.count("type"))
|
||||||
throw Error("attribute 'type' is missing in call to 'fetchTree', at %s", pos);
|
throw Error({
|
||||||
|
.hint = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
|
|
||||||
input = fetchers::inputFromAttrs(attrs);
|
input = fetchers::inputFromAttrs(attrs);
|
||||||
} else
|
} else
|
||||||
|
@ -103,17 +106,21 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
if (n == "url")
|
if (n == "url")
|
||||||
url = state.forceStringNoCtx(*attr.value, *attr.pos);
|
url = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||||
else if (n == "sha256")
|
else if (n == "sha256")
|
||||||
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), HashType::SHA256);
|
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||||
else if (n == "name")
|
else if (n == "name")
|
||||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||||
else
|
else
|
||||||
throw EvalError("unsupported argument '%s' to '%s', at %s",
|
throw EvalError({
|
||||||
attr.name, who, *attr.pos);
|
.hint = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
|
||||||
}
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!url)
|
if (!url)
|
||||||
throw EvalError("'url' argument required, at %s", pos);
|
throw EvalError({
|
||||||
|
.hint = hintfmt("'url' argument required"),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
} else
|
} else
|
||||||
url = state.forceStringNoCtx(*args[0], pos);
|
url = state.forceStringNoCtx(*args[0], pos);
|
||||||
|
|
||||||
|
@ -137,10 +144,10 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
if (expectedHash) {
|
if (expectedHash) {
|
||||||
auto hash = unpack
|
auto hash = unpack
|
||||||
? state.store->queryPathInfo(storePath)->narHash
|
? state.store->queryPathInfo(storePath)->narHash
|
||||||
: hashFile(HashType::SHA256, path);
|
: hashFile(htSHA256, path);
|
||||||
if (hash != *expectedHash)
|
if (hash != *expectedHash)
|
||||||
throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n wanted: %s\n got: %s",
|
throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n wanted: %s\n got: %s",
|
||||||
*url, expectedHash->to_string(), hash.to_string());
|
*url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.allowedPaths)
|
if (state.allowedPaths)
|
||||||
|
|
|
@ -81,7 +81,10 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
try {
|
try {
|
||||||
visit(v, parser(tomlStream).parse());
|
visit(v, parser(tomlStream).parse());
|
||||||
} catch (std::runtime_error & e) {
|
} catch (std::runtime_error & e) {
|
||||||
throw EvalError("while parsing a TOML string at %s: %s", pos, e.what());
|
throw EvalError({
|
||||||
|
.hint = hintfmt("while parsing a TOML string: %s", e.what()),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw TypeError(format("cannot convert %1% to JSON") % showType(v));
|
throw TypeError("cannot convert %1% to JSON", showType(v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||||
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
|
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
|
||||||
JSONPlaceholder & out, PathSet & context) const
|
JSONPlaceholder & out, PathSet & context) const
|
||||||
{
|
{
|
||||||
throw TypeError(format("cannot convert %1% to JSON") % showType());
|
throw TypeError("cannot convert %1% to JSON", showType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ std::unique_ptr<Input> inputFromAttrs(const Attrs & attrs)
|
||||||
if (res) {
|
if (res) {
|
||||||
if (auto narHash = maybeGetStrAttr(attrs, "narHash"))
|
if (auto narHash = maybeGetStrAttr(attrs, "narHash"))
|
||||||
// FIXME: require SRI hash.
|
// FIXME: require SRI hash.
|
||||||
res->narHash = Hash(*narHash);
|
res->narHash = newHashAllowEmpty(*narHash, {});
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ Attrs Input::toAttrs() const
|
||||||
{
|
{
|
||||||
auto attrs = toAttrsInternal();
|
auto attrs = toAttrsInternal();
|
||||||
if (narHash)
|
if (narHash)
|
||||||
attrs.emplace("narHash", narHash->to_string(Base::SRI));
|
attrs.emplace("narHash", narHash->to_string(SRI, true));
|
||||||
attrs.emplace("type", type());
|
attrs.emplace("type", type());
|
||||||
return attrs;
|
return attrs;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ std::pair<Tree, std::shared_ptr<const Input>> Input::fetchTree(ref<Store> store)
|
||||||
|
|
||||||
if (narHash && narHash != input->narHash)
|
if (narHash && narHash != input->narHash)
|
||||||
throw Error("NAR hash mismatch in input '%s' (%s), expected '%s', got '%s'",
|
throw Error("NAR hash mismatch in input '%s' (%s), expected '%s', got '%s'",
|
||||||
to_string(), tree.actualPath, narHash->to_string(Base::SRI), input->narHash->to_string(Base::SRI));
|
to_string(), tree.actualPath, narHash->to_string(SRI, true), input->narHash->to_string(SRI, true));
|
||||||
|
|
||||||
return {std::move(tree), input};
|
return {std::move(tree), input};
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ struct GitInput : Input
|
||||||
|
|
||||||
auto input = std::make_shared<GitInput>(*this);
|
auto input = std::make_shared<GitInput>(*this);
|
||||||
|
|
||||||
assert(!rev || rev->type == HashType::SHA1);
|
assert(!rev || rev->type == htSHA1);
|
||||||
|
|
||||||
std::string cacheType = "git";
|
std::string cacheType = "git";
|
||||||
if (shallow) cacheType += "-shallow";
|
if (shallow) cacheType += "-shallow";
|
||||||
|
@ -195,7 +195,7 @@ struct GitInput : Input
|
||||||
return files.count(file);
|
return files.count(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, HashType::SHA256, filter);
|
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||||
|
|
||||||
auto tree = Tree {
|
auto tree = Tree {
|
||||||
.actualPath = store->printStorePath(storePath),
|
.actualPath = store->printStorePath(storePath),
|
||||||
|
@ -225,21 +225,21 @@ struct GitInput : Input
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
|
|
||||||
if (!input->rev)
|
if (!input->rev)
|
||||||
input->rev = Hash(chomp(runProgram("git", true, { "-C", actualUrl, "rev-parse", *input->ref })), HashType::SHA1);
|
input->rev = Hash(chomp(runProgram("git", true, { "-C", actualUrl, "rev-parse", *input->ref })), htSHA1);
|
||||||
|
|
||||||
repoDir = actualUrl;
|
repoDir = actualUrl;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (auto res = getCache()->lookup(store, mutableAttrs)) {
|
if (auto res = getCache()->lookup(store, mutableAttrs)) {
|
||||||
auto rev2 = Hash(getStrAttr(res->first, "rev"), HashType::SHA1);
|
auto rev2 = Hash(getStrAttr(res->first, "rev"), htSHA1);
|
||||||
if (!rev || rev == rev2) {
|
if (!rev || rev == rev2) {
|
||||||
input->rev = rev2;
|
input->rev = rev2;
|
||||||
return makeResult(res->first, std::move(res->second));
|
return makeResult(res->first, std::move(res->second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Path cacheDir = getCacheDir() + "/nix/gitv3/" + hashString(HashType::SHA256, actualUrl).to_string(Base::Base32, false);
|
Path cacheDir = getCacheDir() + "/nix/gitv3/" + hashString(htSHA256, actualUrl).to_string(Base32, false);
|
||||||
repoDir = cacheDir;
|
repoDir = cacheDir;
|
||||||
|
|
||||||
if (!pathExists(cacheDir)) {
|
if (!pathExists(cacheDir)) {
|
||||||
|
@ -277,7 +277,7 @@ struct GitInput : Input
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doFetch) {
|
if (doFetch) {
|
||||||
Activity act(*logger, Verbosity::Talkative, ActivityType::Unknown, fmt("fetching Git repository '%s'", actualUrl));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Git repository '%s'", actualUrl));
|
||||||
|
|
||||||
// FIXME: git stderr messes up our progress indicator, so
|
// FIXME: git stderr messes up our progress indicator, so
|
||||||
// we're using --quiet for now. Should process its stderr.
|
// we're using --quiet for now. Should process its stderr.
|
||||||
|
@ -301,7 +301,7 @@ struct GitInput : Input
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!input->rev)
|
if (!input->rev)
|
||||||
input->rev = Hash(chomp(readFile(localRefFile)), HashType::SHA1);
|
input->rev = Hash(chomp(readFile(localRefFile)), htSHA1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "rev-parse", "--is-shallow-repository" })) == "true";
|
bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "rev-parse", "--is-shallow-repository" })) == "true";
|
||||||
|
@ -350,7 +350,7 @@ struct GitInput : Input
|
||||||
unpackTarfile(*source, tmpDir);
|
unpackTarfile(*source, tmpDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto storePath = store->addToStore(name, tmpDir, FileIngestionMethod::Recursive, HashType::SHA256, filter);
|
auto storePath = store->addToStore(name, tmpDir, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||||
|
|
||||||
auto lastModified = std::stoull(runProgram("git", true, { "-C", repoDir, "log", "-1", "--format=%ct", input->rev->gitRev() }));
|
auto lastModified = std::stoull(runProgram("git", true, { "-C", repoDir, "log", "-1", "--format=%ct", input->rev->gitRev() }));
|
||||||
|
|
||||||
|
@ -426,7 +426,7 @@ struct GitInputScheme : InputScheme
|
||||||
input->ref = *ref;
|
input->ref = *ref;
|
||||||
}
|
}
|
||||||
if (auto rev = maybeGetStrAttr(attrs, "rev"))
|
if (auto rev = maybeGetStrAttr(attrs, "rev"))
|
||||||
input->rev = Hash(*rev, HashType::SHA1);
|
input->rev = Hash(*rev, htSHA1);
|
||||||
|
|
||||||
input->shallow = maybeGetBoolAttr(attrs, "shallow").value_or(false);
|
input->shallow = maybeGetBoolAttr(attrs, "shallow").value_or(false);
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ struct GitHubInput : Input
|
||||||
auto path = owner + "/" + repo;
|
auto path = owner + "/" + repo;
|
||||||
assert(!(ref && rev));
|
assert(!(ref && rev));
|
||||||
if (ref) path += "/" + *ref;
|
if (ref) path += "/" + *ref;
|
||||||
if (rev) path += "/" + rev->to_string(Base::Base16, false);
|
if (rev) path += "/" + rev->to_string(Base16, false);
|
||||||
return ParsedURL {
|
return ParsedURL {
|
||||||
.scheme = "github",
|
.scheme = "github",
|
||||||
.path = path,
|
.path = path,
|
||||||
|
@ -76,7 +76,7 @@ struct GitHubInput : Input
|
||||||
readFile(
|
readFile(
|
||||||
store->toRealPath(
|
store->toRealPath(
|
||||||
downloadFile(store, url, "source", false).storePath)));
|
downloadFile(store, url, "source", false).storePath)));
|
||||||
rev = Hash(json["sha"], HashType::SHA1);
|
rev = Hash(std::string { json["sha"] }, htSHA1);
|
||||||
debug("HEAD revision for '%s' is %s", url, rev->gitRev());
|
debug("HEAD revision for '%s' is %s", url, rev->gitRev());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ struct GitHubInput : Input
|
||||||
// might have stricter rate limits.
|
// might have stricter rate limits.
|
||||||
|
|
||||||
auto url = fmt("https://api.github.com/repos/%s/%s/tarball/%s",
|
auto url = fmt("https://api.github.com/repos/%s/%s/tarball/%s",
|
||||||
owner, repo, rev->to_string(Base::Base16, false));
|
owner, repo, rev->to_string(Base16, false));
|
||||||
|
|
||||||
std::string accessToken = settings.githubAccessToken.get();
|
std::string accessToken = settings.githubAccessToken.get();
|
||||||
if (accessToken != "")
|
if (accessToken != "")
|
||||||
|
@ -140,7 +140,7 @@ struct GitHubInputScheme : InputScheme
|
||||||
if (path.size() == 2) {
|
if (path.size() == 2) {
|
||||||
} else if (path.size() == 3) {
|
} else if (path.size() == 3) {
|
||||||
if (std::regex_match(path[2], revRegex))
|
if (std::regex_match(path[2], revRegex))
|
||||||
input->rev = Hash(path[2], HashType::SHA1);
|
input->rev = Hash(path[2], htSHA1);
|
||||||
else if (std::regex_match(path[2], refRegex))
|
else if (std::regex_match(path[2], refRegex))
|
||||||
input->ref = path[2];
|
input->ref = path[2];
|
||||||
else
|
else
|
||||||
|
@ -152,7 +152,7 @@ struct GitHubInputScheme : InputScheme
|
||||||
if (name == "rev") {
|
if (name == "rev") {
|
||||||
if (input->rev)
|
if (input->rev)
|
||||||
throw BadURL("GitHub URL '%s' contains multiple commit hashes", url.url);
|
throw BadURL("GitHub URL '%s' contains multiple commit hashes", url.url);
|
||||||
input->rev = Hash(value, HashType::SHA1);
|
input->rev = Hash(value, htSHA1);
|
||||||
}
|
}
|
||||||
else if (name == "ref") {
|
else if (name == "ref") {
|
||||||
if (!std::regex_match(value, refRegex))
|
if (!std::regex_match(value, refRegex))
|
||||||
|
@ -185,7 +185,7 @@ struct GitHubInputScheme : InputScheme
|
||||||
input->repo = getStrAttr(attrs, "repo");
|
input->repo = getStrAttr(attrs, "repo");
|
||||||
input->ref = maybeGetStrAttr(attrs, "ref");
|
input->ref = maybeGetStrAttr(attrs, "ref");
|
||||||
if (auto rev = maybeGetStrAttr(attrs, "rev"))
|
if (auto rev = maybeGetStrAttr(attrs, "rev"))
|
||||||
input->rev = Hash(*rev, HashType::SHA1);
|
input->rev = Hash(*rev, htSHA1);
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,4 +8,4 @@ libfetchers_SOURCES := $(wildcard $(d)/*.cc)
|
||||||
|
|
||||||
libfetchers_CXXFLAGS += -I src/libutil -I src/libstore
|
libfetchers_CXXFLAGS += -I src/libutil -I src/libstore
|
||||||
|
|
||||||
libfetchers_LIBS = libutil libstore libnixrust
|
libfetchers_LIBS = libutil libstore
|
||||||
|
|
|
@ -114,7 +114,7 @@ struct MercurialInput : Input
|
||||||
return files.count(file);
|
return files.count(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, HashType::SHA256, filter);
|
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||||
|
|
||||||
return {Tree {
|
return {Tree {
|
||||||
.actualPath = store->printStorePath(storePath),
|
.actualPath = store->printStorePath(storePath),
|
||||||
|
@ -167,14 +167,14 @@ struct MercurialInput : Input
|
||||||
});
|
});
|
||||||
|
|
||||||
if (auto res = getCache()->lookup(store, mutableAttrs)) {
|
if (auto res = getCache()->lookup(store, mutableAttrs)) {
|
||||||
auto rev2 = Hash(getStrAttr(res->first, "rev"), HashType::SHA1);
|
auto rev2 = Hash(getStrAttr(res->first, "rev"), htSHA1);
|
||||||
if (!rev || rev == rev2) {
|
if (!rev || rev == rev2) {
|
||||||
input->rev = rev2;
|
input->rev = rev2;
|
||||||
return makeResult(res->first, std::move(res->second));
|
return makeResult(res->first, std::move(res->second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(HashType::SHA256, actualUrl).to_string(Base::Base32, false));
|
Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(htSHA256, actualUrl).to_string(Base32, false));
|
||||||
|
|
||||||
/* If this is a commit hash that we already have, we don't
|
/* If this is a commit hash that we already have, we don't
|
||||||
have to pull again. */
|
have to pull again. */
|
||||||
|
@ -184,7 +184,7 @@ struct MercurialInput : Input
|
||||||
RunOptions("hg", { "log", "-R", cacheDir, "-r", input->rev->gitRev(), "--template", "1" })
|
RunOptions("hg", { "log", "-R", cacheDir, "-r", input->rev->gitRev(), "--template", "1" })
|
||||||
.killStderr(true)).second == "1"))
|
.killStderr(true)).second == "1"))
|
||||||
{
|
{
|
||||||
Activity act(*logger, Verbosity::Talkative, ActivityType::Unknown, fmt("fetching Mercurial repository '%s'", actualUrl));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl));
|
||||||
|
|
||||||
if (pathExists(cacheDir)) {
|
if (pathExists(cacheDir)) {
|
||||||
try {
|
try {
|
||||||
|
@ -210,7 +210,7 @@ struct MercurialInput : Input
|
||||||
runProgram("hg", true, { "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" }));
|
runProgram("hg", true, { "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" }));
|
||||||
assert(tokens.size() == 3);
|
assert(tokens.size() == 3);
|
||||||
|
|
||||||
input->rev = Hash(tokens[0], HashType::SHA1);
|
input->rev = Hash(tokens[0], htSHA1);
|
||||||
auto revCount = std::stoull(tokens[1]);
|
auto revCount = std::stoull(tokens[1]);
|
||||||
input->ref = tokens[2];
|
input->ref = tokens[2];
|
||||||
|
|
||||||
|
@ -293,7 +293,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
input->ref = *ref;
|
input->ref = *ref;
|
||||||
}
|
}
|
||||||
if (auto rev = maybeGetStrAttr(attrs, "rev"))
|
if (auto rev = maybeGetStrAttr(attrs, "rev"))
|
||||||
input->rev = Hash(*rev, HashType::SHA1);
|
input->rev = Hash(*rev, htSHA1);
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -101,7 +101,7 @@ struct PathInputScheme : InputScheme
|
||||||
|
|
||||||
for (auto & [name, value] : url.query)
|
for (auto & [name, value] : url.query)
|
||||||
if (name == "rev")
|
if (name == "rev")
|
||||||
input->rev = Hash(value, HashType::SHA1);
|
input->rev = Hash(value, htSHA1);
|
||||||
else if (name == "revCount") {
|
else if (name == "revCount") {
|
||||||
uint64_t revCount;
|
uint64_t revCount;
|
||||||
if (!string2Int(value, revCount))
|
if (!string2Int(value, revCount))
|
||||||
|
@ -129,7 +129,7 @@ struct PathInputScheme : InputScheme
|
||||||
|
|
||||||
for (auto & [name, value] : attrs)
|
for (auto & [name, value] : attrs)
|
||||||
if (name == "rev")
|
if (name == "rev")
|
||||||
input->rev = Hash(getStrAttr(attrs, "rev"), HashType::SHA1);
|
input->rev = Hash(getStrAttr(attrs, "rev"), htSHA1);
|
||||||
else if (name == "revCount")
|
else if (name == "revCount")
|
||||||
input->revCount = getIntAttr(attrs, "revCount");
|
input->revCount = getIntAttr(attrs, "revCount");
|
||||||
else if (name == "lastModified")
|
else if (name == "lastModified")
|
||||||
|
|
|
@ -66,9 +66,9 @@ DownloadFileResult downloadFile(
|
||||||
} else {
|
} else {
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
dumpString(*res.data, sink);
|
dumpString(*res.data, sink);
|
||||||
auto hash = hashString(HashType::SHA256, *res.data);
|
auto hash = hashString(htSHA256, *res.data);
|
||||||
ValidPathInfo info(store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name));
|
ValidPathInfo info(store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name));
|
||||||
info.narHash = hashString(HashType::SHA256, *sink.s);
|
info.narHash = hashString(htSHA256, *sink.s);
|
||||||
info.narSize = sink.s->size();
|
info.narSize = sink.s->size();
|
||||||
info.ca = FileSystemHash {
|
info.ca = FileSystemHash {
|
||||||
FileIngestionMethod::Flat,
|
FileIngestionMethod::Flat,
|
||||||
|
@ -145,7 +145,7 @@ Tree downloadTarball(
|
||||||
throw nix::Error("tarball '%s' contains an unexpected number of top-level files", url);
|
throw nix::Error("tarball '%s' contains an unexpected number of top-level files", url);
|
||||||
auto topDir = tmpDir + "/" + members.begin()->name;
|
auto topDir = tmpDir + "/" + members.begin()->name;
|
||||||
lastModified = lstat(topDir).st_mtime;
|
lastModified = lstat(topDir).st_mtime;
|
||||||
unpackedStorePath = store->addToStore(name, topDir, FileIngestionMethod::Recursive, HashType::SHA256, defaultPathFilter, NoRepair);
|
unpackedStorePath = store->addToStore(name, topDir, FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, NoRepair);
|
||||||
}
|
}
|
||||||
|
|
||||||
Attrs infoAttrs({
|
Attrs infoAttrs({
|
||||||
|
@ -199,9 +199,9 @@ struct TarballInput : Input
|
||||||
// NAR hashes are preferred over file hashes since tar/zip files
|
// NAR hashes are preferred over file hashes since tar/zip files
|
||||||
// don't have a canonical representation.
|
// don't have a canonical representation.
|
||||||
if (narHash)
|
if (narHash)
|
||||||
url2.query.insert_or_assign("narHash", narHash->to_string(Base::SRI));
|
url2.query.insert_or_assign("narHash", narHash->to_string(SRI, true));
|
||||||
else if (hash)
|
else if (hash)
|
||||||
url2.query.insert_or_assign("hash", hash->to_string(Base::SRI));
|
url2.query.insert_or_assign("hash", hash->to_string(SRI, true));
|
||||||
return url2;
|
return url2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ struct TarballInput : Input
|
||||||
Attrs attrs;
|
Attrs attrs;
|
||||||
attrs.emplace("url", url.to_string());
|
attrs.emplace("url", url.to_string());
|
||||||
if (hash)
|
if (hash)
|
||||||
attrs.emplace("hash", hash->to_string(Base::SRI));
|
attrs.emplace("hash", hash->to_string(SRI, true));
|
||||||
return attrs;
|
return attrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,8 +267,7 @@ struct TarballInputScheme : InputScheme
|
||||||
|
|
||||||
auto input = std::make_unique<TarballInput>(parseURL(getStrAttr(attrs, "url")));
|
auto input = std::make_unique<TarballInput>(parseURL(getStrAttr(attrs, "url")));
|
||||||
if (auto hash = maybeGetStrAttr(attrs, "hash"))
|
if (auto hash = maybeGetStrAttr(attrs, "hash"))
|
||||||
// FIXME: require SRI hash.
|
input->hash = newHashAllowEmpty(*hash, {});
|
||||||
input->hash = Hash(*hash);
|
|
||||||
|
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "loggers.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -10,21 +11,19 @@ MixCommonArgs::MixCommonArgs(const string & programName)
|
||||||
.longName = "verbose",
|
.longName = "verbose",
|
||||||
.shortName = 'v',
|
.shortName = 'v',
|
||||||
.description = "increase verbosity level",
|
.description = "increase verbosity level",
|
||||||
.handler = {[]() { verbosity = (Verbosity) ((uint64_t) verbosity + 1); }},
|
.handler = {[]() { verbosity = (Verbosity) (verbosity + 1); }},
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "quiet",
|
.longName = "quiet",
|
||||||
.description = "decrease verbosity level",
|
.description = "decrease verbosity level",
|
||||||
.handler = {[]() { verbosity = verbosity > Verbosity::Error
|
.handler = {[]() { verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError; }},
|
||||||
? (Verbosity) ((uint64_t) verbosity - 1)
|
|
||||||
: Verbosity::Error; }},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "debug",
|
.longName = "debug",
|
||||||
.description = "enable debug output",
|
.description = "enable debug output",
|
||||||
.handler = {[]() { verbosity = Verbosity::Debug; }},
|
.handler = {[]() { verbosity = lvlDebug; }},
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
|
@ -40,6 +39,14 @@ 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({
|
addFlag({
|
||||||
.longName = "max-jobs",
|
.longName = "max-jobs",
|
||||||
.shortName = 'j',
|
.shortName = 'j',
|
||||||
|
|
52
src/libmain/loggers.cc
Normal file
52
src/libmain/loggers.cc
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#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());
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
src/libmain/loggers.hh
Normal file
20
src/libmain/loggers.hh
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#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();
|
||||||
|
|
||||||
|
}
|
|
@ -39,7 +39,7 @@ private:
|
||||||
struct ActInfo
|
struct ActInfo
|
||||||
{
|
{
|
||||||
std::string s, lastLine, phase;
|
std::string s, lastLine, phase;
|
||||||
ActivityType type = ActivityType::Unknown;
|
ActivityType type = actUnknown;
|
||||||
uint64_t done = 0;
|
uint64_t done = 0;
|
||||||
uint64_t expected = 0;
|
uint64_t expected = 0;
|
||||||
uint64_t running = 0;
|
uint64_t running = 0;
|
||||||
|
@ -106,25 +106,36 @@ public:
|
||||||
updateThread.join();
|
updateThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop()
|
void stop() override
|
||||||
{
|
{
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
if (!state->active) return;
|
if (!state->active) return;
|
||||||
state->active = false;
|
state->active = false;
|
||||||
std::string status = getStatus(*state);
|
|
||||||
writeToStderr("\r\e[K");
|
writeToStderr("\r\e[K");
|
||||||
if (status != "")
|
|
||||||
writeToStderr("[" + status + "]\n");
|
|
||||||
updateCV.notify_one();
|
updateCV.notify_one();
|
||||||
quitCV.notify_one();
|
quitCV.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isVerbose() override {
|
||||||
|
return printBuildLogs;
|
||||||
|
}
|
||||||
|
|
||||||
void log(Verbosity lvl, const FormatOrString & fs) override
|
void log(Verbosity lvl, const FormatOrString & fs) override
|
||||||
{
|
{
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
log(*state, lvl, fs.s);
|
log(*state, lvl, fs.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void logEI(const ErrorInfo &ei) override
|
||||||
|
{
|
||||||
|
auto state(state_.lock());
|
||||||
|
|
||||||
|
std::stringstream oss;
|
||||||
|
oss << ei;
|
||||||
|
|
||||||
|
log(*state, ei.level, oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
void log(State & state, Verbosity lvl, const std::string & s)
|
void log(State & state, Verbosity lvl, const std::string & s)
|
||||||
{
|
{
|
||||||
if (state.active) {
|
if (state.active) {
|
||||||
|
@ -142,7 +153,7 @@ public:
|
||||||
{
|
{
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
|
|
||||||
if (lvl <= verbosity && !s.empty())
|
if (lvl <= verbosity && !s.empty() && type != actBuildWaiting)
|
||||||
log(*state, lvl, s + "...");
|
log(*state, lvl, s + "...");
|
||||||
|
|
||||||
state->activities.emplace_back(ActInfo());
|
state->activities.emplace_back(ActInfo());
|
||||||
|
@ -153,8 +164,8 @@ public:
|
||||||
state->its.emplace(act, i);
|
state->its.emplace(act, i);
|
||||||
state->activitiesByType[type].its.emplace(act, i);
|
state->activitiesByType[type].its.emplace(act, i);
|
||||||
|
|
||||||
if (type == ActivityType::Build) {
|
if (type == actBuild) {
|
||||||
auto name = storePathToName(getS(fields, 0));
|
std::string name(storePathToName(getS(fields, 0)));
|
||||||
if (hasSuffix(name, ".drv"))
|
if (hasSuffix(name, ".drv"))
|
||||||
name = name.substr(0, name.size() - 4);
|
name = name.substr(0, name.size() - 4);
|
||||||
i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name);
|
i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name);
|
||||||
|
@ -168,7 +179,7 @@ public:
|
||||||
i->name = DrvName(name).name;
|
i->name = DrvName(name).name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == ActivityType::Substitute) {
|
if (type == actSubstitute) {
|
||||||
auto name = storePathToName(getS(fields, 0));
|
auto name = storePathToName(getS(fields, 0));
|
||||||
auto sub = getS(fields, 1);
|
auto sub = getS(fields, 1);
|
||||||
i->s = fmt(
|
i->s = fmt(
|
||||||
|
@ -178,7 +189,7 @@ public:
|
||||||
name, sub);
|
name, sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == ActivityType::PostBuildHook) {
|
if (type == actPostBuildHook) {
|
||||||
auto name = storePathToName(getS(fields, 0));
|
auto name = storePathToName(getS(fields, 0));
|
||||||
if (hasSuffix(name, ".drv"))
|
if (hasSuffix(name, ".drv"))
|
||||||
name = name.substr(0, name.size() - 4);
|
name = name.substr(0, name.size() - 4);
|
||||||
|
@ -186,14 +197,14 @@ public:
|
||||||
i->name = DrvName(name).name;
|
i->name = DrvName(name).name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == ActivityType::QueryPathInfo) {
|
if (type == actQueryPathInfo) {
|
||||||
auto name = storePathToName(getS(fields, 0));
|
auto name = storePathToName(getS(fields, 0));
|
||||||
i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, getS(fields, 1));
|
i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, getS(fields, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type == ActivityType::Download && hasAncestor(*state, ActivityType::CopyPath, parent))
|
if ((type == actFileTransfer && hasAncestor(*state, actCopyPath, parent))
|
||||||
|| (type == ActivityType::Download && hasAncestor(*state, ActivityType::QueryPathInfo, parent))
|
|| (type == actFileTransfer && hasAncestor(*state, actQueryPathInfo, parent))
|
||||||
|| (type == ActivityType::CopyPath && hasAncestor(*state, ActivityType::Substitute, parent)))
|
|| (type == actCopyPath && hasAncestor(*state, actSubstitute, parent)))
|
||||||
i->visible = false;
|
i->visible = false;
|
||||||
|
|
||||||
update(*state);
|
update(*state);
|
||||||
|
@ -238,13 +249,13 @@ public:
|
||||||
{
|
{
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
|
|
||||||
if (type == ResultType::FileLinked) {
|
if (type == resFileLinked) {
|
||||||
state->filesLinked++;
|
state->filesLinked++;
|
||||||
state->bytesLinked += getI(fields, 0);
|
state->bytesLinked += getI(fields, 0);
|
||||||
update(*state);
|
update(*state);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == ResultType::BuildLogLine || type == ResultType::PostBuildLogLine) {
|
else if (type == resBuildLogLine || type == resPostBuildLogLine) {
|
||||||
auto lastLine = trim(getS(fields, 0));
|
auto lastLine = trim(getS(fields, 0));
|
||||||
if (!lastLine.empty()) {
|
if (!lastLine.empty()) {
|
||||||
auto i = state->its.find(act);
|
auto i = state->its.find(act);
|
||||||
|
@ -252,10 +263,10 @@ public:
|
||||||
ActInfo info = *i->second;
|
ActInfo info = *i->second;
|
||||||
if (printBuildLogs) {
|
if (printBuildLogs) {
|
||||||
auto suffix = "> ";
|
auto suffix = "> ";
|
||||||
if (type == ResultType::PostBuildLogLine) {
|
if (type == resPostBuildLogLine) {
|
||||||
suffix = " (post)> ";
|
suffix = " (post)> ";
|
||||||
}
|
}
|
||||||
log(*state, Verbosity::Info, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine);
|
log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine);
|
||||||
} else {
|
} else {
|
||||||
state->activities.erase(i->second);
|
state->activities.erase(i->second);
|
||||||
info.lastLine = lastLine;
|
info.lastLine = lastLine;
|
||||||
|
@ -266,24 +277,24 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == ResultType::UntrustedPath) {
|
else if (type == resUntrustedPath) {
|
||||||
state->untrustedPaths++;
|
state->untrustedPaths++;
|
||||||
update(*state);
|
update(*state);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == ResultType::CorruptedPath) {
|
else if (type == resCorruptedPath) {
|
||||||
state->corruptedPaths++;
|
state->corruptedPaths++;
|
||||||
update(*state);
|
update(*state);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == ResultType::SetPhase) {
|
else if (type == resSetPhase) {
|
||||||
auto i = state->its.find(act);
|
auto i = state->its.find(act);
|
||||||
assert(i != state->its.end());
|
assert(i != state->its.end());
|
||||||
i->second->phase = getS(fields, 0);
|
i->second->phase = getS(fields, 0);
|
||||||
update(*state);
|
update(*state);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == ResultType::Progress) {
|
else if (type == resProgress) {
|
||||||
auto i = state->its.find(act);
|
auto i = state->its.find(act);
|
||||||
assert(i != state->its.end());
|
assert(i != state->its.end());
|
||||||
ActInfo & actInfo = *i->second;
|
ActInfo & actInfo = *i->second;
|
||||||
|
@ -294,7 +305,7 @@ public:
|
||||||
update(*state);
|
update(*state);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == ResultType::SetExpected) {
|
else if (type == resSetExpected) {
|
||||||
auto i = state->its.find(act);
|
auto i = state->its.find(act);
|
||||||
assert(i != state->its.end());
|
assert(i != state->its.end());
|
||||||
ActInfo & actInfo = *i->second;
|
ActInfo & actInfo = *i->second;
|
||||||
|
@ -406,10 +417,10 @@ public:
|
||||||
res += s;
|
res += s;
|
||||||
};
|
};
|
||||||
|
|
||||||
showActivity(ActivityType::Builds, "%s built");
|
showActivity(actBuilds, "%s built");
|
||||||
|
|
||||||
auto s1 = renderActivity(ActivityType::CopyPaths, "%s copied");
|
auto s1 = renderActivity(actCopyPaths, "%s copied");
|
||||||
auto s2 = renderActivity(ActivityType::CopyPath, "%s MiB", "%.1f", MiB);
|
auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB);
|
||||||
|
|
||||||
if (!s1.empty() || !s2.empty()) {
|
if (!s1.empty() || !s2.empty()) {
|
||||||
if (!res.empty()) res += ", ";
|
if (!res.empty()) res += ", ";
|
||||||
|
@ -417,10 +428,10 @@ public:
|
||||||
if (!s2.empty()) { res += " ("; res += s2; res += ')'; }
|
if (!s2.empty()) { res += " ("; res += s2; res += ')'; }
|
||||||
}
|
}
|
||||||
|
|
||||||
showActivity(ActivityType::Download, "%s MiB DL", "%.1f", MiB);
|
showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto s = renderActivity(ActivityType::OptimiseStore, "%s paths optimised");
|
auto s = renderActivity(actOptimiseStore, "%s paths optimised");
|
||||||
if (s != "") {
|
if (s != "") {
|
||||||
s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked);
|
s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked);
|
||||||
if (!res.empty()) res += ", ";
|
if (!res.empty()) res += ", ";
|
||||||
|
@ -429,7 +440,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: don't show "done" paths in green.
|
// FIXME: don't show "done" paths in green.
|
||||||
showActivity(ActivityType::VerifyPaths, "%s paths verified");
|
showActivity(actVerifyPaths, "%s paths verified");
|
||||||
|
|
||||||
if (state.corruptedPaths) {
|
if (state.corruptedPaths) {
|
||||||
if (!res.empty()) res += ", ";
|
if (!res.empty()) res += ", ";
|
||||||
|
@ -457,11 +468,17 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Logger * makeProgressBar(bool printBuildLogs)
|
||||||
|
{
|
||||||
|
return new ProgressBar(
|
||||||
|
printBuildLogs,
|
||||||
|
isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void startProgressBar(bool printBuildLogs)
|
void startProgressBar(bool printBuildLogs)
|
||||||
{
|
{
|
||||||
logger = new ProgressBar(
|
logger = makeProgressBar(printBuildLogs);
|
||||||
printBuildLogs,
|
|
||||||
isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopProgressBar()
|
void stopProgressBar()
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
Logger * makeProgressBar(bool printBuildLogs = false);
|
||||||
|
|
||||||
void startProgressBar(bool printBuildLogs = false);
|
void startProgressBar(bool printBuildLogs = false);
|
||||||
|
|
||||||
void stopProgressBar();
|
void stopProgressBar();
|
|
@ -2,6 +2,7 @@
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "loggers.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
@ -47,7 +48,10 @@ void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
||||||
unsigned long long downloadSize, unsigned long long narSize, Verbosity lvl)
|
unsigned long long downloadSize, unsigned long long narSize, Verbosity lvl)
|
||||||
{
|
{
|
||||||
if (!willBuild.empty()) {
|
if (!willBuild.empty()) {
|
||||||
printMsg(lvl, "these derivations will be built:");
|
if (willBuild.size() == 1)
|
||||||
|
printMsg(lvl, fmt("this derivation will be built:"));
|
||||||
|
else
|
||||||
|
printMsg(lvl, fmt("these %d derivations will be built:", willBuild.size()));
|
||||||
auto sorted = store->topoSortPaths(willBuild);
|
auto sorted = store->topoSortPaths(willBuild);
|
||||||
reverse(sorted.begin(), sorted.end());
|
reverse(sorted.begin(), sorted.end());
|
||||||
for (auto & i : sorted)
|
for (auto & i : sorted)
|
||||||
|
@ -55,9 +59,18 @@ void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!willSubstitute.empty()) {
|
if (!willSubstitute.empty()) {
|
||||||
printMsg(lvl, fmt("these paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
const float downloadSizeMiB = downloadSize / (1024.f * 1024.f);
|
||||||
downloadSize / (1024.0 * 1024.0),
|
const float narSizeMiB = narSize / (1024.f * 1024.f);
|
||||||
narSize / (1024.0 * 1024.0)));
|
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));
|
||||||
|
}
|
||||||
for (auto & i : willSubstitute)
|
for (auto & i : willSubstitute)
|
||||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
||||||
}
|
}
|
||||||
|
@ -75,7 +88,7 @@ string getArg(const string & opt,
|
||||||
Strings::iterator & i, const Strings::iterator & end)
|
Strings::iterator & i, const Strings::iterator & end)
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
if (i == end) throw UsageError(format("'%1%' requires an argument") % opt);
|
if (i == end) throw UsageError("'%1%' requires an argument", opt);
|
||||||
return *i;
|
return *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +182,7 @@ LegacyArgs::LegacyArgs(const std::string & programName,
|
||||||
.longName = "no-build-output",
|
.longName = "no-build-output",
|
||||||
.shortName = 'Q',
|
.shortName = 'Q',
|
||||||
.description = "do not show build output",
|
.description = "do not show build output",
|
||||||
.handler = {&settings.verboseBuild, false},
|
.handler = {[&]() {setLogFormat(LogFormat::raw); }},
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
|
@ -234,7 +247,7 @@ bool LegacyArgs::processArgs(const Strings & args, bool finish)
|
||||||
Strings ss(args);
|
Strings ss(args);
|
||||||
auto pos = ss.begin();
|
auto pos = ss.begin();
|
||||||
if (!parseArg(pos, ss.end()))
|
if (!parseArg(pos, ss.end()))
|
||||||
throw UsageError(format("unexpected argument '%1%'") % args.front());
|
throw UsageError("unexpected argument '%1%'", args.front());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +269,7 @@ void parseCmdLine(const string & programName, const Strings & args,
|
||||||
void printVersion(const string & programName)
|
void printVersion(const string & programName)
|
||||||
{
|
{
|
||||||
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
|
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
|
||||||
if (verbosity > Verbosity::Info) {
|
if (verbosity > lvlInfo) {
|
||||||
Strings cfg;
|
Strings cfg;
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
cfg.push_back("gc");
|
cfg.push_back("gc");
|
||||||
|
@ -281,7 +294,7 @@ void showManPage(const string & name)
|
||||||
restoreSignals();
|
restoreSignals();
|
||||||
setenv("MANPATH", settings.nixManDir.c_str(), 1);
|
setenv("MANPATH", settings.nixManDir.c_str(), 1);
|
||||||
execlp("man", "man", name.c_str(), nullptr);
|
execlp("man", "man", name.c_str(), nullptr);
|
||||||
throw SysError(format("command 'man %1%' failed") % name.c_str());
|
throw SysError("command 'man %1%' failed", name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -289,6 +302,8 @@ int handleExceptions(const string & programName, std::function<void()> fun)
|
||||||
{
|
{
|
||||||
ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this
|
ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this
|
||||||
|
|
||||||
|
ErrorInfo::programName = baseNameOf(programName);
|
||||||
|
|
||||||
string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
|
@ -304,12 +319,13 @@ int handleExceptions(const string & programName, std::function<void()> fun)
|
||||||
} catch (Exit & e) {
|
} catch (Exit & e) {
|
||||||
return e.status;
|
return e.status;
|
||||||
} catch (UsageError & e) {
|
} catch (UsageError & e) {
|
||||||
printError(
|
logError(e.info());
|
||||||
format(error + "%1%\nTry '%2% --help' for more information.")
|
printError("Try '%1% --help' for more information.", programName);
|
||||||
% e.what() % programName);
|
|
||||||
return 1;
|
return 1;
|
||||||
} catch (BaseError & e) {
|
} catch (BaseError & e) {
|
||||||
printError(format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
|
if (settings.showTrace && e.prefix() != "")
|
||||||
|
printError(e.prefix());
|
||||||
|
logError(e.info());
|
||||||
if (e.prefix() != "" && !settings.showTrace)
|
if (e.prefix() != "" && !settings.showTrace)
|
||||||
printError("(use '--show-trace' to show detailed location information)");
|
printError("(use '--show-trace' to show detailed location information)");
|
||||||
return e.status;
|
return e.status;
|
||||||
|
@ -346,7 +362,7 @@ RunPager::RunPager()
|
||||||
execlp("pager", "pager", nullptr);
|
execlp("pager", "pager", nullptr);
|
||||||
execlp("less", "less", nullptr);
|
execlp("less", "less", nullptr);
|
||||||
execlp("more", "more", nullptr);
|
execlp("more", "more", nullptr);
|
||||||
throw SysError(format("executing '%1%'") % pager);
|
throw SysError("executing '%1%'", pager);
|
||||||
});
|
});
|
||||||
|
|
||||||
pid.setKillSignal(SIGINT);
|
pid.setKillSignal(SIGINT);
|
||||||
|
|
|
@ -43,11 +43,11 @@ struct StorePathWithOutputs;
|
||||||
void printMissing(
|
void printMissing(
|
||||||
ref<Store> store,
|
ref<Store> store,
|
||||||
const std::vector<StorePathWithOutputs> & paths,
|
const std::vector<StorePathWithOutputs> & paths,
|
||||||
Verbosity lvl = Verbosity::Info);
|
Verbosity lvl = lvlInfo);
|
||||||
|
|
||||||
void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
||||||
const StorePathSet & willSubstitute, const StorePathSet & unknown,
|
const StorePathSet & willSubstitute, const StorePathSet & unknown,
|
||||||
unsigned long long downloadSize, unsigned long long narSize, Verbosity lvl = Verbosity::Info);
|
unsigned long long downloadSize, unsigned long long narSize, Verbosity lvl = lvlInfo);
|
||||||
|
|
||||||
string getArg(const string & opt,
|
string getArg(const string & opt,
|
||||||
Strings::iterator & i, const Strings::iterator & end);
|
Strings::iterator & i, const Strings::iterator & end);
|
||||||
|
@ -56,7 +56,7 @@ template<class N> N getIntArg(const string & opt,
|
||||||
Strings::iterator & i, const Strings::iterator & end, bool allowUnit)
|
Strings::iterator & i, const Strings::iterator & end, bool allowUnit)
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
if (i == end) throw UsageError(format("'%1%' requires an argument") % opt);
|
if (i == end) throw UsageError("'%1%' requires an argument", opt);
|
||||||
string s = *i;
|
string s = *i;
|
||||||
N multiplier = 1;
|
N multiplier = 1;
|
||||||
if (allowUnit && !s.empty()) {
|
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 == 'M') multiplier = 1ULL << 20;
|
||||||
else if (u == 'G') multiplier = 1ULL << 30;
|
else if (u == 'G') multiplier = 1ULL << 30;
|
||||||
else if (u == 'T') multiplier = 1ULL << 40;
|
else if (u == 'T') multiplier = 1ULL << 40;
|
||||||
else throw UsageError(format("invalid unit specifier '%1%'") % u);
|
else throw UsageError("invalid unit specifier '%1%'", u);
|
||||||
s.resize(s.size() - 1);
|
s.resize(s.size() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
N n;
|
N n;
|
||||||
if (!string2Int(s, n))
|
if (!string2Int(s, n))
|
||||||
throw UsageError(format("'%1%' requires an integer argument") % opt);
|
throw UsageError("'%1%' requires an integer argument", opt);
|
||||||
return n * multiplier;
|
return n * multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "types.hh"
|
#include "error.hh"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
|
@ -40,14 +40,14 @@ void BinaryCacheStore::init()
|
||||||
upsertFile(cacheInfoFile, "StoreDir: " + storeDir + "\n", "text/x-nix-cache-info");
|
upsertFile(cacheInfoFile, "StoreDir: " + storeDir + "\n", "text/x-nix-cache-info");
|
||||||
} else {
|
} else {
|
||||||
for (auto & line : tokenizeString<Strings>(*cacheInfo, "\n")) {
|
for (auto & line : tokenizeString<Strings>(*cacheInfo, "\n")) {
|
||||||
size_t colon = line.find(':');
|
size_t colon= line.find(':');
|
||||||
if (colon == std::string::npos) continue;
|
if (colon ==std::string::npos) continue;
|
||||||
auto name = line.substr(0, colon);
|
auto name = line.substr(0, colon);
|
||||||
auto value = trim(line.substr(colon + 1, std::string::npos));
|
auto value = trim(line.substr(colon + 1, std::string::npos));
|
||||||
if (name == "StoreDir") {
|
if (name == "StoreDir") {
|
||||||
if (value != storeDir)
|
if (value != storeDir)
|
||||||
throw Error(format("binary cache '%s' is for Nix stores with prefix '%s', not '%s'")
|
throw Error("binary cache '%s' is for Nix stores with prefix '%s', not '%s'",
|
||||||
% getUri() % value % storeDir);
|
getUri(), value, storeDir);
|
||||||
} else if (name == "WantMassQuery") {
|
} else if (name == "WantMassQuery") {
|
||||||
wantMassQuery.setDefault(value == "1" ? "true" : "false");
|
wantMassQuery.setDefault(value == "1" ? "true" : "false");
|
||||||
} else if (name == "Priority") {
|
} else if (name == "Priority") {
|
||||||
|
@ -93,7 +93,7 @@ std::shared_ptr<std::string> BinaryCacheStore::getFile(const std::string & path)
|
||||||
|
|
||||||
std::string BinaryCacheStore::narInfoFileFor(const StorePath & storePath)
|
std::string BinaryCacheStore::narInfoFileFor(const StorePath & storePath)
|
||||||
{
|
{
|
||||||
return storePathToHash(printStorePath(storePath)) + ".narinfo";
|
return std::string(storePath.hashPart()) + ".narinfo";
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
|
void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
|
||||||
|
@ -102,7 +102,7 @@ void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
|
||||||
|
|
||||||
upsertFile(narInfoFile, narInfo->to_string(*this), "text/x-nix-narinfo");
|
upsertFile(narInfoFile, narInfo->to_string(*this), "text/x-nix-narinfo");
|
||||||
|
|
||||||
auto hashPart = storePathToHash(printStorePath(narInfo->path));
|
std::string hashPart(narInfo->path.hashPart());
|
||||||
|
|
||||||
{
|
{
|
||||||
auto state_(state.lock());
|
auto state_(state.lock());
|
||||||
|
@ -137,7 +137,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||||
auto narInfo = make_ref<NarInfo>(info);
|
auto narInfo = make_ref<NarInfo>(info);
|
||||||
|
|
||||||
narInfo->narSize = nar->size();
|
narInfo->narSize = nar->size();
|
||||||
narInfo->narHash = hashString(HashType::SHA256, *nar);
|
narInfo->narHash = hashString(htSHA256, *nar);
|
||||||
|
|
||||||
if (info.narHash && info.narHash != narInfo->narHash)
|
if (info.narHash && info.narHash != narInfo->narHash)
|
||||||
throw Error("refusing to copy corrupted path '%1%' to binary cache", printStorePath(info.path));
|
throw Error("refusing to copy corrupted path '%1%' to binary cache", printStorePath(info.path));
|
||||||
|
@ -164,7 +164,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upsertFile(storePathToHash(printStorePath(info.path)) + ".ls", jsonOut.str(), "application/json");
|
upsertFile(std::string(info.path.to_string()) + ".ls", jsonOut.str(), "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compress the NAR. */
|
/* Compress the NAR. */
|
||||||
|
@ -172,16 +172,16 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||||
auto now1 = std::chrono::steady_clock::now();
|
auto now1 = std::chrono::steady_clock::now();
|
||||||
auto narCompressed = compress(compression, *nar, parallelCompression);
|
auto narCompressed = compress(compression, *nar, parallelCompression);
|
||||||
auto now2 = std::chrono::steady_clock::now();
|
auto now2 = std::chrono::steady_clock::now();
|
||||||
narInfo->fileHash = hashString(HashType::SHA256, *narCompressed);
|
narInfo->fileHash = hashString(htSHA256, *narCompressed);
|
||||||
narInfo->fileSize = narCompressed->size();
|
narInfo->fileSize = narCompressed->size();
|
||||||
|
|
||||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
|
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
|
||||||
printMsg(Verbosity::Talkative, "copying path '%1%' (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache",
|
printMsg(lvlTalkative, "copying path '%1%' (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache",
|
||||||
printStorePath(narInfo->path), narInfo->narSize,
|
printStorePath(narInfo->path), narInfo->narSize,
|
||||||
((1.0 - (double) narCompressed->size() / nar->size()) * 100.0),
|
((1.0 - (double) narCompressed->size() / nar->size()) * 100.0),
|
||||||
duration);
|
duration);
|
||||||
|
|
||||||
narInfo->url = "nar/" + narInfo->fileHash.to_string(Base::Base32, false) + ".nar"
|
narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar"
|
||||||
+ (compression == "xz" ? ".xz" :
|
+ (compression == "xz" ? ".xz" :
|
||||||
compression == "bzip2" ? ".bz2" :
|
compression == "bzip2" ? ".bz2" :
|
||||||
compression == "br" ? ".br" :
|
compression == "br" ? ".br" :
|
||||||
|
@ -209,7 +209,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||||
// to a GC'ed file, so overwriting might be useful...
|
// to a GC'ed file, so overwriting might be useful...
|
||||||
if (fileExists(key)) return;
|
if (fileExists(key)) return;
|
||||||
|
|
||||||
printMsg(Verbosity::Talkative, "creating debuginfo link from '%s' to '%s'", key, target);
|
printMsg(lvlTalkative, "creating debuginfo link from '%s' to '%s'", key, target);
|
||||||
|
|
||||||
upsertFile(key, json.dump(), "application/json");
|
upsertFile(key, json.dump(), "application/json");
|
||||||
};
|
};
|
||||||
|
@ -287,7 +287,7 @@ void BinaryCacheStore::narFromPath(const StorePath & storePath, Sink & sink)
|
||||||
try {
|
try {
|
||||||
getFile(info->url, *decompressor);
|
getFile(info->url, *decompressor);
|
||||||
} catch (NoSuchBinaryCacheFile & e) {
|
} catch (NoSuchBinaryCacheFile & e) {
|
||||||
throw SubstituteGone(e.what());
|
throw SubstituteGone(e.info());
|
||||||
}
|
}
|
||||||
|
|
||||||
decompressor->finish();
|
decompressor->finish();
|
||||||
|
@ -302,7 +302,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
|
||||||
{
|
{
|
||||||
auto uri = getUri();
|
auto uri = getUri();
|
||||||
auto storePathS = printStorePath(storePath);
|
auto storePathS = printStorePath(storePath);
|
||||||
auto act = std::make_shared<Activity>(*logger, Verbosity::Talkative, ActivityType::QueryPathInfo,
|
auto act = std::make_shared<Activity>(*logger, lvlTalkative, actQueryPathInfo,
|
||||||
fmt("querying info about '%s' on '%s'", storePathS, uri), Logger::Fields{storePathS, uri});
|
fmt("querying info about '%s' on '%s'", storePathS, uri), Logger::Fields{storePathS, uri});
|
||||||
PushActivity pact(act->id);
|
PushActivity pact(act->id);
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
|
||||||
const StorePathSet & references, RepairFlag repair)
|
const StorePathSet & references, RepairFlag repair)
|
||||||
{
|
{
|
||||||
ValidPathInfo info(computeStorePathForText(name, s, references));
|
ValidPathInfo info(computeStorePathForText(name, s, references));
|
||||||
info.references = cloneStorePathSet(references);
|
info.references = references;
|
||||||
|
|
||||||
if (repair || !isValidPath(info.path)) {
|
if (repair || !isValidPath(info.path)) {
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
|
@ -388,21 +388,19 @@ void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSe
|
||||||
|
|
||||||
narInfo->sigs.insert(sigs.begin(), sigs.end());
|
narInfo->sigs.insert(sigs.begin(), sigs.end());
|
||||||
|
|
||||||
auto narInfoFile = narInfoFileFor(narInfo->path);
|
|
||||||
|
|
||||||
writeNarInfo(narInfo);
|
writeNarInfo(narInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
|
std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
|
||||||
{
|
{
|
||||||
auto drvPath = path.clone();
|
auto drvPath = path;
|
||||||
|
|
||||||
if (!path.isDerivation()) {
|
if (!path.isDerivation()) {
|
||||||
try {
|
try {
|
||||||
auto info = queryPathInfo(path);
|
auto info = queryPathInfo(path);
|
||||||
// FIXME: add a "Log" field to .narinfo
|
// FIXME: add a "Log" field to .narinfo
|
||||||
if (!info->deriver) return nullptr;
|
if (!info->deriver) return nullptr;
|
||||||
drvPath = info->deriver->clone();
|
drvPath = *info->deriver;
|
||||||
} catch (InvalidPath &) {
|
} catch (InvalidPath &) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -22,7 +22,10 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||||
srcFiles = readDirectory(srcDir);
|
srcFiles = readDirectory(srcDir);
|
||||||
} catch (SysError & e) {
|
} catch (SysError & e) {
|
||||||
if (e.errNo == ENOTDIR) {
|
if (e.errNo == ENOTDIR) {
|
||||||
printError("warning: not including '%s' in the user environment because it's not a directory", srcDir);
|
logWarning({
|
||||||
|
.name = "Create links - directory",
|
||||||
|
.hint = hintfmt("not including '%s' in the user environment because it's not a directory", srcDir)
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
|
@ -41,7 +44,10 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||||
throw SysError("getting status of '%1%'", srcFile);
|
throw SysError("getting status of '%1%'", srcFile);
|
||||||
} catch (SysError & e) {
|
} catch (SysError & e) {
|
||||||
if (e.errNo == ENOENT || e.errNo == ENOTDIR) {
|
if (e.errNo == ENOENT || e.errNo == ENOTDIR) {
|
||||||
printError("warning: skipping dangling symlink '%s'", dstFile);
|
logWarning({
|
||||||
|
.name = "Create links - skipping symlink",
|
||||||
|
.hint = hintfmt("skipping dangling symlink '%s'", dstFile)
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
|
@ -72,15 +78,15 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||||
if (!S_ISDIR(lstat(target).st_mode))
|
if (!S_ISDIR(lstat(target).st_mode))
|
||||||
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
|
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
|
||||||
if (unlink(dstFile.c_str()) == -1)
|
if (unlink(dstFile.c_str()) == -1)
|
||||||
throw SysError(format("unlinking '%1%'") % dstFile);
|
throw SysError("unlinking '%1%'", dstFile);
|
||||||
if (mkdir(dstFile.c_str(), 0755) == -1)
|
if (mkdir(dstFile.c_str(), 0755) == -1)
|
||||||
throw SysError(format("creating directory '%1%'"));
|
throw SysError("creating directory '%1%'", dstFile);
|
||||||
createLinks(state, target, dstFile, state.priorities[dstFile]);
|
createLinks(state, target, dstFile, state.priorities[dstFile]);
|
||||||
createLinks(state, srcFile, dstFile, priority);
|
createLinks(state, srcFile, dstFile, priority);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (errno != ENOENT)
|
} else if (errno != ENOENT)
|
||||||
throw SysError(format("getting status of '%1%'") % dstFile);
|
throw SysError("getting status of '%1%'", dstFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -99,11 +105,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||||
if (prevPriority < priority)
|
if (prevPriority < priority)
|
||||||
continue;
|
continue;
|
||||||
if (unlink(dstFile.c_str()) == -1)
|
if (unlink(dstFile.c_str()) == -1)
|
||||||
throw SysError(format("unlinking '%1%'") % dstFile);
|
throw SysError("unlinking '%1%'", dstFile);
|
||||||
} else if (S_ISDIR(dstSt.st_mode))
|
} else if (S_ISDIR(dstSt.st_mode))
|
||||||
throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile);
|
throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile);
|
||||||
} else if (errno != ENOENT)
|
} else if (errno != ENOENT)
|
||||||
throw SysError(format("getting status of '%1%'") % dstFile);
|
throw SysError("getting status of '%1%'", dstFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
createSymlink(srcFile, dstFile);
|
createSymlink(srcFile, dstFile);
|
||||||
|
|
|
@ -18,7 +18,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||||
|
|
||||||
auto getAttr = [&](const string & name) {
|
auto getAttr = [&](const string & name) {
|
||||||
auto i = drv.env.find(name);
|
auto i = drv.env.find(name);
|
||||||
if (i == drv.env.end()) throw Error(format("attribute '%s' missing") % name);
|
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
|
||||||
return i->second;
|
return i->second;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||||
auto executable = drv.env.find("executable");
|
auto executable = drv.env.find("executable");
|
||||||
if (executable != drv.env.end() && executable->second == "1") {
|
if (executable != drv.env.end() && executable->second == "1") {
|
||||||
if (chmod(storePath.c_str(), 0755) == -1)
|
if (chmod(storePath.c_str(), 0755) == -1)
|
||||||
throw SysError(format("making '%1%' executable") % storePath);
|
throw SysError("making '%1%' executable", storePath);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||||
if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/';
|
if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/';
|
||||||
auto ht = parseHashTypeOpt(getAttr("outputHashAlgo"));
|
auto ht = parseHashTypeOpt(getAttr("outputHashAlgo"));
|
||||||
auto h = Hash(getAttr("outputHash"), ht);
|
auto h = Hash(getAttr("outputHash"), ht);
|
||||||
fetch(hashedMirror + printHashType(*h.type) + "/" + h.to_string(Base::Base16, false));
|
fetch(hashedMirror + printHashType(*h.type) + "/" + h.to_string(Base16, false));
|
||||||
return;
|
return;
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
debug(e.what());
|
debug(e.what());
|
||||||
|
|
|
@ -21,7 +21,7 @@ std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash)
|
||||||
{
|
{
|
||||||
return "fixed:"
|
return "fixed:"
|
||||||
+ makeFileIngestionPrefix(method)
|
+ makeFileIngestionPrefix(method)
|
||||||
+ hash.to_string();
|
+ hash.to_string(Base32, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME Put this somewhere?
|
// FIXME Put this somewhere?
|
||||||
|
@ -31,7 +31,7 @@ template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||||
std::string renderContentAddress(ContentAddress ca) {
|
std::string renderContentAddress(ContentAddress ca) {
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[](TextHash th) {
|
[](TextHash th) {
|
||||||
return "text:" + th.hash.to_string();
|
return "text:" + th.hash.to_string(Base::Base32, true);
|
||||||
},
|
},
|
||||||
[](FileSystemHash fsh) {
|
[](FileSystemHash fsh) {
|
||||||
return makeFixedOutputCA(fsh.method, fsh.hash);
|
return makeFixedOutputCA(fsh.method, fsh.hash);
|
||||||
|
@ -46,7 +46,7 @@ ContentAddress parseContentAddress(std::string_view rawCa) {
|
||||||
if (prefix == "text") {
|
if (prefix == "text") {
|
||||||
auto hashTypeAndHash = rawCa.substr(prefixSeparator+1, string::npos);
|
auto hashTypeAndHash = rawCa.substr(prefixSeparator+1, string::npos);
|
||||||
Hash hash = Hash(string(hashTypeAndHash));
|
Hash hash = Hash(string(hashTypeAndHash));
|
||||||
if (*hash.type != HashType::SHA256) {
|
if (*hash.type != htSHA256) {
|
||||||
throw Error("parseContentAddress: the text hash should have type SHA256");
|
throw Error("parseContentAddress: the text hash should have type SHA256");
|
||||||
}
|
}
|
||||||
return TextHash { hash };
|
return TextHash { hash };
|
||||||
|
|
|
@ -73,6 +73,18 @@ struct TunnelLogger : public Logger
|
||||||
enqueueMsg(*buf.s);
|
enqueueMsg(*buf.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void logEI(const ErrorInfo & ei) override
|
||||||
|
{
|
||||||
|
if (ei.level > verbosity) return;
|
||||||
|
|
||||||
|
std::stringstream oss;
|
||||||
|
oss << ei;
|
||||||
|
|
||||||
|
StringSink buf;
|
||||||
|
buf << STDERR_NEXT << oss.str() << "\n"; // (fs.s + "\n");
|
||||||
|
enqueueMsg(*buf.s);
|
||||||
|
}
|
||||||
|
|
||||||
/* startWork() means that we're starting an operation for which we
|
/* startWork() means that we're starting an operation for which we
|
||||||
want to send out stderr to the client. */
|
want to send out stderr to the client. */
|
||||||
void startWork()
|
void startWork()
|
||||||
|
@ -114,13 +126,7 @@ struct TunnelLogger : public Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSink buf;
|
StringSink buf;
|
||||||
buf << STDERR_START_ACTIVITY
|
buf << STDERR_START_ACTIVITY << act << lvl << type << s << fields << parent;
|
||||||
<< act
|
|
||||||
<< (uint64_t) lvl
|
|
||||||
<< (uint64_t) type
|
|
||||||
<< s
|
|
||||||
<< fields
|
|
||||||
<< parent;
|
|
||||||
enqueueMsg(*buf.s);
|
enqueueMsg(*buf.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,10 +142,7 @@ struct TunnelLogger : public Logger
|
||||||
{
|
{
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
|
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
|
||||||
StringSink buf;
|
StringSink buf;
|
||||||
buf << STDERR_RESULT
|
buf << STDERR_RESULT << act << type << fields;
|
||||||
<< act
|
|
||||||
<< (uint64_t) type
|
|
||||||
<< fields;
|
|
||||||
enqueueMsg(*buf.s);
|
enqueueMsg(*buf.s);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -290,7 +293,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
auto path = store->parseStorePath(readString(from));
|
auto path = store->parseStorePath(readString(from));
|
||||||
logger->startWork();
|
logger->startWork();
|
||||||
StorePathSet paths; // FIXME
|
StorePathSet paths; // FIXME
|
||||||
paths.insert(path.clone());
|
paths.insert(path);
|
||||||
auto res = store->querySubstitutablePaths(paths);
|
auto res = store->querySubstitutablePaths(paths);
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
to << (res.count(path) != 0);
|
to << (res.count(path) != 0);
|
||||||
|
@ -311,7 +314,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
logger->startWork();
|
logger->startWork();
|
||||||
auto hash = store->queryPathInfo(path)->narHash;
|
auto hash = store->queryPathInfo(path)->narHash;
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
to << hash.to_string(Base::Base16, false);
|
to << hash.to_string(Base16, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +327,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
StorePathSet paths;
|
StorePathSet paths;
|
||||||
if (op == wopQueryReferences)
|
if (op == wopQueryReferences)
|
||||||
for (auto & i : store->queryPathInfo(path)->references)
|
for (auto & i : store->queryPathInfo(path)->references)
|
||||||
paths.insert(i.clone());
|
paths.insert(i);
|
||||||
else if (op == wopQueryReferrers)
|
else if (op == wopQueryReferrers)
|
||||||
store->queryReferrers(path, paths);
|
store->queryReferrers(path, paths);
|
||||||
else if (op == wopQueryValidDerivers)
|
else if (op == wopQueryValidDerivers)
|
||||||
|
@ -338,8 +341,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
case wopQueryDerivationOutputNames: {
|
case wopQueryDerivationOutputNames: {
|
||||||
auto path = store->parseStorePath(readString(from));
|
auto path = store->parseStorePath(readString(from));
|
||||||
logger->startWork();
|
logger->startWork();
|
||||||
StringSet names;
|
auto names = store->readDerivation(path).outputNames();
|
||||||
names = store->queryDerivationOutputNames(path);
|
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
to << names;
|
to << names;
|
||||||
break;
|
break;
|
||||||
|
@ -367,8 +369,10 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
std::string s, baseName;
|
std::string s, baseName;
|
||||||
FileIngestionMethod method;
|
FileIngestionMethod method;
|
||||||
{
|
{
|
||||||
bool fixed, recursive;
|
bool fixed; uint8_t recursive;
|
||||||
from >> baseName >> fixed /* obsolete */ >> recursive >> s;
|
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 };
|
method = FileIngestionMethod { recursive };
|
||||||
/* Compatibility hack. */
|
/* Compatibility hack. */
|
||||||
if (!fixed) {
|
if (!fixed) {
|
||||||
|
@ -559,7 +563,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
clientSettings.maxBuildJobs = readInt(from);
|
clientSettings.maxBuildJobs = readInt(from);
|
||||||
clientSettings.maxSilentTime = readInt(from);
|
clientSettings.maxSilentTime = readInt(from);
|
||||||
readInt(from); // obsolete useBuildHook
|
readInt(from); // obsolete useBuildHook
|
||||||
clientSettings.verboseBuild = Verbosity::Error == (Verbosity) readInt(from);
|
clientSettings.verboseBuild = lvlError == (Verbosity) readInt(from);
|
||||||
readInt(from); // obsolete logType
|
readInt(from); // obsolete logType
|
||||||
readInt(from); // obsolete printBuildTrace
|
readInt(from); // obsolete printBuildTrace
|
||||||
clientSettings.buildCores = readInt(from);
|
clientSettings.buildCores = readInt(from);
|
||||||
|
@ -589,9 +593,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
auto path = store->parseStorePath(readString(from));
|
auto path = store->parseStorePath(readString(from));
|
||||||
logger->startWork();
|
logger->startWork();
|
||||||
SubstitutablePathInfos infos;
|
SubstitutablePathInfos infos;
|
||||||
StorePathSet paths;
|
store->querySubstitutablePathInfos({path}, infos);
|
||||||
paths.insert(path.clone()); // FIXME
|
|
||||||
store->querySubstitutablePathInfos(paths, infos);
|
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
auto i = infos.find(path);
|
auto i = infos.find(path);
|
||||||
if (i == infos.end())
|
if (i == infos.end())
|
||||||
|
@ -644,7 +646,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
|
||||||
to << 1;
|
to << 1;
|
||||||
to << (info->deriver ? store->printStorePath(*info->deriver) : "")
|
to << (info->deriver ? store->printStorePath(*info->deriver) : "")
|
||||||
<< info->narHash.to_string(Base::Base16, false);
|
<< info->narHash.to_string(Base16, false);
|
||||||
writeStorePaths(*store, to, info->references);
|
writeStorePaths(*store, to, info->references);
|
||||||
to << info->registrationTime << info->narSize;
|
to << info->registrationTime << info->narSize;
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
|
||||||
|
@ -704,7 +706,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
auto deriver = readString(from);
|
auto deriver = readString(from);
|
||||||
if (deriver != "")
|
if (deriver != "")
|
||||||
info.deriver = store->parseStorePath(deriver);
|
info.deriver = store->parseStorePath(deriver);
|
||||||
info.narHash = Hash(readString(from), HashType::SHA256);
|
info.narHash = Hash(readString(from), htSHA256);
|
||||||
info.references = readStorePaths<StorePathSet>(*store, from);
|
info.references = readStorePaths<StorePathSet>(*store, from);
|
||||||
from >> info.registrationTime >> info.narSize >> info.ultimate;
|
from >> info.registrationTime >> info.narSize >> info.ultimate;
|
||||||
info.sigs = readStrings<StringSet>(from);
|
info.sigs = readStrings<StringSet>(from);
|
||||||
|
@ -753,7 +755,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw Error(format("invalid operation %1%") % op);
|
throw Error("invalid operation %1%", op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -788,7 +790,7 @@ void processConnection(
|
||||||
|
|
||||||
Finally finally([&]() {
|
Finally finally([&]() {
|
||||||
_isInterrupted = false;
|
_isInterrupted = false;
|
||||||
prevLogger->log(Verbosity::Debug, fmt("%d operations", opCount));
|
prevLogger->log(lvlDebug, fmt("%d operations", opCount));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) {
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) {
|
||||||
|
|
|
@ -8,28 +8,6 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
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::optional<FileSystemHash>(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
|
const StorePath & BasicDerivation::findOutput(const string & id) const
|
||||||
{
|
{
|
||||||
auto i = outputs.find(id);
|
auto i = outputs.find(id);
|
||||||
|
@ -48,9 +26,9 @@ bool BasicDerivation::isBuiltin() const
|
||||||
StorePath writeDerivation(ref<Store> store,
|
StorePath writeDerivation(ref<Store> store,
|
||||||
const Derivation & drv, std::string_view name, RepairFlag repair)
|
const Derivation & drv, std::string_view name, RepairFlag repair)
|
||||||
{
|
{
|
||||||
auto references = cloneStorePathSet(drv.inputSrcs);
|
auto references = drv.inputSrcs;
|
||||||
for (auto & i : drv.inputDrvs)
|
for (auto & i : drv.inputDrvs)
|
||||||
references.insert(i.first.clone());
|
references.insert(i.first);
|
||||||
/* Note that the outputs of a derivation are *not* references
|
/* Note that the outputs of a derivation are *not* references
|
||||||
(that can be missing (of course) and should not necessarily be
|
(that can be missing (of course) and should not necessarily be
|
||||||
held during a garbage collection). */
|
held during a garbage collection). */
|
||||||
|
@ -68,7 +46,7 @@ static void expect(std::istream & str, const string & s)
|
||||||
char s2[s.size()];
|
char s2[s.size()];
|
||||||
str.read(s2, s.size());
|
str.read(s2, s.size());
|
||||||
if (string(s2, s.size()) != s)
|
if (string(s2, s.size()) != s)
|
||||||
throw FormatError(format("expected string '%1%'") % s);
|
throw FormatError("expected string '%1%'", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +73,7 @@ static Path parsePath(std::istream & str)
|
||||||
{
|
{
|
||||||
string s = parseString(str);
|
string s = parseString(str);
|
||||||
if (s.size() == 0 || s[0] != '/')
|
if (s.size() == 0 || s[0] != '/')
|
||||||
throw FormatError(format("bad path '%1%' in derivation") % s);
|
throw FormatError("bad path '%1%' in derivation", s);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,9 +108,9 @@ static DerivationOutput parseDerivationOutput(const Store & store, istringstream
|
||||||
expect(str, ","); const auto hash = parseString(str);
|
expect(str, ","); const auto hash = parseString(str);
|
||||||
expect(str, ")");
|
expect(str, ")");
|
||||||
|
|
||||||
auto method = FileIngestionMethod::Flat;
|
|
||||||
std::optional<FileSystemHash> fsh;
|
std::optional<FileSystemHash> fsh;
|
||||||
if (hashAlgo != "") {
|
if (hashAlgo != "") {
|
||||||
|
auto method = FileIngestionMethod::Flat;
|
||||||
if (string(hashAlgo, 0, 2) == "r:") {
|
if (string(hashAlgo, 0, 2) == "r:") {
|
||||||
method = FileIngestionMethod::Recursive;
|
method = FileIngestionMethod::Recursive;
|
||||||
hashAlgo = string(hashAlgo, 2);
|
hashAlgo = string(hashAlgo, 2);
|
||||||
|
@ -144,7 +122,10 @@ static DerivationOutput parseDerivationOutput(const Store & store, istringstream
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return DerivationOutput(std::move(path), std::move(fsh));
|
return DerivationOutput {
|
||||||
|
.path = std::move(path),
|
||||||
|
.hash = std::move(fsh),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,7 +138,8 @@ static Derivation parseDerivation(const Store & store, const string & s)
|
||||||
/* Parse the list of outputs. */
|
/* Parse the list of outputs. */
|
||||||
while (!endOfList(str)) {
|
while (!endOfList(str)) {
|
||||||
expect(str, "("); std::string id = parseString(str);
|
expect(str, "("); std::string id = parseString(str);
|
||||||
drv.outputs.emplace(id, parseDerivationOutput(store, str));
|
auto output = parseDerivationOutput(store, str);
|
||||||
|
drv.outputs.emplace(std::move(id), std::move(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse the list of input derivations. */
|
/* Parse the list of input derivations. */
|
||||||
|
@ -198,7 +180,7 @@ Derivation readDerivation(const Store & store, const Path & drvPath)
|
||||||
try {
|
try {
|
||||||
return parseDerivation(store, readFile(drvPath));
|
return parseDerivation(store, readFile(drvPath));
|
||||||
} catch (FormatError & e) {
|
} catch (FormatError & e) {
|
||||||
throw Error(format("error parsing derivation '%1%': %2%") % drvPath % e.msg());
|
throw Error("error parsing derivation '%1%': %2%", drvPath, e.msg());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +188,12 @@ Derivation readDerivation(const Store & store, const Path & drvPath)
|
||||||
Derivation Store::derivationFromPath(const StorePath & drvPath)
|
Derivation Store::derivationFromPath(const StorePath & drvPath)
|
||||||
{
|
{
|
||||||
ensurePath(drvPath);
|
ensurePath(drvPath);
|
||||||
|
return readDerivation(drvPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Derivation Store::readDerivation(const StorePath & drvPath)
|
||||||
|
{
|
||||||
auto accessor = getFSAccessor();
|
auto accessor = getFSAccessor();
|
||||||
try {
|
try {
|
||||||
return parseDerivation(*this, accessor->readFile(printStorePath(drvPath)));
|
return parseDerivation(*this, accessor->readFile(printStorePath(drvPath)));
|
||||||
|
@ -367,9 +355,9 @@ Hash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutput
|
||||||
/* Return a fixed hash for fixed-output derivations. */
|
/* Return a fixed hash for fixed-output derivations. */
|
||||||
if (drv.isFixedOutput()) {
|
if (drv.isFixedOutput()) {
|
||||||
DerivationOutputs::const_iterator i = drv.outputs.begin();
|
DerivationOutputs::const_iterator i = drv.outputs.begin();
|
||||||
return hashString(HashType::SHA256, "fixed:out:"
|
return hashString(htSHA256, "fixed:out:"
|
||||||
+ i->second.hash->printMethodAlgo() + ":"
|
+ i->second.hash->printMethodAlgo() + ":"
|
||||||
+ i->second.hash->hash.to_string(Base::Base16, false) + ":"
|
+ i->second.hash->hash.to_string(Base16, false) + ":"
|
||||||
+ store.printStorePath(i->second.path));
|
+ store.printStorePath(i->second.path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,13 +368,13 @@ Hash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutput
|
||||||
auto h = drvHashes.find(i.first);
|
auto h = drvHashes.find(i.first);
|
||||||
if (h == drvHashes.end()) {
|
if (h == drvHashes.end()) {
|
||||||
assert(store.isValidPath(i.first));
|
assert(store.isValidPath(i.first));
|
||||||
h = drvHashes.insert_or_assign(i.first.clone(), hashDerivationModulo(store,
|
h = drvHashes.insert_or_assign(i.first, hashDerivationModulo(store,
|
||||||
readDerivation(store, store.toRealPath(i.first)), false)).first;
|
store.readDerivation(i.first), false)).first;
|
||||||
}
|
}
|
||||||
inputs2.insert_or_assign(h->second.to_string(Base::Base16, false), i.second);
|
inputs2.insert_or_assign(h->second.to_string(Base16, false), i.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hashString(HashType::SHA256, drv.unparse(store, maskOutputs, &inputs2));
|
return hashString(htSHA256, drv.unparse(store, maskOutputs, &inputs2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -408,7 +396,7 @@ StorePathSet BasicDerivation::outputPaths() const
|
||||||
{
|
{
|
||||||
StorePathSet paths;
|
StorePathSet paths;
|
||||||
for (auto & i : outputs)
|
for (auto & i : outputs)
|
||||||
paths.insert(i.second.path.clone());
|
paths.insert(i.second.path);
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,23 +406,35 @@ static DerivationOutput readDerivationOutput(Source & in, const Store & store)
|
||||||
auto hashAlgo = readString(in);
|
auto hashAlgo = readString(in);
|
||||||
const auto hash = readString(in);
|
const auto hash = readString(in);
|
||||||
|
|
||||||
auto method = FileIngestionMethod::Flat;
|
|
||||||
std::optional<FileSystemHash> fsh;
|
std::optional<FileSystemHash> fsh;
|
||||||
if (hashAlgo != "") {
|
if (hashAlgo != "") {
|
||||||
|
auto method = FileIngestionMethod::Flat;
|
||||||
if (string(hashAlgo, 0, 2) == "r:") {
|
if (string(hashAlgo, 0, 2) == "r:") {
|
||||||
method = FileIngestionMethod::Recursive;
|
method = FileIngestionMethod::Recursive;
|
||||||
hashAlgo = string(hashAlgo, 2);
|
hashAlgo = string(hashAlgo, 2);
|
||||||
}
|
}
|
||||||
HashType hashType = parseHashType(hashAlgo);
|
const HashType hashType = parseHashType(hashAlgo);
|
||||||
fsh = FileSystemHash {
|
fsh = FileSystemHash {
|
||||||
std::move(method),
|
std::move(method),
|
||||||
Hash(hash, hashType),
|
Hash(hash, hashType),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return DerivationOutput(std::move(path), std::move(fsh));
|
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)
|
Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv)
|
||||||
{
|
{
|
||||||
drv.outputs.clear();
|
drv.outputs.clear();
|
||||||
|
@ -442,7 +442,7 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv)
|
||||||
for (size_t n = 0; n < nr; n++) {
|
for (size_t n = 0; n < nr; n++) {
|
||||||
auto name = readString(in);
|
auto name = readString(in);
|
||||||
auto output = readDerivationOutput(in, store);
|
auto output = readDerivationOutput(in, store);
|
||||||
drv.outputs.emplace(name, output);
|
drv.outputs.emplace(std::move(name), std::move(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
drv.inputSrcs = readStorePaths<StorePathSet>(store, in);
|
drv.inputSrcs = readStorePaths<StorePathSet>(store, in);
|
||||||
|
@ -479,7 +479,7 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
|
||||||
std::string hashPlaceholder(const std::string & outputName)
|
std::string hashPlaceholder(const std::string & outputName)
|
||||||
{
|
{
|
||||||
// FIXME: memoize?
|
// FIXME: memoize?
|
||||||
return "/" + hashString(HashType::SHA256, "nix-output:" + outputName).to_string(Base::Base32, false);
|
return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,6 @@ struct DerivationOutput
|
||||||
{
|
{
|
||||||
StorePath path;
|
StorePath path;
|
||||||
std::optional<FileSystemHash> hash; /* hash used for expected hash computation */
|
std::optional<FileSystemHash> hash; /* hash used for expected hash computation */
|
||||||
DerivationOutput(StorePath && path, std::optional<FileSystemHash> && hash)
|
|
||||||
: path(std::move(path))
|
|
||||||
, hash(std::move(hash))
|
|
||||||
{ }
|
|
||||||
DerivationOutput(const DerivationOutput &) = default;
|
|
||||||
DerivationOutput(DerivationOutput &&) = default;
|
|
||||||
DerivationOutput & operator = (const DerivationOutput &) = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::map<string, DerivationOutput> DerivationOutputs;
|
typedef std::map<string, DerivationOutput> DerivationOutputs;
|
||||||
|
@ -44,7 +37,6 @@ struct BasicDerivation
|
||||||
StringPairs env;
|
StringPairs env;
|
||||||
|
|
||||||
BasicDerivation() { }
|
BasicDerivation() { }
|
||||||
explicit BasicDerivation(const BasicDerivation & other);
|
|
||||||
virtual ~BasicDerivation() { };
|
virtual ~BasicDerivation() { };
|
||||||
|
|
||||||
/* Return the path corresponding to the output identifier `id' in
|
/* Return the path corresponding to the output identifier `id' in
|
||||||
|
@ -59,6 +51,8 @@ struct BasicDerivation
|
||||||
/* Return the output paths of a derivation. */
|
/* Return the output paths of a derivation. */
|
||||||
StorePathSet outputPaths() const;
|
StorePathSet outputPaths() const;
|
||||||
|
|
||||||
|
/* Return the output names of a derivation. */
|
||||||
|
StringSet outputNames() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Derivation : BasicDerivation
|
struct Derivation : BasicDerivation
|
||||||
|
@ -70,8 +64,6 @@ struct Derivation : BasicDerivation
|
||||||
std::map<std::string, StringSet> * actualInputs = nullptr) const;
|
std::map<std::string, StringSet> * actualInputs = nullptr) const;
|
||||||
|
|
||||||
Derivation() { }
|
Derivation() { }
|
||||||
Derivation(Derivation && other) = default;
|
|
||||||
explicit Derivation(const Derivation & other);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ struct HashAndWriteSink : Sink
|
||||||
{
|
{
|
||||||
Sink & writeSink;
|
Sink & writeSink;
|
||||||
HashSink hashSink;
|
HashSink hashSink;
|
||||||
HashAndWriteSink(Sink & writeSink) : writeSink(writeSink), hashSink(HashType::SHA256)
|
HashAndWriteSink(Sink & writeSink) : writeSink(writeSink), hashSink(htSHA256)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
virtual void operator () (const unsigned char * data, size_t len)
|
virtual void operator () (const unsigned char * data, size_t len)
|
||||||
|
@ -34,7 +34,7 @@ void Store::exportPaths(const StorePathSet & paths, Sink & sink)
|
||||||
//logger->incExpected(doneLabel, sorted.size());
|
//logger->incExpected(doneLabel, sorted.size());
|
||||||
|
|
||||||
for (auto & path : sorted) {
|
for (auto & path : sorted) {
|
||||||
//Activity act(*logger, Verbosity::Info, format("exporting path '%s'") % path);
|
//Activity act(*logger, lvlInfo, format("exporting path '%s'") % path);
|
||||||
sink << 1;
|
sink << 1;
|
||||||
exportPath(path, sink);
|
exportPath(path, sink);
|
||||||
//logger->incProgress(doneLabel);
|
//logger->incProgress(doneLabel);
|
||||||
|
@ -57,7 +57,7 @@ void Store::exportPath(const StorePath & path, Sink & sink)
|
||||||
Hash hash = hashAndWriteSink.currentHash();
|
Hash hash = hashAndWriteSink.currentHash();
|
||||||
if (hash != info->narHash && info->narHash != Hash(*info->narHash.type))
|
if (hash != info->narHash && info->narHash != Hash(*info->narHash.type))
|
||||||
throw Error("hash of path '%s' has changed from '%s' to '%s'!",
|
throw Error("hash of path '%s' has changed from '%s' to '%s'!",
|
||||||
printStorePath(path), info->narHash.to_string(), hash.to_string());
|
printStorePath(path), info->narHash.to_string(Base32, true), hash.to_string(Base32, true));
|
||||||
|
|
||||||
hashAndWriteSink
|
hashAndWriteSink
|
||||||
<< exportMagic
|
<< exportMagic
|
||||||
|
@ -86,7 +86,7 @@ StorePaths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> acces
|
||||||
|
|
||||||
ValidPathInfo info(parseStorePath(readString(source)));
|
ValidPathInfo info(parseStorePath(readString(source)));
|
||||||
|
|
||||||
//Activity act(*logger, Verbosity::Info, format("importing path '%s'") % info.path);
|
//Activity act(*logger, lvlInfo, format("importing path '%s'") % info.path);
|
||||||
|
|
||||||
info.references = readStorePaths<StorePathSet>(*this, source);
|
info.references = readStorePaths<StorePathSet>(*this, source);
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ StorePaths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> acces
|
||||||
if (deriver != "")
|
if (deriver != "")
|
||||||
info.deriver = parseStorePath(deriver);
|
info.deriver = parseStorePath(deriver);
|
||||||
|
|
||||||
info.narHash = hashString(HashType::SHA256, *tee.source.data);
|
info.narHash = hashString(htSHA256, *tee.source.data);
|
||||||
info.narSize = tee.source.data->size();
|
info.narSize = tee.source.data->size();
|
||||||
|
|
||||||
// Ignore optional legacy signature.
|
// Ignore optional legacy signature.
|
||||||
|
@ -105,7 +105,7 @@ StorePaths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> acces
|
||||||
auto source = StringSource { *tee.source.data };
|
auto source = StringSource { *tee.source.data };
|
||||||
addToStore(info, source, NoRepair, checkSigs, accessor);
|
addToStore(info, source, NoRepair, checkSigs, accessor);
|
||||||
|
|
||||||
res.push_back(info.path.clone());
|
res.push_back(info.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -72,23 +72,34 @@ struct curlFileTransfer : public FileTransfer
|
||||||
|
|
||||||
curl_off_t writtenToSink = 0;
|
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,
|
TransferItem(curlFileTransfer & fileTransfer,
|
||||||
const FileTransferRequest & request,
|
const FileTransferRequest & request,
|
||||||
Callback<FileTransferResult> && callback)
|
Callback<FileTransferResult> && callback)
|
||||||
: fileTransfer(fileTransfer)
|
: fileTransfer(fileTransfer)
|
||||||
, request(request)
|
, request(request)
|
||||||
, act(*logger, Verbosity::Talkative, ActivityType::Download,
|
, act(*logger, lvlTalkative, actFileTransfer,
|
||||||
fmt(request.data ? "uploading '%s'" : "downloading '%s'", request.uri),
|
fmt(request.data ? "uploading '%s'" : "downloading '%s'", request.uri),
|
||||||
{request.uri}, request.parentAct)
|
{request.uri}, request.parentAct)
|
||||||
, callback(std::move(callback))
|
, callback(std::move(callback))
|
||||||
, finalSink([this](const unsigned char * data, size_t len) {
|
, finalSink([this](const unsigned char * data, size_t len) {
|
||||||
if (this->request.dataCallback) {
|
if (this->request.dataCallback) {
|
||||||
long httpStatus = 0;
|
auto httpStatus = getHTTPStatus();
|
||||||
curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus);
|
|
||||||
|
|
||||||
/* Only write data to the sink if this is a
|
/* Only write data to the sink if this is a
|
||||||
successful response. */
|
successful response. */
|
||||||
if (httpStatus == 0 || httpStatus == 200 || httpStatus == 201 || httpStatus == 206) {
|
if (successfulStatuses.count(httpStatus)) {
|
||||||
writtenToSink += len;
|
writtenToSink += len;
|
||||||
this->request.dataCallback((char *) data, len);
|
this->request.dataCallback((char *) data, len);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +123,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
if (requestHeaders) curl_slist_free_all(requestHeaders);
|
if (requestHeaders) curl_slist_free_all(requestHeaders);
|
||||||
try {
|
try {
|
||||||
if (!done)
|
if (!done)
|
||||||
fail(FileTransferError(Interrupted, format("download of '%s' was interrupted") % request.uri));
|
fail(FileTransferError(Interrupted, "download of '%s' was interrupted", request.uri));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -163,7 +174,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
{
|
{
|
||||||
size_t realSize = size * nmemb;
|
size_t realSize = size * nmemb;
|
||||||
std::string line((char *) contents, realSize);
|
std::string line((char *) contents, realSize);
|
||||||
printMsg(Verbosity::Vomit, format("got header for '%s': %s") % request.uri % trim(line));
|
printMsg(lvlVomit, format("got header for '%s': %s") % request.uri % trim(line));
|
||||||
if (line.compare(0, 5, "HTTP/") == 0) { // new response starts
|
if (line.compare(0, 5, "HTTP/") == 0) { // new response starts
|
||||||
result.etag = "";
|
result.etag = "";
|
||||||
auto ss = tokenizeString<vector<string>>(line, " ");
|
auto ss = tokenizeString<vector<string>>(line, " ");
|
||||||
|
@ -246,7 +257,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
|
|
||||||
curl_easy_reset(req);
|
curl_easy_reset(req);
|
||||||
|
|
||||||
if (verbosity >= Verbosity::Vomit) {
|
if (verbosity >= lvlVomit) {
|
||||||
curl_easy_setopt(req, CURLOPT_VERBOSE, 1);
|
curl_easy_setopt(req, CURLOPT_VERBOSE, 1);
|
||||||
curl_easy_setopt(req, CURLOPT_DEBUGFUNCTION, TransferItem::debugCallback);
|
curl_easy_setopt(req, CURLOPT_DEBUGFUNCTION, TransferItem::debugCallback);
|
||||||
}
|
}
|
||||||
|
@ -316,8 +327,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
|
|
||||||
void finish(CURLcode code)
|
void finish(CURLcode code)
|
||||||
{
|
{
|
||||||
long httpStatus = 0;
|
auto httpStatus = getHTTPStatus();
|
||||||
curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus);
|
|
||||||
|
|
||||||
char * effectiveUriCStr;
|
char * effectiveUriCStr;
|
||||||
curl_easy_getinfo(req, CURLINFO_EFFECTIVE_URL, &effectiveUriCStr);
|
curl_easy_getinfo(req, CURLINFO_EFFECTIVE_URL, &effectiveUriCStr);
|
||||||
|
@ -343,8 +353,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
if (writeException)
|
if (writeException)
|
||||||
failEx(writeException);
|
failEx(writeException);
|
||||||
|
|
||||||
else if (code == CURLE_OK &&
|
else if (code == CURLE_OK && successfulStatuses.count(httpStatus))
|
||||||
(httpStatus == 200 || httpStatus == 201 || httpStatus == 204 || httpStatus == 206 || httpStatus == 304 || httpStatus == 226 /* FTP */ || httpStatus == 0 /* other protocol */))
|
|
||||||
{
|
{
|
||||||
result.cached = httpStatus == 304;
|
result.cached = httpStatus == 304;
|
||||||
act.progress(result.bodySize, result.bodySize);
|
act.progress(result.bodySize, result.bodySize);
|
||||||
|
@ -517,7 +526,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
int running;
|
int running;
|
||||||
CURLMcode mc = curl_multi_perform(curlm, &running);
|
CURLMcode mc = curl_multi_perform(curlm, &running);
|
||||||
if (mc != CURLM_OK)
|
if (mc != CURLM_OK)
|
||||||
throw nix::Error(format("unexpected error from curl_multi_perform(): %s") % curl_multi_strerror(mc));
|
throw nix::Error("unexpected error from curl_multi_perform(): %s", curl_multi_strerror(mc));
|
||||||
|
|
||||||
/* Set the promises of any finished requests. */
|
/* Set the promises of any finished requests. */
|
||||||
CURLMsg * msg;
|
CURLMsg * msg;
|
||||||
|
@ -547,7 +556,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
vomit("download thread waiting for %d ms", sleepTimeMs);
|
vomit("download thread waiting for %d ms", sleepTimeMs);
|
||||||
mc = curl_multi_wait(curlm, extraFDs, 1, sleepTimeMs, &numfds);
|
mc = curl_multi_wait(curlm, extraFDs, 1, sleepTimeMs, &numfds);
|
||||||
if (mc != CURLM_OK)
|
if (mc != CURLM_OK)
|
||||||
throw nix::Error(format("unexpected error from curl_multi_wait(): %s") % curl_multi_strerror(mc));
|
throw nix::Error("unexpected error from curl_multi_wait(): %s", curl_multi_strerror(mc));
|
||||||
|
|
||||||
nextWakeup = std::chrono::steady_clock::time_point();
|
nextWakeup = std::chrono::steady_clock::time_point();
|
||||||
|
|
||||||
|
@ -599,7 +608,11 @@ struct curlFileTransfer : public FileTransfer
|
||||||
workerThreadMain();
|
workerThreadMain();
|
||||||
} catch (nix::Interrupted & e) {
|
} catch (nix::Interrupted & e) {
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
printError("unexpected error in download thread: %s", e.what());
|
logError({
|
||||||
|
.name = "File transfer",
|
||||||
|
.hint = hintfmt("unexpected error in download thread: %s",
|
||||||
|
e.what())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -103,8 +103,9 @@ class FileTransferError : public Error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FileTransfer::Error error;
|
FileTransfer::Error error;
|
||||||
FileTransferError(FileTransfer::Error error, const FormatOrString & fs)
|
template<typename... Args>
|
||||||
: Error(fs), error(error)
|
FileTransferError(FileTransfer::Error error, const Args & ... args)
|
||||||
|
: Error(args...), error(error)
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -38,10 +38,10 @@ AutoCloseFD LocalStore::openGCLock(LockType lockType)
|
||||||
|
|
||||||
AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
|
AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
|
||||||
if (!fdGCLock)
|
if (!fdGCLock)
|
||||||
throw SysError(format("opening global GC lock '%1%'") % fnGCLock);
|
throw SysError("opening global GC lock '%1%'", fnGCLock);
|
||||||
|
|
||||||
if (!lockFile(fdGCLock.get(), lockType, false)) {
|
if (!lockFile(fdGCLock.get(), lockType, false)) {
|
||||||
printError(format("waiting for the big garbage collector lock..."));
|
printInfo("waiting for the big garbage collector lock...");
|
||||||
lockFile(fdGCLock.get(), lockType, true);
|
lockFile(fdGCLock.get(), lockType, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +65,8 @@ static void makeSymlink(const Path & link, const Path & target)
|
||||||
|
|
||||||
/* Atomically replace the old one. */
|
/* Atomically replace the old one. */
|
||||||
if (rename(tempLink.c_str(), link.c_str()) == -1)
|
if (rename(tempLink.c_str(), link.c_str()) == -1)
|
||||||
throw SysError(format("cannot rename '%1%' to '%2%'")
|
throw SysError("cannot rename '%1%' to '%2%'",
|
||||||
% tempLink % link);
|
tempLink , link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ void LocalStore::syncWithGC()
|
||||||
|
|
||||||
void LocalStore::addIndirectRoot(const Path & path)
|
void LocalStore::addIndirectRoot(const Path & path)
|
||||||
{
|
{
|
||||||
string hash = hashString(HashType::SHA1, path).to_string(Base::Base32, false);
|
string hash = hashString(htSHA1, path).to_string(Base32, false);
|
||||||
Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
|
Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
|
||||||
% stateDir % gcRootsDir % hash).str());
|
% stateDir % gcRootsDir % hash).str());
|
||||||
makeSymlink(realRoot, path);
|
makeSymlink(realRoot, path);
|
||||||
|
@ -91,15 +91,15 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath,
|
||||||
Path gcRoot(canonPath(_gcRoot));
|
Path gcRoot(canonPath(_gcRoot));
|
||||||
|
|
||||||
if (isInStore(gcRoot))
|
if (isInStore(gcRoot))
|
||||||
throw Error(format(
|
throw Error(
|
||||||
"creating a garbage collector root (%1%) in the Nix store is forbidden "
|
"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) {
|
if (indirect) {
|
||||||
/* Don't clobber the link if it already exists and doesn't
|
/* Don't clobber the link if it already exists and doesn't
|
||||||
point to the Nix store. */
|
point to the Nix store. */
|
||||||
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
|
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
|
||||||
throw Error(format("cannot create symlink '%1%'; already exists") % gcRoot);
|
throw Error("cannot create symlink '%1%'; already exists", gcRoot);
|
||||||
makeSymlink(gcRoot, printStorePath(storePath));
|
makeSymlink(gcRoot, printStorePath(storePath));
|
||||||
addIndirectRoot(gcRoot);
|
addIndirectRoot(gcRoot);
|
||||||
}
|
}
|
||||||
|
@ -109,10 +109,10 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath,
|
||||||
Path rootsDir = canonPath((format("%1%/%2%") % stateDir % gcRootsDir).str());
|
Path rootsDir = canonPath((format("%1%/%2%") % stateDir % gcRootsDir).str());
|
||||||
|
|
||||||
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
|
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
|
||||||
throw Error(format(
|
throw Error(
|
||||||
"path '%1%' is not a valid garbage collector root; "
|
"path '%1%' is not a valid garbage collector root; "
|
||||||
"it's not in the directory '%2%'")
|
"it's not in the directory '%2%'",
|
||||||
% gcRoot % rootsDir);
|
gcRoot, rootsDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baseNameOf(gcRoot) == std::string(storePath.to_string()))
|
if (baseNameOf(gcRoot) == std::string(storePath.to_string()))
|
||||||
|
@ -128,11 +128,13 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath,
|
||||||
gcroots directory. */
|
gcroots directory. */
|
||||||
if (settings.checkRootReachability) {
|
if (settings.checkRootReachability) {
|
||||||
auto roots = findRoots(false);
|
auto roots = findRoots(false);
|
||||||
if (roots[storePath.clone()].count(gcRoot) == 0)
|
if (roots[storePath].count(gcRoot) == 0)
|
||||||
printError(
|
logWarning({
|
||||||
"warning: '%1%' is not in a directory where the garbage collector looks for roots; "
|
.name = "GC root",
|
||||||
"therefore, '%2%' might be removed by the garbage collector",
|
.hint = hintfmt("warning: '%1%' is not in a directory where the garbage collector looks for roots; "
|
||||||
gcRoot, printStorePath(storePath));
|
"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
|
/* Grab the global GC root, causing us to block while a GC is in
|
||||||
|
@ -170,7 +172,7 @@ void LocalStore::addTempRoot(const StorePath & path)
|
||||||
way. */
|
way. */
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstat(state->fdTempRoots.get(), &st) == -1)
|
if (fstat(state->fdTempRoots.get(), &st) == -1)
|
||||||
throw SysError(format("statting '%1%'") % fnTempRoots);
|
throw SysError("statting '%1%'", fnTempRoots);
|
||||||
if (st.st_size == 0) break;
|
if (st.st_size == 0) break;
|
||||||
|
|
||||||
/* The garbage collector deleted this file before we could
|
/* The garbage collector deleted this file before we could
|
||||||
|
@ -216,7 +218,7 @@ void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
|
||||||
if (!*fd) {
|
if (!*fd) {
|
||||||
/* It's okay if the file has disappeared. */
|
/* It's okay if the file has disappeared. */
|
||||||
if (errno == ENOENT) continue;
|
if (errno == ENOENT) continue;
|
||||||
throw SysError(format("opening temporary roots file '%1%'") % path);
|
throw SysError("opening temporary roots file '%1%'", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This should work, but doesn't, for some reason. */
|
/* This should work, but doesn't, for some reason. */
|
||||||
|
@ -227,7 +229,7 @@ void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
|
||||||
only succeed if the owning process has died. In that case
|
only succeed if the owning process has died. In that case
|
||||||
we don't care about its temporary roots. */
|
we don't care about its temporary roots. */
|
||||||
if (lockFile(fd->get(), ltWrite, false)) {
|
if (lockFile(fd->get(), ltWrite, false)) {
|
||||||
printError(format("removing stale temporary roots file '%1%'") % path);
|
printInfo("removing stale temporary roots file '%1%'", path);
|
||||||
unlink(path.c_str());
|
unlink(path.c_str());
|
||||||
writeFull(fd->get(), "d");
|
writeFull(fd->get(), "d");
|
||||||
continue;
|
continue;
|
||||||
|
@ -403,7 +405,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||||
if (!fdDir) {
|
if (!fdDir) {
|
||||||
if (errno == ENOENT || errno == EACCES)
|
if (errno == ENOENT || errno == EACCES)
|
||||||
continue;
|
continue;
|
||||||
throw SysError(format("opening %1%") % fdStr);
|
throw SysError("opening %1%", fdStr);
|
||||||
}
|
}
|
||||||
struct dirent * fd_ent;
|
struct dirent * fd_ent;
|
||||||
while (errno = 0, fd_ent = readdir(fdDir.get())) {
|
while (errno = 0, fd_ent = readdir(fdDir.get())) {
|
||||||
|
@ -413,7 +415,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||||
if (errno) {
|
if (errno) {
|
||||||
if (errno == ESRCH)
|
if (errno == ESRCH)
|
||||||
continue;
|
continue;
|
||||||
throw SysError(format("iterating /proc/%1%/fd") % ent->d_name);
|
throw SysError("iterating /proc/%1%/fd", ent->d_name);
|
||||||
}
|
}
|
||||||
fdDir.reset();
|
fdDir.reset();
|
||||||
|
|
||||||
|
@ -476,9 +478,9 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
||||||
if (!isValidPath(path)) continue;
|
if (!isValidPath(path)) continue;
|
||||||
debug("got additional root '%1%'", pathS);
|
debug("got additional root '%1%'", pathS);
|
||||||
if (censor)
|
if (censor)
|
||||||
roots[path.clone()].insert(censored);
|
roots[path].insert(censored);
|
||||||
else
|
else
|
||||||
roots[path.clone()].insert(links.begin(), links.end());
|
roots[path].insert(links.begin(), links.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,7 +543,7 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(realPath.c_str(), &st)) {
|
if (lstat(realPath.c_str(), &st)) {
|
||||||
if (errno == ENOENT) return;
|
if (errno == ENOENT) return;
|
||||||
throw SysError(format("getting status of %1%") % realPath);
|
throw SysError("getting status of %1%", realPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
printInfo(format("deleting '%1%'") % path);
|
printInfo(format("deleting '%1%'") % path);
|
||||||
|
@ -559,10 +561,10 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||||
// size.
|
// size.
|
||||||
try {
|
try {
|
||||||
if (chmod(realPath.c_str(), st.st_mode | S_IWUSR) == -1)
|
if (chmod(realPath.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||||
throw SysError(format("making '%1%' writable") % realPath);
|
throw SysError("making '%1%' writable", realPath);
|
||||||
Path tmp = trashDir + "/" + std::string(baseNameOf(path));
|
Path tmp = trashDir + "/" + std::string(baseNameOf(path));
|
||||||
if (rename(realPath.c_str(), tmp.c_str()))
|
if (rename(realPath.c_str(), tmp.c_str()))
|
||||||
throw SysError(format("unable to rename '%1%' to '%2%'") % realPath % tmp);
|
throw SysError("unable to rename '%1%' to '%2%'", realPath, tmp);
|
||||||
state.bytesInvalidated += size;
|
state.bytesInvalidated += size;
|
||||||
} catch (SysError & e) {
|
} catch (SysError & e) {
|
||||||
if (e.errNo == ENOSPC) {
|
if (e.errNo == ENOSPC) {
|
||||||
|
@ -590,11 +592,11 @@ bool LocalStore::canReachRoot(GCState & state, StorePathSet & visited, const Sto
|
||||||
|
|
||||||
if (state.roots.count(path)) {
|
if (state.roots.count(path)) {
|
||||||
debug("cannot delete '%1%' because it's a root", printStorePath(path));
|
debug("cannot delete '%1%' because it's a root", printStorePath(path));
|
||||||
state.alive.insert(path.clone());
|
state.alive.insert(path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
visited.insert(path.clone());
|
visited.insert(path);
|
||||||
|
|
||||||
if (!isValidPath(path)) return false;
|
if (!isValidPath(path)) return false;
|
||||||
|
|
||||||
|
@ -608,7 +610,7 @@ bool LocalStore::canReachRoot(GCState & state, StorePathSet & visited, const Sto
|
||||||
if (state.gcKeepDerivations && path.isDerivation()) {
|
if (state.gcKeepDerivations && path.isDerivation()) {
|
||||||
for (auto & i : queryDerivationOutputs(path))
|
for (auto & i : queryDerivationOutputs(path))
|
||||||
if (isValidPath(i) && queryPathInfo(i)->deriver == path)
|
if (isValidPath(i) && queryPathInfo(i)->deriver == path)
|
||||||
incoming.insert(i.clone());
|
incoming.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If keep-outputs is set, then don't delete this path if there
|
/* If keep-outputs is set, then don't delete this path if there
|
||||||
|
@ -616,13 +618,13 @@ bool LocalStore::canReachRoot(GCState & state, StorePathSet & visited, const Sto
|
||||||
if (state.gcKeepOutputs) {
|
if (state.gcKeepOutputs) {
|
||||||
auto derivers = queryValidDerivers(path);
|
auto derivers = queryValidDerivers(path);
|
||||||
for (auto & i : derivers)
|
for (auto & i : derivers)
|
||||||
incoming.insert(i.clone());
|
incoming.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & i : incoming)
|
for (auto & i : incoming)
|
||||||
if (i != path)
|
if (i != path)
|
||||||
if (canReachRoot(state, visited, i)) {
|
if (canReachRoot(state, visited, i)) {
|
||||||
state.alive.insert(path.clone());
|
state.alive.insert(path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,7 +639,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||||
auto realPath = realStoreDir + "/" + std::string(baseNameOf(path));
|
auto realPath = realStoreDir + "/" + std::string(baseNameOf(path));
|
||||||
if (realPath == linksDir || realPath == trashDir) return;
|
if (realPath == linksDir || realPath == trashDir) return;
|
||||||
|
|
||||||
//Activity act(*logger, Verbosity::Debug, format("considering whether to delete '%1%'") % path);
|
//Activity act(*logger, lvlDebug, format("considering whether to delete '%1%'") % path);
|
||||||
|
|
||||||
auto storePath = maybeParseStorePath(path);
|
auto storePath = maybeParseStorePath(path);
|
||||||
|
|
||||||
|
@ -666,7 +668,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||||
‘nix-store --delete’ doesn't have the unexpected effect of
|
‘nix-store --delete’ doesn't have the unexpected effect of
|
||||||
recursing into derivations and outputs. */
|
recursing into derivations and outputs. */
|
||||||
for (auto & i : visited)
|
for (auto & i : visited)
|
||||||
state.dead.insert(i.clone());
|
state.dead.insert(i);
|
||||||
if (state.shouldDelete)
|
if (state.shouldDelete)
|
||||||
deletePathRecursive(state, path);
|
deletePathRecursive(state, path);
|
||||||
}
|
}
|
||||||
|
@ -681,7 +683,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||||
void LocalStore::removeUnusedLinks(const GCState & state)
|
void LocalStore::removeUnusedLinks(const GCState & state)
|
||||||
{
|
{
|
||||||
AutoCloseDir dir(opendir(linksDir.c_str()));
|
AutoCloseDir dir(opendir(linksDir.c_str()));
|
||||||
if (!dir) throw SysError(format("opening directory '%1%'") % linksDir);
|
if (!dir) throw SysError("opening directory '%1%'", linksDir);
|
||||||
|
|
||||||
long long actualSize = 0, unsharedSize = 0;
|
long long actualSize = 0, unsharedSize = 0;
|
||||||
|
|
||||||
|
@ -694,7 +696,7 @@ void LocalStore::removeUnusedLinks(const GCState & state)
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st) == -1)
|
if (lstat(path.c_str(), &st) == -1)
|
||||||
throw SysError(format("statting '%1%'") % path);
|
throw SysError("statting '%1%'", path);
|
||||||
|
|
||||||
if (st.st_nlink != 1) {
|
if (st.st_nlink != 1) {
|
||||||
actualSize += st.st_size;
|
actualSize += st.st_size;
|
||||||
|
@ -702,17 +704,17 @@ void LocalStore::removeUnusedLinks(const GCState & state)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
printMsg(Verbosity::Talkative, format("deleting unused link '%1%'") % path);
|
printMsg(lvlTalkative, format("deleting unused link '%1%'") % path);
|
||||||
|
|
||||||
if (unlink(path.c_str()) == -1)
|
if (unlink(path.c_str()) == -1)
|
||||||
throw SysError(format("deleting '%1%'") % path);
|
throw SysError("deleting '%1%'", path);
|
||||||
|
|
||||||
state.results.bytesFreed += st.st_size;
|
state.results.bytesFreed += st.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(linksDir.c_str(), &st) == -1)
|
if (stat(linksDir.c_str(), &st) == -1)
|
||||||
throw SysError(format("statting '%1%'") % linksDir);
|
throw SysError("statting '%1%'", linksDir);
|
||||||
long long overhead = st.st_blocks * 512ULL;
|
long long overhead = st.st_blocks * 512ULL;
|
||||||
|
|
||||||
printInfo(format("note: currently hard linking saves %.2f MiB")
|
printInfo(format("note: currently hard linking saves %.2f MiB")
|
||||||
|
@ -747,12 +749,12 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
|
|
||||||
/* Find the roots. Since we've grabbed the GC lock, the set of
|
/* Find the roots. Since we've grabbed the GC lock, the set of
|
||||||
permanent roots cannot increase now. */
|
permanent roots cannot increase now. */
|
||||||
printError("finding garbage collector roots...");
|
printInfo("finding garbage collector roots...");
|
||||||
Roots rootMap;
|
Roots rootMap;
|
||||||
if (!options.ignoreLiveness)
|
if (!options.ignoreLiveness)
|
||||||
findRootsNoTemp(rootMap, true);
|
findRootsNoTemp(rootMap, true);
|
||||||
|
|
||||||
for (auto & i : rootMap) state.roots.insert(i.first.clone());
|
for (auto & i : rootMap) state.roots.insert(i.first);
|
||||||
|
|
||||||
/* Read the temporary roots. This acquires read locks on all
|
/* Read the temporary roots. This acquires read locks on all
|
||||||
per-process temporary root files. So after this point no paths
|
per-process temporary root files. So after this point no paths
|
||||||
|
@ -761,8 +763,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
Roots tempRoots;
|
Roots tempRoots;
|
||||||
findTempRoots(fds, tempRoots, true);
|
findTempRoots(fds, tempRoots, true);
|
||||||
for (auto & root : tempRoots) {
|
for (auto & root : tempRoots) {
|
||||||
state.tempRoots.insert(root.first.clone());
|
state.tempRoots.insert(root.first);
|
||||||
state.roots.insert(root.first.clone());
|
state.roots.insert(root.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After this point the set of roots or temporary roots cannot
|
/* After this point the set of roots or temporary roots cannot
|
||||||
|
@ -799,14 +801,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
} else if (options.maxFreed > 0) {
|
} else if (options.maxFreed > 0) {
|
||||||
|
|
||||||
if (state.shouldDelete)
|
if (state.shouldDelete)
|
||||||
printError("deleting garbage...");
|
printInfo("deleting garbage...");
|
||||||
else
|
else
|
||||||
printError("determining live/dead paths...");
|
printInfo("determining live/dead paths...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
AutoCloseDir dir(opendir(realStoreDir.c_str()));
|
AutoCloseDir dir(opendir(realStoreDir.c_str()));
|
||||||
if (!dir) throw SysError(format("opening directory '%1%'") % realStoreDir);
|
if (!dir) throw SysError("opening directory '%1%'", realStoreDir);
|
||||||
|
|
||||||
/* Read the store and immediately delete all paths that
|
/* Read the store and immediately delete all paths that
|
||||||
aren't valid. When using --max-freed etc., deleting
|
aren't valid. When using --max-freed etc., deleting
|
||||||
|
@ -868,7 +870,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
|
|
||||||
/* Clean up the links directory. */
|
/* Clean up the links directory. */
|
||||||
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
|
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
|
||||||
printError("deleting unused links...");
|
printInfo("deleting unused links...");
|
||||||
removeUnusedLinks(state);
|
removeUnusedLinks(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -271,7 +271,7 @@ public:
|
||||||
"listed in 'trusted-public-keys'."};
|
"listed in 'trusted-public-keys'."};
|
||||||
|
|
||||||
Setting<StringSet> extraPlatforms{this,
|
Setting<StringSet> extraPlatforms{this,
|
||||||
std::string{SYSTEM} == "x86_64-linux" ? StringSet{"i686-linux"} : StringSet{},
|
std::string{SYSTEM} == "x86_64-linux" && !isWSL1() ? StringSet{"i686-linux"} : StringSet{},
|
||||||
"extra-platforms",
|
"extra-platforms",
|
||||||
"Additional platforms that can be built on the local system. "
|
"Additional platforms that can be built on the local system. "
|
||||||
"These may be supported natively (e.g. armv7 on some aarch64 CPUs "
|
"These may be supported natively (e.g. armv7 on some aarch64 CPUs "
|
||||||
|
|
|
@ -139,7 +139,7 @@ struct LegacySSHStore : public Store
|
||||||
<< cmdAddToStoreNar
|
<< cmdAddToStoreNar
|
||||||
<< printStorePath(info.path)
|
<< printStorePath(info.path)
|
||||||
<< (info.deriver ? printStorePath(*info.deriver) : "")
|
<< (info.deriver ? printStorePath(*info.deriver) : "")
|
||||||
<< info.narHash.to_string(Base::Base16, false);
|
<< info.narHash.to_string(Base16, false);
|
||||||
writeStorePaths(*this, conn->to, info.references);
|
writeStorePaths(*this, conn->to, info.references);
|
||||||
conn->to
|
conn->to
|
||||||
<< info.registrationTime
|
<< info.registrationTime
|
||||||
|
@ -256,7 +256,7 @@ struct LegacySSHStore : public Store
|
||||||
conn->to.flush();
|
conn->to.flush();
|
||||||
|
|
||||||
for (auto & i : readStorePaths<StorePathSet>(*this, conn->from))
|
for (auto & i : readStorePaths<StorePathSet>(*this, conn->from))
|
||||||
out.insert(i.clone());
|
out.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePathSet queryValidPaths(const StorePathSet & paths,
|
StorePathSet queryValidPaths(const StorePathSet & paths,
|
||||||
|
|
|
@ -74,7 +74,7 @@ static void atomicWrite(const Path & path, const std::string & s)
|
||||||
AutoDelete del(tmp, false);
|
AutoDelete del(tmp, false);
|
||||||
writeFile(tmp, s);
|
writeFile(tmp, s);
|
||||||
if (rename(tmp.c_str(), path.c_str()))
|
if (rename(tmp.c_str(), path.c_str()))
|
||||||
throw SysError(format("renaming '%1%' to '%2%'") % tmp % path);
|
throw SysError("renaming '%1%' to '%2%'", tmp, path);
|
||||||
del.cancel();
|
del.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ struct LocalStoreAccessor : public FSAccessor
|
||||||
{
|
{
|
||||||
Path storePath = store->toStorePath(path);
|
Path storePath = store->toStorePath(path);
|
||||||
if (!store->isValidPath(store->parseStorePath(storePath)))
|
if (!store->isValidPath(store->parseStorePath(storePath)))
|
||||||
throw InvalidPath(format("path '%1%' is not a valid store path") % storePath);
|
throw InvalidPath("path '%1%' is not a valid store path", storePath);
|
||||||
return store->getRealStoreDir() + std::string(path, store->storeDir.size());
|
return store->getRealStoreDir() + std::string(path, store->storeDir.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,11 +33,11 @@ struct LocalStoreAccessor : public FSAccessor
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(realPath.c_str(), &st)) {
|
if (lstat(realPath.c_str(), &st)) {
|
||||||
if (errno == ENOENT || errno == ENOTDIR) return {Type::tMissing, 0, false};
|
if (errno == ENOENT || errno == ENOTDIR) return {Type::tMissing, 0, false};
|
||||||
throw SysError(format("getting status of '%1%'") % path);
|
throw SysError("getting status of '%1%'", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode))
|
if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode))
|
||||||
throw Error(format("file '%1%' has unsupported type") % path);
|
throw Error("file '%1%' has unsupported type", path);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
S_ISREG(st.st_mode) ? Type::tRegular :
|
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_)
|
std::shared_ptr<std::string> LocalFSStore::getBuildLog(const StorePath & path_)
|
||||||
{
|
{
|
||||||
auto path = path_.clone();
|
auto path = path_;
|
||||||
|
|
||||||
if (!path.isDerivation()) {
|
if (!path.isDerivation()) {
|
||||||
try {
|
try {
|
||||||
auto info = queryPathInfo(path);
|
auto info = queryPathInfo(path);
|
||||||
if (!info->deriver) return nullptr;
|
if (!info->deriver) return nullptr;
|
||||||
path = info->deriver->clone();
|
path = *info->deriver;
|
||||||
} catch (InvalidPath &) {
|
} catch (InvalidPath &) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,18 +87,22 @@ LocalStore::LocalStore(const Params & params)
|
||||||
|
|
||||||
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
|
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
|
||||||
if (!gr)
|
if (!gr)
|
||||||
printError(format("warning: the group '%1%' specified in 'build-users-group' does not exist")
|
logError({
|
||||||
% settings.buildUsersGroup);
|
.name = "'build-users-group' not found",
|
||||||
|
.hint = hintfmt(
|
||||||
|
"warning: the group '%1%' specified in 'build-users-group' does not exist",
|
||||||
|
settings.buildUsersGroup)
|
||||||
|
});
|
||||||
else {
|
else {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(realStoreDir.c_str(), &st))
|
if (stat(realStoreDir.c_str(), &st))
|
||||||
throw SysError(format("getting attributes of path '%1%'") % realStoreDir);
|
throw SysError("getting attributes of path '%1%'", realStoreDir);
|
||||||
|
|
||||||
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
|
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)
|
if (chown(realStoreDir.c_str(), 0, gr->gr_gid) == -1)
|
||||||
throw SysError(format("changing ownership of path '%1%'") % realStoreDir);
|
throw SysError("changing ownership of path '%1%'", realStoreDir);
|
||||||
if (chmod(realStoreDir.c_str(), perm) == -1)
|
if (chmod(realStoreDir.c_str(), perm) == -1)
|
||||||
throw SysError(format("changing permissions on path '%1%'") % realStoreDir);
|
throw SysError("changing permissions on path '%1%'", realStoreDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,12 +113,12 @@ LocalStore::LocalStore(const Params & params)
|
||||||
struct stat st;
|
struct stat st;
|
||||||
while (path != "/") {
|
while (path != "/") {
|
||||||
if (lstat(path.c_str(), &st))
|
if (lstat(path.c_str(), &st))
|
||||||
throw SysError(format("getting status of '%1%'") % path);
|
throw SysError("getting status of '%1%'", path);
|
||||||
if (S_ISLNK(st.st_mode))
|
if (S_ISLNK(st.st_mode))
|
||||||
throw Error(format(
|
throw Error(
|
||||||
"the path '%1%' is a symlink; "
|
"the path '%1%' is a symlink; "
|
||||||
"this is not allowed for the Nix store and its parent directories")
|
"this is not allowed for the Nix store and its parent directories",
|
||||||
% path);
|
path);
|
||||||
path = dirOf(path);
|
path = dirOf(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +151,7 @@ LocalStore::LocalStore(const Params & params)
|
||||||
globalLock = openLockFile(globalLockPath.c_str(), true);
|
globalLock = openLockFile(globalLockPath.c_str(), true);
|
||||||
|
|
||||||
if (!lockFile(globalLock.get(), ltRead, false)) {
|
if (!lockFile(globalLock.get(), ltRead, false)) {
|
||||||
printError("waiting for the big Nix store lock...");
|
printInfo("waiting for the big Nix store lock...");
|
||||||
lockFile(globalLock.get(), ltRead, true);
|
lockFile(globalLock.get(), ltRead, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,8 +159,8 @@ LocalStore::LocalStore(const Params & params)
|
||||||
upgrade. */
|
upgrade. */
|
||||||
int curSchema = getSchema();
|
int curSchema = getSchema();
|
||||||
if (curSchema > nixSchemaVersion)
|
if (curSchema > nixSchemaVersion)
|
||||||
throw Error(format("current Nix store schema is version %1%, but I only support %2%")
|
throw Error("current Nix store schema is version %1%, but I only support %2%",
|
||||||
% curSchema % nixSchemaVersion);
|
curSchema, nixSchemaVersion);
|
||||||
|
|
||||||
else if (curSchema == 0) { /* new store */
|
else if (curSchema == 0) { /* new store */
|
||||||
curSchema = nixSchemaVersion;
|
curSchema = nixSchemaVersion;
|
||||||
|
@ -178,7 +182,7 @@ LocalStore::LocalStore(const Params & params)
|
||||||
"please upgrade Nix to version 1.11 first.");
|
"please upgrade Nix to version 1.11 first.");
|
||||||
|
|
||||||
if (!lockFile(globalLock.get(), ltWrite, false)) {
|
if (!lockFile(globalLock.get(), ltWrite, false)) {
|
||||||
printError("waiting for exclusive access to the Nix store...");
|
printInfo("waiting for exclusive access to the Nix store...");
|
||||||
lockFile(globalLock.get(), ltWrite, true);
|
lockFile(globalLock.get(), ltWrite, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +260,7 @@ LocalStore::~LocalStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (future.valid()) {
|
if (future.valid()) {
|
||||||
printError("waiting for auto-GC to finish on exit...");
|
printInfo("waiting for auto-GC to finish on exit...");
|
||||||
future.get();
|
future.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +288,7 @@ int LocalStore::getSchema()
|
||||||
if (pathExists(schemaPath)) {
|
if (pathExists(schemaPath)) {
|
||||||
string s = readFile(schemaPath);
|
string s = readFile(schemaPath);
|
||||||
if (!string2Int(s, curSchema))
|
if (!string2Int(s, curSchema))
|
||||||
throw Error(format("'%1%' is corrupt") % schemaPath);
|
throw Error("'%1%' is corrupt", schemaPath);
|
||||||
}
|
}
|
||||||
return curSchema;
|
return curSchema;
|
||||||
}
|
}
|
||||||
|
@ -293,7 +297,7 @@ int LocalStore::getSchema()
|
||||||
void LocalStore::openDB(State & state, bool create)
|
void LocalStore::openDB(State & state, bool create)
|
||||||
{
|
{
|
||||||
if (access(dbDir.c_str(), R_OK | W_OK))
|
if (access(dbDir.c_str(), R_OK | W_OK))
|
||||||
throw SysError(format("Nix database directory '%1%' is not writable") % dbDir);
|
throw SysError("Nix database directory '%1%' is not writable", dbDir);
|
||||||
|
|
||||||
/* Open the Nix database. */
|
/* Open the Nix database. */
|
||||||
string dbPath = dbDir + "/db.sqlite";
|
string dbPath = dbDir + "/db.sqlite";
|
||||||
|
@ -367,7 +371,7 @@ void LocalStore::makeStoreWritable()
|
||||||
throw SysError("setting up a private mount namespace");
|
throw SysError("setting up a private mount namespace");
|
||||||
|
|
||||||
if (mount(0, realStoreDir.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
|
if (mount(0, realStoreDir.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
|
||||||
throw SysError(format("remounting %1% writable") % realStoreDir);
|
throw SysError("remounting %1% writable", realStoreDir);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -388,7 +392,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
|
||||||
| 0444
|
| 0444
|
||||||
| (st.st_mode & S_IXUSR ? 0111 : 0);
|
| (st.st_mode & S_IXUSR ? 0111 : 0);
|
||||||
if (chmod(path.c_str(), mode) == -1)
|
if (chmod(path.c_str(), mode) == -1)
|
||||||
throw SysError(format("changing mode of '%1%' to %2$o") % path % mode);
|
throw SysError("changing mode of '%1%' to %2$o", path, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -406,7 +410,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
|
||||||
#else
|
#else
|
||||||
if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)
|
if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)
|
||||||
#endif
|
#endif
|
||||||
throw SysError(format("changing modification time of '%1%'") % path);
|
throw SysError("changing modification time of '%1%'", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,7 +419,7 @@ void canonicaliseTimestampAndPermissions(const Path & path)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st))
|
if (lstat(path.c_str(), &st))
|
||||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
throw SysError("getting attributes of path '%1%'", path);
|
||||||
canonicaliseTimestampAndPermissions(path, st);
|
canonicaliseTimestampAndPermissions(path, st);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,17 +434,17 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||||
setattrlist() to remove other attributes as well. */
|
setattrlist() to remove other attributes as well. */
|
||||||
if (lchflags(path.c_str(), 0)) {
|
if (lchflags(path.c_str(), 0)) {
|
||||||
if (errno != ENOTSUP)
|
if (errno != ENOTSUP)
|
||||||
throw SysError(format("clearing flags of path '%1%'") % path);
|
throw SysError("clearing flags of path '%1%'", path);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st))
|
if (lstat(path.c_str(), &st))
|
||||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
throw SysError("getting attributes of path '%1%'", path);
|
||||||
|
|
||||||
/* Really make sure that the path is of a supported type. */
|
/* 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)))
|
if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)))
|
||||||
throw Error(format("file '%1%' has an unsupported type") % path);
|
throw Error("file '%1%' has an unsupported type", path);
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
/* Remove extended attributes / ACLs. */
|
/* Remove extended attributes / ACLs. */
|
||||||
|
@ -474,7 +478,7 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||||
if (fromUid != (uid_t) -1 && st.st_uid != fromUid) {
|
if (fromUid != (uid_t) -1 && st.st_uid != fromUid) {
|
||||||
assert(!S_ISDIR(st.st_mode));
|
assert(!S_ISDIR(st.st_mode));
|
||||||
if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end())
|
if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end())
|
||||||
throw BuildError(format("invalid ownership on file '%1%'") % path);
|
throw BuildError("invalid ownership on file '%1%'", path);
|
||||||
mode_t mode = st.st_mode & ~S_IFMT;
|
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));
|
assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore));
|
||||||
return;
|
return;
|
||||||
|
@ -498,8 +502,8 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||||
if (!S_ISLNK(st.st_mode) &&
|
if (!S_ISLNK(st.st_mode) &&
|
||||||
chown(path.c_str(), geteuid(), getegid()) == -1)
|
chown(path.c_str(), geteuid(), getegid()) == -1)
|
||||||
#endif
|
#endif
|
||||||
throw SysError(format("changing owner of '%1%' to %2%")
|
throw SysError("changing owner of '%1%' to %2%",
|
||||||
% path % geteuid());
|
path, geteuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (S_ISDIR(st.st_mode)) {
|
if (S_ISDIR(st.st_mode)) {
|
||||||
|
@ -518,11 +522,11 @@ void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & ino
|
||||||
be a symlink, since we can't change its ownership. */
|
be a symlink, since we can't change its ownership. */
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st))
|
if (lstat(path.c_str(), &st))
|
||||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
throw SysError("getting attributes of path '%1%'", path);
|
||||||
|
|
||||||
if (st.st_uid != geteuid()) {
|
if (st.st_uid != geteuid()) {
|
||||||
assert(S_ISLNK(st.st_mode));
|
assert(S_ISLNK(st.st_mode));
|
||||||
throw Error(format("wrong ownership of top-level store path '%1%'") % path);
|
throw Error("wrong ownership of top-level store path '%1%'", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,7 +586,7 @@ uint64_t LocalStore::addValidPath(State & state,
|
||||||
|
|
||||||
state.stmtRegisterValidPath.use()
|
state.stmtRegisterValidPath.use()
|
||||||
(printStorePath(info.path))
|
(printStorePath(info.path))
|
||||||
(info.narHash.to_string(Base::Base16))
|
(info.narHash.to_string(Base16, true))
|
||||||
(info.registrationTime == 0 ? time(0) : info.registrationTime)
|
(info.registrationTime == 0 ? time(0) : info.registrationTime)
|
||||||
(info.deriver ? printStorePath(*info.deriver) : "", (bool) info.deriver)
|
(info.deriver ? printStorePath(*info.deriver) : "", (bool) info.deriver)
|
||||||
(info.narSize, info.narSize != 0)
|
(info.narSize, info.narSize != 0)
|
||||||
|
@ -597,7 +601,7 @@ uint64_t LocalStore::addValidPath(State & state,
|
||||||
efficiently query whether a path is an output of some
|
efficiently query whether a path is an output of some
|
||||||
derivation. */
|
derivation. */
|
||||||
if (info.path.isDerivation()) {
|
if (info.path.isDerivation()) {
|
||||||
auto drv = readDerivation(*this, realStoreDir + "/" + std::string(info.path.to_string()));
|
auto drv = readDerivation(info.path);
|
||||||
|
|
||||||
/* Verify that the output paths in the derivation are correct
|
/* Verify that the output paths in the derivation are correct
|
||||||
(i.e., follow the scheme for computing output paths from
|
(i.e., follow the scheme for computing output paths from
|
||||||
|
@ -617,7 +621,7 @@ uint64_t LocalStore::addValidPath(State & state,
|
||||||
|
|
||||||
{
|
{
|
||||||
auto state_(Store::state.lock());
|
auto state_(Store::state.lock());
|
||||||
state_->pathInfoCache.upsert(storePathToHash(printStorePath(info.path)),
|
state_->pathInfoCache.upsert(std::string(info.path.hashPart()),
|
||||||
PathInfoCacheValue{ .value = std::make_shared<const ValidPathInfo>(info) });
|
PathInfoCacheValue{ .value = std::make_shared<const ValidPathInfo>(info) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -629,7 +633,7 @@ void LocalStore::queryPathInfoUncached(const StorePath & path,
|
||||||
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
|
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
auto info = std::make_shared<ValidPathInfo>(path.clone());
|
auto info = std::make_shared<ValidPathInfo>(path);
|
||||||
|
|
||||||
callback(retrySQLite<std::shared_ptr<ValidPathInfo>>([&]() {
|
callback(retrySQLite<std::shared_ptr<ValidPathInfo>>([&]() {
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
|
@ -682,7 +686,7 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info)
|
||||||
{
|
{
|
||||||
state.stmtUpdatePathInfo.use()
|
state.stmtUpdatePathInfo.use()
|
||||||
(info.narSize, info.narSize != 0)
|
(info.narSize, info.narSize != 0)
|
||||||
(info.narHash.to_string(Base::Base16))
|
(info.narHash.to_string(Base16, true))
|
||||||
(info.ultimate ? 1 : 0, info.ultimate)
|
(info.ultimate ? 1 : 0, info.ultimate)
|
||||||
(concatStringsSep(" ", info.sigs), !info.sigs.empty())
|
(concatStringsSep(" ", info.sigs), !info.sigs.empty())
|
||||||
(renderContentAddress(info.ca), (bool) info.ca)
|
(renderContentAddress(info.ca), (bool) info.ca)
|
||||||
|
@ -719,7 +723,7 @@ StorePathSet LocalStore::queryValidPaths(const StorePathSet & paths, SubstituteF
|
||||||
{
|
{
|
||||||
StorePathSet res;
|
StorePathSet res;
|
||||||
for (auto & i : paths)
|
for (auto & i : paths)
|
||||||
if (isValidPath(i)) res.insert(i.clone());
|
if (isValidPath(i)) res.insert(i);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -787,26 +791,9 @@ StorePathSet LocalStore::queryDerivationOutputs(const StorePath & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
std::optional<StorePath> LocalStore::queryPathFromHashPart(const std::string & hashPart)
|
||||||
{
|
{
|
||||||
if (hashPart.size() != storePathHashLen) throw Error("invalid hash part");
|
if (hashPart.size() != StorePath::HashLen) throw Error("invalid hash part");
|
||||||
|
|
||||||
Path prefix = storeDir + "/" + hashPart;
|
Path prefix = storeDir + "/" + hashPart;
|
||||||
|
|
||||||
|
@ -831,7 +818,7 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths)
|
||||||
|
|
||||||
StorePathSet remaining;
|
StorePathSet remaining;
|
||||||
for (auto & i : paths)
|
for (auto & i : paths)
|
||||||
remaining.insert(i.clone());
|
remaining.insert(i);
|
||||||
|
|
||||||
StorePathSet res;
|
StorePathSet res;
|
||||||
|
|
||||||
|
@ -845,9 +832,9 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths)
|
||||||
StorePathSet remaining2;
|
StorePathSet remaining2;
|
||||||
for (auto & path : remaining)
|
for (auto & path : remaining)
|
||||||
if (valid.count(path))
|
if (valid.count(path))
|
||||||
res.insert(path.clone());
|
res.insert(path);
|
||||||
else
|
else
|
||||||
remaining2.insert(path.clone());
|
remaining2.insert(path);
|
||||||
|
|
||||||
std::swap(remaining, remaining2);
|
std::swap(remaining, remaining2);
|
||||||
}
|
}
|
||||||
|
@ -869,16 +856,16 @@ void LocalStore::querySubstitutablePathInfos(const StorePathSet & paths,
|
||||||
auto info = sub->queryPathInfo(path);
|
auto info = sub->queryPathInfo(path);
|
||||||
auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
|
auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
|
||||||
std::shared_ptr<const ValidPathInfo>(info));
|
std::shared_ptr<const ValidPathInfo>(info));
|
||||||
infos.insert_or_assign(path.clone(), SubstitutablePathInfo{
|
infos.insert_or_assign(path, SubstitutablePathInfo{
|
||||||
info->deriver ? info->deriver->clone() : std::optional<StorePath>(),
|
info->deriver,
|
||||||
cloneStorePathSet(info->references),
|
info->references,
|
||||||
narInfo ? narInfo->fileSize : 0,
|
narInfo ? narInfo->fileSize : 0,
|
||||||
info->narSize});
|
info->narSize});
|
||||||
} catch (InvalidPath &) {
|
} catch (InvalidPath &) {
|
||||||
} catch (SubstituterDisabled &) {
|
} catch (SubstituterDisabled &) {
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (settings.tryFallback)
|
if (settings.tryFallback)
|
||||||
printError(e.what());
|
logError(e.info());
|
||||||
else
|
else
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -910,12 +897,12 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
StorePathSet paths;
|
StorePathSet paths;
|
||||||
|
|
||||||
for (auto & i : infos) {
|
for (auto & i : infos) {
|
||||||
assert(i.narHash.type == HashType::SHA256);
|
assert(i.narHash.type == htSHA256);
|
||||||
if (isValidPath_(*state, i.path))
|
if (isValidPath_(*state, i.path))
|
||||||
updatePathInfo(*state, i);
|
updatePathInfo(*state, i);
|
||||||
else
|
else
|
||||||
addValidPath(*state, i, false);
|
addValidPath(*state, i, false);
|
||||||
paths.insert(i.path.clone());
|
paths.insert(i.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & i : infos) {
|
for (auto & i : infos) {
|
||||||
|
@ -930,8 +917,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
for (auto & i : infos)
|
for (auto & i : infos)
|
||||||
if (i.path.isDerivation()) {
|
if (i.path.isDerivation()) {
|
||||||
// FIXME: inefficient; we already loaded the derivation in addValidPath().
|
// FIXME: inefficient; we already loaded the derivation in addValidPath().
|
||||||
checkDerivationOutputs(i.path,
|
checkDerivationOutputs(i.path, readDerivation(i.path));
|
||||||
readDerivation(*this, realStoreDir + "/" + std::string(i.path.to_string())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do a topological sort of the paths. This will throw an
|
/* Do a topological sort of the paths. This will throw an
|
||||||
|
@ -958,7 +944,7 @@ void LocalStore::invalidatePath(State & state, const StorePath & path)
|
||||||
|
|
||||||
{
|
{
|
||||||
auto state_(Store::state.lock());
|
auto state_(Store::state.lock());
|
||||||
state_->pathInfoCache.erase(storePathToHash(printStorePath(path)));
|
state_->pathInfoCache.erase(std::string(path.hashPart()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1008,9 +994,9 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
of the NAR. */
|
of the NAR. */
|
||||||
std::unique_ptr<AbstractHashSink> hashSink;
|
std::unique_ptr<AbstractHashSink> hashSink;
|
||||||
if (!info.ca.has_value() || !info.references.count(info.path))
|
if (!info.ca.has_value() || !info.references.count(info.path))
|
||||||
hashSink = std::make_unique<HashSink>(HashType::SHA256);
|
hashSink = std::make_unique<HashSink>(htSHA256);
|
||||||
else
|
else
|
||||||
hashSink = std::make_unique<HashModuloSink>(HashType::SHA256, storePathToHash(printStorePath(info.path)));
|
hashSink = std::make_unique<HashModuloSink>(htSHA256, std::string(info.path.hashPart()));
|
||||||
|
|
||||||
LambdaSource wrapperSource([&](unsigned char * data, size_t len) -> size_t {
|
LambdaSource wrapperSource([&](unsigned char * data, size_t len) -> size_t {
|
||||||
size_t n = source.read(data, len);
|
size_t n = source.read(data, len);
|
||||||
|
@ -1024,7 +1010,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
|
|
||||||
if (hashResult.first != info.narHash)
|
if (hashResult.first != info.narHash)
|
||||||
throw Error("hash mismatch importing path '%s';\n wanted: %s\n got: %s",
|
throw Error("hash mismatch importing path '%s';\n wanted: %s\n got: %s",
|
||||||
printStorePath(info.path), info.narHash.to_string(), hashResult.first.to_string());
|
printStorePath(info.path), info.narHash.to_string(Base32, true), hashResult.first.to_string(Base32, true));
|
||||||
|
|
||||||
if (hashResult.second != info.narSize)
|
if (hashResult.second != info.narSize)
|
||||||
throw Error("size mismatch importing path '%s';\n wanted: %s\n got: %s",
|
throw Error("size mismatch importing path '%s';\n wanted: %s\n got: %s",
|
||||||
|
@ -1083,14 +1069,14 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||||
sha256); otherwise, compute it here. */
|
sha256); otherwise, compute it here. */
|
||||||
HashResult hash;
|
HashResult hash;
|
||||||
if (method == FileIngestionMethod::Recursive) {
|
if (method == FileIngestionMethod::Recursive) {
|
||||||
hash.first = hashAlgo == HashType::SHA256 ? h : hashString(HashType::SHA256, dump);
|
hash.first = hashAlgo == htSHA256 ? h : hashString(htSHA256, dump);
|
||||||
hash.second = dump.size();
|
hash.second = dump.size();
|
||||||
} else
|
} else
|
||||||
hash = hashPath(HashType::SHA256, realPath);
|
hash = hashPath(htSHA256, realPath);
|
||||||
|
|
||||||
optimisePath(realPath); // FIXME: combine with hashPath()
|
optimisePath(realPath); // FIXME: combine with hashPath()
|
||||||
|
|
||||||
ValidPathInfo info(dstPath.clone());
|
ValidPathInfo info(dstPath);
|
||||||
info.narHash = hash.first;
|
info.narHash = hash.first;
|
||||||
info.narSize = hash.second;
|
info.narSize = hash.second;
|
||||||
info.ca = FileSystemHash { method, h };
|
info.ca = FileSystemHash { method, h };
|
||||||
|
@ -1125,7 +1111,7 @@ StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
|
||||||
StorePath LocalStore::addTextToStore(const string & name, const string & s,
|
StorePath LocalStore::addTextToStore(const string & name, const string & s,
|
||||||
const StorePathSet & references, RepairFlag repair)
|
const StorePathSet & references, RepairFlag repair)
|
||||||
{
|
{
|
||||||
auto hash = hashString(HashType::SHA256, s);
|
auto hash = hashString(htSHA256, s);
|
||||||
auto dstPath = makeTextPath(name, hash, references);
|
auto dstPath = makeTextPath(name, hash, references);
|
||||||
|
|
||||||
addTempRoot(dstPath);
|
addTempRoot(dstPath);
|
||||||
|
@ -1149,14 +1135,14 @@ StorePath LocalStore::addTextToStore(const string & name, const string & s,
|
||||||
|
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
dumpString(s, sink);
|
dumpString(s, sink);
|
||||||
auto narHash = hashString(HashType::SHA256, *sink.s);
|
auto narHash = hashString(htSHA256, *sink.s);
|
||||||
|
|
||||||
optimisePath(realPath);
|
optimisePath(realPath);
|
||||||
|
|
||||||
ValidPathInfo info(dstPath.clone());
|
ValidPathInfo info(dstPath);
|
||||||
info.narHash = narHash;
|
info.narHash = narHash;
|
||||||
info.narSize = sink.s->size();
|
info.narSize = sink.s->size();
|
||||||
info.references = cloneStorePathSet(references);
|
info.references = references;
|
||||||
info.ca = TextHash { .hash = hash };
|
info.ca = TextHash { .hash = hash };
|
||||||
registerValidPath(info);
|
registerValidPath(info);
|
||||||
}
|
}
|
||||||
|
@ -1207,7 +1193,7 @@ void LocalStore::invalidatePathChecked(const StorePath & path)
|
||||||
|
|
||||||
bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||||
{
|
{
|
||||||
printError(format("reading the Nix store..."));
|
printInfo(format("reading the Nix store..."));
|
||||||
|
|
||||||
bool errors = false;
|
bool errors = false;
|
||||||
|
|
||||||
|
@ -1235,16 +1221,19 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||||
printInfo("checking link hashes...");
|
printInfo("checking link hashes...");
|
||||||
|
|
||||||
for (auto & link : readDirectory(linksDir)) {
|
for (auto & link : readDirectory(linksDir)) {
|
||||||
printMsg(Verbosity::Talkative, "checking contents of '%s'", link.name);
|
printMsg(lvlTalkative, "checking contents of '%s'", link.name);
|
||||||
Path linkPath = linksDir + "/" + link.name;
|
Path linkPath = linksDir + "/" + link.name;
|
||||||
string hash = hashPath(HashType::SHA256, linkPath).first.to_string(Base::Base32, false);
|
string hash = hashPath(htSHA256, linkPath).first.to_string(Base32, false);
|
||||||
if (hash != link.name) {
|
if (hash != link.name) {
|
||||||
printError(
|
logError({
|
||||||
"link '%s' was modified! expected hash '%s', got '%s'",
|
.name = "Invalid hash",
|
||||||
linkPath, link.name, hash);
|
.hint = hintfmt(
|
||||||
|
"link '%s' was modified! expected hash '%s', got '%s'",
|
||||||
|
linkPath, link.name, hash)
|
||||||
|
});
|
||||||
if (repair) {
|
if (repair) {
|
||||||
if (unlink(linkPath.c_str()) == 0)
|
if (unlink(linkPath.c_str()) == 0)
|
||||||
printError("removed link '%s'", linkPath);
|
printInfo("removed link '%s'", linkPath);
|
||||||
else
|
else
|
||||||
throw SysError("removing corrupt link '%s'", linkPath);
|
throw SysError("removing corrupt link '%s'", linkPath);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1255,27 +1244,30 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||||
|
|
||||||
printInfo("checking store hashes...");
|
printInfo("checking store hashes...");
|
||||||
|
|
||||||
Hash nullHash(HashType::SHA256);
|
Hash nullHash(htSHA256);
|
||||||
|
|
||||||
for (auto & i : validPaths) {
|
for (auto & i : validPaths) {
|
||||||
try {
|
try {
|
||||||
auto info = std::const_pointer_cast<ValidPathInfo>(std::shared_ptr<const ValidPathInfo>(queryPathInfo(i)));
|
auto info = std::const_pointer_cast<ValidPathInfo>(std::shared_ptr<const ValidPathInfo>(queryPathInfo(i)));
|
||||||
|
|
||||||
/* Check the content hash (optionally - slow). */
|
/* Check the content hash (optionally - slow). */
|
||||||
printMsg(Verbosity::Talkative, "checking contents of '%s'", printStorePath(i));
|
printMsg(lvlTalkative, "checking contents of '%s'", printStorePath(i));
|
||||||
|
|
||||||
std::unique_ptr<AbstractHashSink> hashSink;
|
std::unique_ptr<AbstractHashSink> hashSink;
|
||||||
if (!info->ca || !info->references.count(info->path))
|
if (!info->ca || !info->references.count(info->path))
|
||||||
hashSink = std::make_unique<HashSink>(*info->narHash.type);
|
hashSink = std::make_unique<HashSink>(*info->narHash.type);
|
||||||
else
|
else
|
||||||
hashSink = std::make_unique<HashModuloSink>(*info->narHash.type, storePathToHash(printStorePath(info->path)));
|
hashSink = std::make_unique<HashModuloSink>(*info->narHash.type, std::string(info->path.hashPart()));
|
||||||
|
|
||||||
dumpPath(Store::toRealPath(i), *hashSink);
|
dumpPath(Store::toRealPath(i), *hashSink);
|
||||||
auto current = hashSink->finish();
|
auto current = hashSink->finish();
|
||||||
|
|
||||||
if (info->narHash != nullHash && info->narHash != current.first) {
|
if (info->narHash != nullHash && info->narHash != current.first) {
|
||||||
printError("path '%s' was modified! expected hash '%s', got '%s'",
|
logError({
|
||||||
printStorePath(i), info->narHash.to_string(), current.first.to_string());
|
.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))
|
||||||
|
});
|
||||||
if (repair) repairPath(i); else errors = true;
|
if (repair) repairPath(i); else errors = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -1283,14 +1275,14 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||||
|
|
||||||
/* Fill in missing hashes. */
|
/* Fill in missing hashes. */
|
||||||
if (info->narHash == nullHash) {
|
if (info->narHash == nullHash) {
|
||||||
printError("fixing missing hash on '%s'", printStorePath(i));
|
printInfo("fixing missing hash on '%s'", printStorePath(i));
|
||||||
info->narHash = current.first;
|
info->narHash = current.first;
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in missing narSize fields (from old stores). */
|
/* Fill in missing narSize fields (from old stores). */
|
||||||
if (info->narSize == 0) {
|
if (info->narSize == 0) {
|
||||||
printError("updating size field on '%s' to %s", printStorePath(i), current.second);
|
printInfo("updating size field on '%s' to %s", printStorePath(i), current.second);
|
||||||
info->narSize = current.second;
|
info->narSize = current.second;
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
|
@ -1306,7 +1298,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||||
/* It's possible that the path got GC'ed, so ignore
|
/* It's possible that the path got GC'ed, so ignore
|
||||||
errors on invalid paths. */
|
errors on invalid paths. */
|
||||||
if (isValidPath(i))
|
if (isValidPath(i))
|
||||||
printError("error: %s", e.msg());
|
logError(e.info());
|
||||||
else
|
else
|
||||||
warn(e.msg());
|
warn(e.msg());
|
||||||
errors = true;
|
errors = true;
|
||||||
|
@ -1326,7 +1318,10 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store,
|
||||||
if (!done.insert(pathS).second) return;
|
if (!done.insert(pathS).second) return;
|
||||||
|
|
||||||
if (!isStorePath(pathS)) {
|
if (!isStorePath(pathS)) {
|
||||||
printError("path '%s' is not in the Nix store", pathS);
|
logError({
|
||||||
|
.name = "Nix path not found",
|
||||||
|
.hint = hintfmt("path '%s' is not in the Nix store", pathS)
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1345,16 +1340,19 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canInvalidate) {
|
if (canInvalidate) {
|
||||||
printError("path '%s' disappeared, removing from database...", pathS);
|
printInfo("path '%s' disappeared, removing from database...", pathS);
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
invalidatePath(*state, path);
|
invalidatePath(*state, path);
|
||||||
} else {
|
} else {
|
||||||
printError("path '%s' disappeared, but it still has valid referrers!", pathS);
|
logError({
|
||||||
|
.name = "Missing path with referrers",
|
||||||
|
.hint = hintfmt("path '%s' disappeared, but it still has valid referrers!", pathS)
|
||||||
|
});
|
||||||
if (repair)
|
if (repair)
|
||||||
try {
|
try {
|
||||||
repairPath(path);
|
repairPath(path);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
warn(e.msg());
|
logWarning(e.info());
|
||||||
errors = true;
|
errors = true;
|
||||||
}
|
}
|
||||||
else errors = true;
|
else errors = true;
|
||||||
|
@ -1394,7 +1392,7 @@ static void makeMutable(const Path & path)
|
||||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
if (errno == ELOOP) return; // it's a symlink
|
if (errno == ELOOP) return; // it's a symlink
|
||||||
throw SysError(format("opening file '%1%'") % path);
|
throw SysError("opening file '%1%'", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int flags = 0, old;
|
unsigned int flags = 0, old;
|
||||||
|
@ -1412,7 +1410,7 @@ static void makeMutable(const Path & path)
|
||||||
void LocalStore::upgradeStore7()
|
void LocalStore::upgradeStore7()
|
||||||
{
|
{
|
||||||
if (getuid() != 0) return;
|
if (getuid() != 0) return;
|
||||||
printError("removing immutable bits from the Nix store (this may take a while)...");
|
printInfo("removing immutable bits from the Nix store (this may take a while)...");
|
||||||
makeMutable(realStoreDir);
|
makeMutable(realStoreDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,8 +135,6 @@ public:
|
||||||
|
|
||||||
StorePathSet queryDerivationOutputs(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;
|
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override;
|
||||||
|
|
||||||
StorePathSet querySubstitutablePaths(const StorePathSet & paths) override;
|
StorePathSet querySubstitutablePaths(const StorePathSet & paths) override;
|
||||||
|
@ -157,7 +155,7 @@ public:
|
||||||
true) or simply the contents of a regular file (if recursive ==
|
true) or simply the contents of a regular file (if recursive ==
|
||||||
false). */
|
false). */
|
||||||
StorePath addToStoreFromDump(const string & dump, const string & name,
|
StorePath addToStoreFromDump(const string & dump, const string & name,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = HashType::SHA256, RepairFlag repair = NoRepair) override;
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override;
|
||||||
|
|
||||||
StorePath addTextToStore(const string & name, const string & s,
|
StorePath addTextToStore(const string & name, const string & s,
|
||||||
const StorePathSet & references, RepairFlag repair) override;
|
const StorePathSet & references, RepairFlag repair) override;
|
||||||
|
|
|
@ -6,7 +6,7 @@ libstore_DIR := $(d)
|
||||||
|
|
||||||
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc)
|
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc)
|
||||||
|
|
||||||
libstore_LIBS = libutil libnixrust
|
libstore_LIBS = libutil
|
||||||
|
|
||||||
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
|
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
|
||||||
ifneq ($(OS), FreeBSD)
|
ifneq ($(OS), FreeBSD)
|
||||||
|
|
|
@ -103,7 +103,7 @@ void Store::computeFSClosure(const StorePath & startPath,
|
||||||
StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers)
|
StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers)
|
||||||
{
|
{
|
||||||
StorePathSet paths;
|
StorePathSet paths;
|
||||||
paths.insert(startPath.clone());
|
paths.insert(startPath);
|
||||||
computeFSClosure(paths, paths_, flipDirection, includeOutputs, includeDerivers);
|
computeFSClosure(paths, paths_, flipDirection, includeOutputs, includeDerivers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||||
StorePathSet & willBuild_, StorePathSet & willSubstitute_, StorePathSet & unknown_,
|
StorePathSet & willBuild_, StorePathSet & willSubstitute_, StorePathSet & unknown_,
|
||||||
unsigned long long & downloadSize_, unsigned long long & narSize_)
|
unsigned long long & downloadSize_, unsigned long long & narSize_)
|
||||||
{
|
{
|
||||||
Activity act(*logger, Verbosity::Debug, ActivityType::Unknown, "querying info about missing paths");
|
Activity act(*logger, lvlDebug, actUnknown, "querying info about missing paths");
|
||||||
|
|
||||||
downloadSize_ = narSize_ = 0;
|
downloadSize_ = narSize_ = 0;
|
||||||
|
|
||||||
|
@ -141,11 +141,11 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||||
auto mustBuildDrv = [&](const StorePath & drvPath, const Derivation & drv) {
|
auto mustBuildDrv = [&](const StorePath & drvPath, const Derivation & drv) {
|
||||||
{
|
{
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
state->willBuild.insert(drvPath.clone());
|
state->willBuild.insert(drvPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & i : drv.inputDrvs)
|
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 = [&](
|
auto checkOutput = [&](
|
||||||
|
@ -157,9 +157,7 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||||
auto outPath = parseStorePath(outPathS);
|
auto outPath = parseStorePath(outPathS);
|
||||||
|
|
||||||
SubstitutablePathInfos infos;
|
SubstitutablePathInfos infos;
|
||||||
StorePathSet paths; // FIXME
|
querySubstitutablePathInfos({outPath}, infos);
|
||||||
paths.insert(outPath.clone());
|
|
||||||
querySubstitutablePathInfos(paths, infos);
|
|
||||||
|
|
||||||
if (infos.empty()) {
|
if (infos.empty()) {
|
||||||
drvState_->lock()->done = true;
|
drvState_->lock()->done = true;
|
||||||
|
@ -170,10 +168,10 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||||
if (drvState->done) return;
|
if (drvState->done) return;
|
||||||
assert(drvState->left);
|
assert(drvState->left);
|
||||||
drvState->left--;
|
drvState->left--;
|
||||||
drvState->outPaths.insert(outPath.clone());
|
drvState->outPaths.insert(outPath);
|
||||||
if (!drvState->left) {
|
if (!drvState->left) {
|
||||||
for (auto & path : drvState->outPaths)
|
for (auto & path : drvState->outPaths)
|
||||||
pool.enqueue(std::bind(doPath, StorePathWithOutputs(path.clone())));
|
pool.enqueue(std::bind(doPath, StorePathWithOutputs { path } ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,12 +188,12 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||||
if (!isValidPath(path.path)) {
|
if (!isValidPath(path.path)) {
|
||||||
// FIXME: we could try to substitute the derivation.
|
// FIXME: we could try to substitute the derivation.
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
state->unknown.insert(path.path.clone());
|
state->unknown.insert(path.path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto drv = make_ref<Derivation>(derivationFromPath(path.path));
|
auto drv = make_ref<Derivation>(derivationFromPath(path.path));
|
||||||
ParsedDerivation parsedDrv(path.path.clone(), *drv);
|
ParsedDerivation parsedDrv(StorePath(path.path), *drv);
|
||||||
|
|
||||||
PathSet invalid;
|
PathSet invalid;
|
||||||
for (auto & j : drv->outputs)
|
for (auto & j : drv->outputs)
|
||||||
|
@ -216,13 +214,11 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||||
if (isValidPath(path.path)) return;
|
if (isValidPath(path.path)) return;
|
||||||
|
|
||||||
SubstitutablePathInfos infos;
|
SubstitutablePathInfos infos;
|
||||||
StorePathSet paths; // FIXME
|
querySubstitutablePathInfos({path.path}, infos);
|
||||||
paths.insert(path.path.clone());
|
|
||||||
querySubstitutablePathInfos(paths, infos);
|
|
||||||
|
|
||||||
if (infos.empty()) {
|
if (infos.empty()) {
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
state->unknown.insert(path.path.clone());
|
state->unknown.insert(path.path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,13 +227,13 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
|
||||||
|
|
||||||
{
|
{
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
state->willSubstitute.insert(path.path.clone());
|
state->willSubstitute.insert(path.path);
|
||||||
state->downloadSize += info->second.downloadSize;
|
state->downloadSize += info->second.downloadSize;
|
||||||
state->narSize += info->second.narSize;
|
state->narSize += info->second.narSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & ref : info->second.references)
|
for (auto & ref : info->second.references)
|
||||||
pool.enqueue(std::bind(doPath, StorePathWithOutputs(ref)));
|
pool.enqueue(std::bind(doPath, StorePathWithOutputs { ref }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -260,12 +256,12 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths)
|
||||||
throw BuildError("cycle detected in the references of '%s' from '%s'",
|
throw BuildError("cycle detected in the references of '%s' from '%s'",
|
||||||
printStorePath(path), printStorePath(*parent));
|
printStorePath(path), printStorePath(*parent));
|
||||||
|
|
||||||
if (!visited.insert(path.clone()).second) return;
|
if (!visited.insert(path).second) return;
|
||||||
parents.insert(path.clone());
|
parents.insert(path);
|
||||||
|
|
||||||
StorePathSet references;
|
StorePathSet references;
|
||||||
try {
|
try {
|
||||||
references = cloneStorePathSet(queryPathInfo(path)->references);
|
references = queryPathInfo(path)->references;
|
||||||
} catch (InvalidPath &) {
|
} catch (InvalidPath &) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +271,7 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths)
|
||||||
if (i != path && paths.count(i))
|
if (i != path && paths.count(i))
|
||||||
dfsVisit(i, &path);
|
dfsVisit(i, &path);
|
||||||
|
|
||||||
sorted.push_back(path.clone());
|
sorted.push_back(path);
|
||||||
parents.erase(path);
|
parents.erase(path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,7 @@ struct NarAccessor : public FSAccessor
|
||||||
auto i = get(path);
|
auto i = get(path);
|
||||||
|
|
||||||
if (i.type != FSAccessor::Type::tDirectory)
|
if (i.type != FSAccessor::Type::tDirectory)
|
||||||
throw Error(format("path '%1%' inside NAR file is not a directory") % path);
|
throw Error("path '%1%' inside NAR file is not a directory", path);
|
||||||
|
|
||||||
StringSet res;
|
StringSet res;
|
||||||
for (auto & child : i.children)
|
for (auto & child : i.children)
|
||||||
|
@ -197,7 +197,7 @@ struct NarAccessor : public FSAccessor
|
||||||
{
|
{
|
||||||
auto i = get(path);
|
auto i = get(path);
|
||||||
if (i.type != FSAccessor::Type::tRegular)
|
if (i.type != FSAccessor::Type::tRegular)
|
||||||
throw Error(format("path '%1%' inside NAR file is not a regular file") % path);
|
throw Error("path '%1%' inside NAR file is not a regular file", path);
|
||||||
|
|
||||||
if (getNarBytes) return getNarBytes(i.start, i.size);
|
if (getNarBytes) return getNarBytes(i.start, i.size);
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ struct NarAccessor : public FSAccessor
|
||||||
{
|
{
|
||||||
auto i = get(path);
|
auto i = get(path);
|
||||||
if (i.type != FSAccessor::Type::tSymlink)
|
if (i.type != FSAccessor::Type::tSymlink)
|
||||||
throw Error(format("path '%1%' inside NAR file is not a symlink") % path);
|
throw Error("path '%1%' inside NAR file is not a symlink", path);
|
||||||
return i.target;
|
return i.target;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -189,7 +189,7 @@ public:
|
||||||
return {oInvalid, 0};
|
return {oInvalid, 0};
|
||||||
|
|
||||||
auto namePart = queryNAR.getStr(1);
|
auto namePart = queryNAR.getStr(1);
|
||||||
auto narInfo = make_ref<NarInfo>(StorePath::fromBaseName(hashPart + "-" + namePart));
|
auto narInfo = make_ref<NarInfo>(StorePath(hashPart + "-" + namePart));
|
||||||
narInfo->url = queryNAR.getStr(2);
|
narInfo->url = queryNAR.getStr(2);
|
||||||
narInfo->compression = queryNAR.getStr(3);
|
narInfo->compression = queryNAR.getStr(3);
|
||||||
if (!queryNAR.isNull(4))
|
if (!queryNAR.isNull(4))
|
||||||
|
@ -198,9 +198,9 @@ public:
|
||||||
narInfo->narHash = Hash(queryNAR.getStr(6));
|
narInfo->narHash = Hash(queryNAR.getStr(6));
|
||||||
narInfo->narSize = queryNAR.getInt(7);
|
narInfo->narSize = queryNAR.getInt(7);
|
||||||
for (auto & r : tokenizeString<Strings>(queryNAR.getStr(8), " "))
|
for (auto & r : tokenizeString<Strings>(queryNAR.getStr(8), " "))
|
||||||
narInfo->references.insert(StorePath::fromBaseName(r));
|
narInfo->references.insert(StorePath(r));
|
||||||
if (!queryNAR.isNull(9))
|
if (!queryNAR.isNull(9))
|
||||||
narInfo->deriver = StorePath::fromBaseName(queryNAR.getStr(9));
|
narInfo->deriver = StorePath(queryNAR.getStr(9));
|
||||||
for (auto & sig : tokenizeString<Strings>(queryNAR.getStr(10), " "))
|
for (auto & sig : tokenizeString<Strings>(queryNAR.getStr(10), " "))
|
||||||
narInfo->sigs.insert(sig);
|
narInfo->sigs.insert(sig);
|
||||||
narInfo->ca = parseContentAddressOpt(queryNAR.getStr(11));
|
narInfo->ca = parseContentAddressOpt(queryNAR.getStr(11));
|
||||||
|
@ -230,9 +230,9 @@ public:
|
||||||
(std::string(info->path.name()))
|
(std::string(info->path.name()))
|
||||||
(narInfo ? narInfo->url : "", narInfo != 0)
|
(narInfo ? narInfo->url : "", narInfo != 0)
|
||||||
(narInfo ? narInfo->compression : "", narInfo != 0)
|
(narInfo ? narInfo->compression : "", narInfo != 0)
|
||||||
(narInfo && narInfo->fileHash ? narInfo->fileHash.to_string() : "", narInfo && narInfo->fileHash)
|
(narInfo && narInfo->fileHash ? narInfo->fileHash.to_string(Base32, true) : "", narInfo && narInfo->fileHash)
|
||||||
(narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize)
|
(narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize)
|
||||||
(info->narHash.to_string())
|
(info->narHash.to_string(Base32, true))
|
||||||
(info->narSize)
|
(info->narSize)
|
||||||
(concatStringsSep(" ", info->shortRefs()))
|
(concatStringsSep(" ", info->shortRefs()))
|
||||||
(info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver)
|
(info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver)
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence)
|
NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence)
|
||||||
: ValidPathInfo(StorePath::dummy) // FIXME: hack
|
: ValidPathInfo(StorePath(StorePath::dummy)) // FIXME: hack
|
||||||
{
|
{
|
||||||
auto corrupt = [&]() {
|
auto corrupt = [&]() {
|
||||||
throw Error(format("NAR info file '%1%' is corrupt") % whence);
|
throw Error("NAR info file '%1%' is corrupt", whence);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto parseHashField = [&](const string & s) {
|
auto parseHashField = [&](const string & s) {
|
||||||
|
@ -56,11 +56,11 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
|
||||||
auto refs = tokenizeString<Strings>(value, " ");
|
auto refs = tokenizeString<Strings>(value, " ");
|
||||||
if (!references.empty()) corrupt();
|
if (!references.empty()) corrupt();
|
||||||
for (auto & r : refs)
|
for (auto & r : refs)
|
||||||
references.insert(StorePath::fromBaseName(r));
|
references.insert(StorePath(r));
|
||||||
}
|
}
|
||||||
else if (name == "Deriver") {
|
else if (name == "Deriver") {
|
||||||
if (value != "unknown-deriver")
|
if (value != "unknown-deriver")
|
||||||
deriver = StorePath::fromBaseName(value);
|
deriver = StorePath(value);
|
||||||
}
|
}
|
||||||
else if (name == "System")
|
else if (name == "System")
|
||||||
system = value;
|
system = value;
|
||||||
|
@ -87,11 +87,11 @@ std::string NarInfo::to_string(const Store & store) const
|
||||||
res += "URL: " + url + "\n";
|
res += "URL: " + url + "\n";
|
||||||
assert(compression != "");
|
assert(compression != "");
|
||||||
res += "Compression: " + compression + "\n";
|
res += "Compression: " + compression + "\n";
|
||||||
assert(fileHash.type == HashType::SHA256);
|
assert(fileHash.type == htSHA256);
|
||||||
res += "FileHash: " + fileHash.to_string(Base::Base32) + "\n";
|
res += "FileHash: " + fileHash.to_string(Base32, true) + "\n";
|
||||||
res += "FileSize: " + std::to_string(fileSize) + "\n";
|
res += "FileSize: " + std::to_string(fileSize) + "\n";
|
||||||
assert(narHash.type == HashType::SHA256);
|
assert(narHash.type == htSHA256);
|
||||||
res += "NarHash: " + narHash.to_string(Base::Base32) + "\n";
|
res += "NarHash: " + narHash.to_string(Base32, true) + "\n";
|
||||||
res += "NarSize: " + std::to_string(narSize) + "\n";
|
res += "NarSize: " + std::to_string(narSize) + "\n";
|
||||||
|
|
||||||
res += "References: " + concatStringsSep(" ", shortRefs()) + "\n";
|
res += "References: " + concatStringsSep(" ", shortRefs()) + "\n";
|
||||||
|
|
|
@ -19,9 +19,9 @@ static void makeWritable(const Path & path)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st))
|
if (lstat(path.c_str(), &st))
|
||||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
throw SysError("getting attributes of path '%1%'", path);
|
||||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||||
throw SysError(format("changing writability of '%1%'") % path);
|
throw SysError("changing writability of '%1%'", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
|
||||||
InodeHash inodeHash;
|
InodeHash inodeHash;
|
||||||
|
|
||||||
AutoCloseDir dir(opendir(linksDir.c_str()));
|
AutoCloseDir dir(opendir(linksDir.c_str()));
|
||||||
if (!dir) throw SysError(format("opening directory '%1%'") % linksDir);
|
if (!dir) throw SysError("opening directory '%1%'", linksDir);
|
||||||
|
|
||||||
struct dirent * dirent;
|
struct dirent * dirent;
|
||||||
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
|
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
|
||||||
|
@ -55,9 +55,9 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
|
||||||
// We don't care if we hit non-hash files, anything goes
|
// We don't care if we hit non-hash files, anything goes
|
||||||
inodeHash.insert(dirent->d_ino);
|
inodeHash.insert(dirent->d_ino);
|
||||||
}
|
}
|
||||||
if (errno) throw SysError(format("reading directory '%1%'") % linksDir);
|
if (errno) throw SysError("reading directory '%1%'", linksDir);
|
||||||
|
|
||||||
printMsg(Verbosity::Talkative, format("loaded %1% hash inodes") % inodeHash.size());
|
printMsg(lvlTalkative, format("loaded %1% hash inodes") % inodeHash.size());
|
||||||
|
|
||||||
return inodeHash;
|
return inodeHash;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
|
||||||
Strings names;
|
Strings names;
|
||||||
|
|
||||||
AutoCloseDir dir(opendir(path.c_str()));
|
AutoCloseDir dir(opendir(path.c_str()));
|
||||||
if (!dir) throw SysError(format("opening directory '%1%'") % path);
|
if (!dir) throw SysError("opening directory '%1%'", path);
|
||||||
|
|
||||||
struct dirent * dirent;
|
struct dirent * dirent;
|
||||||
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
|
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
|
||||||
|
@ -83,7 +83,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
|
||||||
if (name == "." || name == "..") continue;
|
if (name == "." || name == "..") continue;
|
||||||
names.push_back(name);
|
names.push_back(name);
|
||||||
}
|
}
|
||||||
if (errno) throw SysError(format("reading directory '%1%'") % path);
|
if (errno) throw SysError("reading directory '%1%'", path);
|
||||||
|
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st))
|
if (lstat(path.c_str(), &st))
|
||||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
throw SysError("getting attributes of path '%1%'", path);
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
/* HFS/macOS has some undocumented security feature disabling hardlinking for
|
/* HFS/macOS has some undocumented security feature disabling hardlinking for
|
||||||
|
@ -130,7 +130,10 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
NixOS (example: $fontconfig/var/cache being modified). Skip
|
NixOS (example: $fontconfig/var/cache being modified). Skip
|
||||||
those files. FIXME: check the modification time. */
|
those files. FIXME: check the modification time. */
|
||||||
if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
|
if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
|
||||||
printError(format("skipping suspicious writable file '%1%'") % path);
|
logWarning({
|
||||||
|
.name = "Suspicious file",
|
||||||
|
.hint = hintfmt("skipping suspicious writable file '%1%'", path)
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,11 +152,11 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
Also note that if `path' is a symlink, then we're hashing the
|
Also note that if `path' is a symlink, then we're hashing the
|
||||||
contents of the symlink (i.e. the result of readlink()), not
|
contents of the symlink (i.e. the result of readlink()), not
|
||||||
the contents of the target (which may not even exist). */
|
the contents of the target (which may not even exist). */
|
||||||
Hash hash = hashPath(HashType::SHA256, path).first;
|
Hash hash = hashPath(htSHA256, path).first;
|
||||||
debug(format("'%1%' has hash '%2%'") % path % hash.to_string());
|
debug(format("'%1%' has hash '%2%'") % path % hash.to_string(Base32, true));
|
||||||
|
|
||||||
/* Check if this is a known hash. */
|
/* Check if this is a known hash. */
|
||||||
Path linkPath = linksDir + "/" + hash.to_string(Base::Base32, false);
|
Path linkPath = linksDir + "/" + hash.to_string(Base32, false);
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
if (!pathExists(linkPath)) {
|
if (!pathExists(linkPath)) {
|
||||||
|
@ -186,7 +189,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
current file with a hard link to that file. */
|
current file with a hard link to that file. */
|
||||||
struct stat stLink;
|
struct stat stLink;
|
||||||
if (lstat(linkPath.c_str(), &stLink))
|
if (lstat(linkPath.c_str(), &stLink))
|
||||||
throw SysError(format("getting attributes of path '%1%'") % linkPath);
|
throw SysError("getting attributes of path '%1%'", linkPath);
|
||||||
|
|
||||||
if (st.st_ino == stLink.st_ino) {
|
if (st.st_ino == stLink.st_ino) {
|
||||||
debug(format("'%1%' is already linked to '%2%'") % path % linkPath);
|
debug(format("'%1%' is already linked to '%2%'") % path % linkPath);
|
||||||
|
@ -194,12 +197,15 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.st_size != stLink.st_size) {
|
if (st.st_size != stLink.st_size) {
|
||||||
printError(format("removing corrupted link '%1%'") % linkPath);
|
logWarning({
|
||||||
|
.name = "Corrupted link",
|
||||||
|
.hint = hintfmt("removing corrupted link '%1%'", linkPath)
|
||||||
|
});
|
||||||
unlink(linkPath.c_str());
|
unlink(linkPath.c_str());
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
printMsg(Verbosity::Talkative, format("linking '%1%' to '%2%'") % path % linkPath);
|
printMsg(lvlTalkative, format("linking '%1%' to '%2%'") % path % linkPath);
|
||||||
|
|
||||||
/* Make the containing directory writable, but only if it's not
|
/* Make the containing directory writable, but only if it's not
|
||||||
the store itself (we don't want or need to mess with its
|
the store itself (we don't want or need to mess with its
|
||||||
|
@ -229,7 +235,10 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
/* Atomically replace the old file with the new hard link. */
|
/* Atomically replace the old file with the new hard link. */
|
||||||
if (rename(tempLink.c_str(), path.c_str()) == -1) {
|
if (rename(tempLink.c_str(), path.c_str()) == -1) {
|
||||||
if (unlink(tempLink.c_str()) == -1)
|
if (unlink(tempLink.c_str()) == -1)
|
||||||
printError(format("unable to unlink '%1%'") % tempLink);
|
logError({
|
||||||
|
.name = "Unlink error",
|
||||||
|
.hint = hintfmt("unable to unlink '%1%'", tempLink)
|
||||||
|
});
|
||||||
if (errno == EMLINK) {
|
if (errno == EMLINK) {
|
||||||
/* Some filesystems generate too many links on the rename,
|
/* Some filesystems generate too many links on the rename,
|
||||||
rather than on the original link. (Probably it
|
rather than on the original link. (Probably it
|
||||||
|
@ -238,7 +247,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
debug("'%s' has reached maximum number of links", linkPath);
|
debug("'%s' has reached maximum number of links", linkPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw SysError(format("cannot rename '%1%' to '%2%'") % tempLink % path);
|
throw SysError("cannot rename '%1%' to '%2%'", tempLink, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.filesLinked++;
|
stats.filesLinked++;
|
||||||
|
@ -246,13 +255,13 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
stats.blocksFreed += st.st_blocks;
|
stats.blocksFreed += st.st_blocks;
|
||||||
|
|
||||||
if (act)
|
if (act)
|
||||||
act->result(ResultType::FileLinked, st.st_size, st.st_blocks);
|
act->result(resFileLinked, st.st_size, st.st_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::optimiseStore(OptimiseStats & stats)
|
void LocalStore::optimiseStore(OptimiseStats & stats)
|
||||||
{
|
{
|
||||||
Activity act(*logger, ActivityType::OptimiseStore);
|
Activity act(*logger, actOptimiseStore);
|
||||||
|
|
||||||
auto paths = queryAllValidPaths();
|
auto paths = queryAllValidPaths();
|
||||||
InodeHash inodeHash = loadInodeHash();
|
InodeHash inodeHash = loadInodeHash();
|
||||||
|
@ -265,7 +274,7 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
|
||||||
addTempRoot(i);
|
addTempRoot(i);
|
||||||
if (!isValidPath(i)) continue; /* path was GC'ed, probably */
|
if (!isValidPath(i)) continue; /* path was GC'ed, probably */
|
||||||
{
|
{
|
||||||
Activity act(*logger, Verbosity::Talkative, ActivityType::Unknown, fmt("optimising path '%s'", printStorePath(i)));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("optimising path '%s'", printStorePath(i)));
|
||||||
optimisePath_(&act, stats, realStoreDir + "/" + std::string(i.to_string()), inodeHash);
|
optimisePath_(&act, stats, realStoreDir + "/" + std::string(i.to_string()), inodeHash);
|
||||||
}
|
}
|
||||||
done++;
|
done++;
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
ParsedDerivation::ParsedDerivation(StorePath && drvPath, BasicDerivation & drv)
|
ParsedDerivation::ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv)
|
||||||
: drvPath(std::move(drvPath)), drv(drv)
|
: drvPath(drvPath), drv(drv)
|
||||||
{
|
{
|
||||||
/* Parse the __json attribute, if any. */
|
/* Parse the __json attribute, if any. */
|
||||||
auto jsonAttr = drv.env.find("__json");
|
auto jsonAttr = drv.env.find("__json");
|
||||||
|
@ -117,4 +117,9 @@ bool ParsedDerivation::substitutesAllowed() const
|
||||||
return getBoolAttr("allowSubstitutes", true);
|
return getBoolAttr("allowSubstitutes", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ParsedDerivation::contentAddressed() const
|
||||||
|
{
|
||||||
|
return getBoolAttr("__contentAddressed", false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ class ParsedDerivation
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ParsedDerivation(StorePath && drvPath, BasicDerivation & drv);
|
ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv);
|
||||||
|
|
||||||
~ParsedDerivation();
|
~ParsedDerivation();
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ public:
|
||||||
bool willBuildLocally() const;
|
bool willBuildLocally() const;
|
||||||
|
|
||||||
bool substitutesAllowed() const;
|
bool substitutesAllowed() const;
|
||||||
|
|
||||||
|
bool contentAddressed() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,38 +2,38 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
extern "C" {
|
MakeError(BadStorePath, Error);
|
||||||
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);
|
static void checkName(std::string_view path, std::string_view name)
|
||||||
rust::Result<StorePath> ffi_StorePath_fromBaseName(rust::StringSlice baseName);
|
{
|
||||||
rust::String ffi_StorePath_to_string(const StorePath & _this);
|
if (name.empty())
|
||||||
StorePath ffi_StorePath_clone(const StorePath & _this);
|
throw BadStorePath("store path '%s' has an empty name", path);
|
||||||
rust::StringSlice ffi_StorePath_name(const StorePath & _this);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath StorePath::make(std::string_view path, std::string_view storeDir)
|
StorePath::StorePath(std::string_view _baseName)
|
||||||
|
: baseName(_baseName)
|
||||||
{
|
{
|
||||||
return ffi_StorePath_new((rust::StringSlice) path, (rust::StringSlice) storeDir).unwrap();
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath StorePath::make(unsigned char hash[20], std::string_view name)
|
StorePath::StorePath(const Hash & hash, std::string_view _name)
|
||||||
|
: baseName((hash.to_string(Base32, false) + "-").append(std::string(_name)))
|
||||||
{
|
{
|
||||||
return ffi_StorePath_new2(hash, (rust::StringSlice) name).unwrap();
|
checkName(baseName, name());
|
||||||
}
|
|
||||||
|
|
||||||
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
|
bool StorePath::isDerivation() const
|
||||||
|
@ -41,18 +41,14 @@ bool StorePath::isDerivation() const
|
||||||
return hasSuffix(name(), drvExtension);
|
return hasSuffix(name(), drvExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view StorePath::name() const
|
StorePath StorePath::dummy("ffffffffffffffffffffffffffffffff-x");
|
||||||
{
|
|
||||||
return ffi_StorePath_name(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorePath StorePath::dummy(
|
|
||||||
StorePath::make(
|
|
||||||
(unsigned char *) "xxxxxxxxxxxxxxxxxxxx", "x"));
|
|
||||||
|
|
||||||
StorePath Store::parseStorePath(std::string_view path) const
|
StorePath Store::parseStorePath(std::string_view path) const
|
||||||
{
|
{
|
||||||
return StorePath::make(path, storeDir);
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<StorePath> Store::maybeParseStorePath(std::string_view path) const
|
std::optional<StorePath> Store::maybeParseStorePath(std::string_view path) const
|
||||||
|
@ -78,9 +74,7 @@ StorePathSet Store::parseStorePathSet(const PathSet & paths) const
|
||||||
|
|
||||||
std::string Store::printStorePath(const StorePath & path) const
|
std::string Store::printStorePath(const StorePath & path) const
|
||||||
{
|
{
|
||||||
auto s = storeDir + "/";
|
return (storeDir + "/").append(path.to_string());
|
||||||
s += (std::string_view) path.to_string();
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PathSet Store::printStorePathSet(const StorePathSet & paths) const
|
PathSet Store::printStorePathSet(const StorePathSet & paths) const
|
||||||
|
@ -90,29 +84,6 @@ PathSet Store::printStorePathSet(const StorePathSet & paths) const
|
||||||
return res;
|
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)
|
std::pair<std::string_view, StringSet> parsePathWithOutputs(std::string_view s)
|
||||||
{
|
{
|
||||||
size_t n = s.find("!");
|
size_t n = s.find("!");
|
||||||
|
|
|
@ -1,74 +1,60 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "rust-ffi.hh"
|
|
||||||
#include "content-address.hh"
|
#include "content-address.hh"
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
/* See path.rs. */
|
|
||||||
struct StorePath;
|
|
||||||
|
|
||||||
class Store;
|
class Store;
|
||||||
|
struct Hash;
|
||||||
|
|
||||||
extern "C" {
|
class StorePath
|
||||||
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);
|
|
||||||
void ffi_StorePath_clone_to(const StorePath & _other, StorePath & _this);
|
|
||||||
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() = delete;
|
||||||
|
|
||||||
static StorePath make(std::string_view path, std::string_view storeDir);
|
StorePath(std::string_view baseName);
|
||||||
|
|
||||||
static StorePath make(unsigned char hash[20], std::string_view name);
|
StorePath(const Hash & hash, std::string_view name);
|
||||||
|
|
||||||
static StorePath fromBaseName(std::string_view baseName);
|
std::string_view to_string() const
|
||||||
|
{
|
||||||
rust::String to_string() const;
|
return baseName;
|
||||||
|
}
|
||||||
|
|
||||||
bool operator < (const StorePath & other) const
|
bool operator < (const StorePath & other) const
|
||||||
{
|
{
|
||||||
return ffi_StorePath_less_than(*this, other);
|
return baseName < other.baseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator == (const StorePath & other) const
|
bool operator == (const StorePath & other) const
|
||||||
{
|
{
|
||||||
return ffi_StorePath_eq(*this, other);
|
return baseName == other.baseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator != (const StorePath & other) const
|
bool operator != (const StorePath & other) const
|
||||||
{
|
{
|
||||||
return !(*this == other);
|
return baseName != other.baseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath(StorePath && that) = default;
|
|
||||||
|
|
||||||
StorePath(const StorePath & that)
|
|
||||||
{
|
|
||||||
ffi_StorePath_clone_to(that, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator = (const StorePath & that)
|
|
||||||
{
|
|
||||||
(rust::Value<3 * sizeof(void *) + 24, ffi_StorePath_drop>::operator = (that));
|
|
||||||
ffi_StorePath_clone_to(that, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorePath clone() const;
|
|
||||||
|
|
||||||
/* Check whether a file name ends with the extension for
|
/* Check whether a file name ends with the extension for
|
||||||
derivations. */
|
derivations. */
|
||||||
bool isDerivation() const;
|
bool isDerivation() const;
|
||||||
|
|
||||||
std::string_view name() const;
|
std::string_view name() const
|
||||||
|
|
||||||
unsigned char * hashData() const
|
|
||||||
{
|
{
|
||||||
return ffi_StorePath_hash_data(*this);
|
return std::string_view(baseName).substr(HashLen + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view hashPart() const
|
||||||
|
{
|
||||||
|
return std::string_view(baseName).substr(0, HashLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static StorePath dummy;
|
static StorePath dummy;
|
||||||
|
@ -77,14 +63,6 @@ struct StorePath : rust::Value<3 * sizeof(void *) + 24, ffi_StorePath_drop>
|
||||||
typedef std::set<StorePath> StorePathSet;
|
typedef std::set<StorePath> StorePathSet;
|
||||||
typedef std::vector<StorePath> StorePaths;
|
typedef std::vector<StorePath> StorePaths;
|
||||||
|
|
||||||
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. */
|
/* Extension of derivations in the Nix store. */
|
||||||
const std::string drvExtension = ".drv";
|
const std::string drvExtension = ".drv";
|
||||||
|
|
||||||
|
@ -93,18 +71,6 @@ struct StorePathWithOutputs
|
||||||
StorePath path;
|
StorePath path;
|
||||||
std::set<std::string> outputs;
|
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;
|
std::string to_string(const Store & store) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -117,7 +83,7 @@ namespace std {
|
||||||
template<> struct hash<nix::StorePath> {
|
template<> struct hash<nix::StorePath> {
|
||||||
std::size_t operator()(const nix::StorePath & path) const noexcept
|
std::size_t operator()(const nix::StorePath & path) const noexcept
|
||||||
{
|
{
|
||||||
return * (std::size_t *) path.hashData();
|
return * (std::size_t *) path.to_string().data();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
||||||
if (!fd && (create || errno != ENOENT))
|
if (!fd && (create || errno != ENOENT))
|
||||||
throw SysError(format("opening lock file '%1%'") % path);
|
throw SysError("opening lock file '%1%'", path);
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ bool lockFile(int fd, LockType lockType, bool wait)
|
||||||
while (flock(fd, type) != 0) {
|
while (flock(fd, type) != 0) {
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
throw SysError(format("acquiring/releasing lock"));
|
throw SysError("acquiring/releasing lock");
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ bool lockFile(int fd, LockType lockType, bool wait)
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
if (errno == EWOULDBLOCK) return false;
|
if (errno == EWOULDBLOCK) return false;
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
throw SysError(format("acquiring/releasing lock"));
|
throw SysError("acquiring/releasing lock");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ bool PathLocks::lockPaths(const PathSet & paths,
|
||||||
hasn't been unlinked). */
|
hasn't been unlinked). */
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstat(fd.get(), &st) == -1)
|
if (fstat(fd.get(), &st) == -1)
|
||||||
throw SysError(format("statting lock file '%1%'") % lockPath);
|
throw SysError("statting lock file '%1%'", lockPath);
|
||||||
if (st.st_size != 0)
|
if (st.st_size != 0)
|
||||||
/* This lock file has been unlinked, so we're holding
|
/* This lock file has been unlinked, so we're holding
|
||||||
a lock on a deleted file. This means that other
|
a lock on a deleted file. This means that other
|
||||||
|
@ -160,7 +160,8 @@ void PathLocks::unlock()
|
||||||
|
|
||||||
if (close(i.first) == -1)
|
if (close(i.first) == -1)
|
||||||
printError(
|
printError(
|
||||||
format("error (ignored): cannot close lock file on '%1%'") % i.second);
|
"error (ignored): cannot close lock file on '%1%'",
|
||||||
|
i.second);
|
||||||
|
|
||||||
debug(format("lock released 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;
|
gen.number = n;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(gen.path.c_str(), &st) != 0)
|
if (lstat(gen.path.c_str(), &st) != 0)
|
||||||
throw SysError(format("statting '%1%'") % gen.path);
|
throw SysError("statting '%1%'", gen.path);
|
||||||
gen.creationTime = st.st_mtime;
|
gen.creationTime = st.st_mtime;
|
||||||
gens.push_back(gen);
|
gens.push_back(gen);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ Path createGeneration(ref<LocalFSStore> store, Path profile, Path outPath)
|
||||||
static void removeFile(const Path & path)
|
static void removeFile(const Path & path)
|
||||||
{
|
{
|
||||||
if (remove(path.c_str()) == -1)
|
if (remove(path.c_str()) == -1)
|
||||||
throw SysError(format("cannot unlink '%1%'") % path);
|
throw SysError("cannot unlink '%1%'", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ void deleteGenerations(const Path & profile, const std::set<unsigned int> & gens
|
||||||
Generations gens = findGenerations(profile, curGen);
|
Generations gens = findGenerations(profile, curGen);
|
||||||
|
|
||||||
if (gensToDelete.find(curGen) != gensToDelete.end())
|
if (gensToDelete.find(curGen) != gensToDelete.end())
|
||||||
throw Error(format("cannot delete current generation of profile %1%'") % profile);
|
throw Error("cannot delete current generation of profile %1%'", profile);
|
||||||
|
|
||||||
for (auto & i : gens) {
|
for (auto & i : gens) {
|
||||||
if (gensToDelete.find(i.number) == gensToDelete.end()) continue;
|
if (gensToDelete.find(i.number) == gensToDelete.end()) continue;
|
||||||
|
@ -226,7 +226,7 @@ void deleteGenerationsOlderThan(const Path & profile, const string & timeSpec, b
|
||||||
int days;
|
int days;
|
||||||
|
|
||||||
if (!string2Int(strDays, days) || days < 1)
|
if (!string2Int(strDays, days) || days < 1)
|
||||||
throw Error(format("invalid number of days specifier '%1%'") % timeSpec);
|
throw Error("invalid number of days specifier '%1%'", timeSpec);
|
||||||
|
|
||||||
time_t oldTime = curTime - days * 24 * 3600;
|
time_t oldTime = curTime - days * 24 * 3600;
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ struct RefScanSink : Sink
|
||||||
|
|
||||||
string tail;
|
string tail;
|
||||||
|
|
||||||
RefScanSink() : hashSink(HashType::SHA256) { }
|
RefScanSink() : hashSink(htSHA256) { }
|
||||||
|
|
||||||
void operator () (const unsigned char * data, size_t len);
|
void operator () (const unsigned char * data, size_t len);
|
||||||
};
|
};
|
||||||
|
@ -92,11 +92,11 @@ PathSet scanForReferences(const string & path,
|
||||||
auto baseName = std::string(baseNameOf(i));
|
auto baseName = std::string(baseNameOf(i));
|
||||||
string::size_type pos = baseName.find('-');
|
string::size_type pos = baseName.find('-');
|
||||||
if (pos == string::npos)
|
if (pos == string::npos)
|
||||||
throw Error(format("bad reference '%1%'") % i);
|
throw Error("bad reference '%1%'", i);
|
||||||
string s = string(baseName, 0, pos);
|
string s = string(baseName, 0, pos);
|
||||||
assert(s.size() == refLength);
|
assert(s.size() == refLength);
|
||||||
assert(backMap.find(s) == backMap.end());
|
assert(backMap.find(s) == backMap.end());
|
||||||
// parseHash(HashType::SHA256, s);
|
// parseHash(htSHA256, s);
|
||||||
sink.hashes.insert(s);
|
sink.hashes.insert(s);
|
||||||
backMap[s] = i;
|
backMap[s] = i;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ RemoteFSAccessor::RemoteFSAccessor(ref<Store> store, const Path & cacheDir)
|
||||||
Path RemoteFSAccessor::makeCacheFile(const Path & storePath, const std::string & ext)
|
Path RemoteFSAccessor::makeCacheFile(const Path & storePath, const std::string & ext)
|
||||||
{
|
{
|
||||||
assert(cacheDir != "");
|
assert(cacheDir != "");
|
||||||
return fmt("%s/%s.%s", cacheDir, storePathToHash(storePath), ext);
|
return fmt("%s/%s.%s", cacheDir, store->parseStorePath(storePath).hashPart(), ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteFSAccessor::addToCache(const Path & storePath, const std::string & nar,
|
void RemoteFSAccessor::addToCache(const Path & storePath, const std::string & nar,
|
||||||
|
@ -51,7 +51,7 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
|
||||||
std::string restPath = std::string(path, storePath.size());
|
std::string restPath = std::string(path, storePath.size());
|
||||||
|
|
||||||
if (!store->isValidPath(store->parseStorePath(storePath)))
|
if (!store->isValidPath(store->parseStorePath(storePath)))
|
||||||
throw InvalidPath(format("path '%1%' is not a valid store path") % storePath);
|
throw InvalidPath("path '%1%' is not a valid store path", storePath);
|
||||||
|
|
||||||
auto i = nars.find(storePath);
|
auto i = nars.find(storePath);
|
||||||
if (i != nars.end()) return {i->second, restPath};
|
if (i != nars.end()) return {i->second, restPath};
|
||||||
|
|
|
@ -116,11 +116,11 @@ ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
if (socketPath.size() + 1 >= sizeof(addr.sun_path))
|
if (socketPath.size() + 1 >= sizeof(addr.sun_path))
|
||||||
throw Error(format("socket path '%1%' is too long") % socketPath);
|
throw Error("socket path '%1%' is too long", socketPath);
|
||||||
strcpy(addr.sun_path, socketPath.c_str());
|
strcpy(addr.sun_path, socketPath.c_str());
|
||||||
|
|
||||||
if (::connect(conn->fd.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
if (::connect(conn->fd.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||||
throw SysError(format("cannot connect to daemon at '%1%'") % socketPath);
|
throw SysError("cannot connect to daemon at '%1%'", socketPath);
|
||||||
|
|
||||||
conn->from.fd = conn->fd.get();
|
conn->from.fd = conn->fd.get();
|
||||||
conn->to.fd = conn->fd.get();
|
conn->to.fd = conn->fd.get();
|
||||||
|
@ -177,11 +177,11 @@ void RemoteStore::setOptions(Connection & conn)
|
||||||
<< settings.keepFailed
|
<< settings.keepFailed
|
||||||
<< settings.keepGoing
|
<< settings.keepGoing
|
||||||
<< settings.tryFallback
|
<< settings.tryFallback
|
||||||
<< (uint64_t) verbosity
|
<< verbosity
|
||||||
<< settings.maxBuildJobs
|
<< settings.maxBuildJobs
|
||||||
<< settings.maxSilentTime
|
<< settings.maxSilentTime
|
||||||
<< true
|
<< true
|
||||||
<< (uint64_t) (settings.verboseBuild ? Verbosity::Error : Verbosity::Vomit)
|
<< (settings.verboseBuild ? lvlError : lvlVomit)
|
||||||
<< 0 // obsolete log type
|
<< 0 // obsolete log type
|
||||||
<< 0 /* obsolete print build trace */
|
<< 0 /* obsolete print build trace */
|
||||||
<< settings.buildCores
|
<< settings.buildCores
|
||||||
|
@ -228,7 +228,7 @@ struct ConnectionHandle
|
||||||
|
|
||||||
~ConnectionHandle()
|
~ConnectionHandle()
|
||||||
{
|
{
|
||||||
if (!daemonException && std::uncaught_exception()) {
|
if (!daemonException && std::uncaught_exceptions()) {
|
||||||
handle.markBad();
|
handle.markBad();
|
||||||
debug("closing daemon connection because of an exception");
|
debug("closing daemon connection because of an exception");
|
||||||
}
|
}
|
||||||
|
@ -268,7 +268,7 @@ StorePathSet RemoteStore::queryValidPaths(const StorePathSet & paths, Substitute
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) {
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) {
|
||||||
StorePathSet res;
|
StorePathSet res;
|
||||||
for (auto & i : paths)
|
for (auto & i : paths)
|
||||||
if (isValidPath(i)) res.insert(i.clone());
|
if (isValidPath(i)) res.insert(i);
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
conn->to << wopQueryValidPaths;
|
conn->to << wopQueryValidPaths;
|
||||||
|
@ -296,7 +296,7 @@ StorePathSet RemoteStore::querySubstitutablePaths(const StorePathSet & paths)
|
||||||
for (auto & i : paths) {
|
for (auto & i : paths) {
|
||||||
conn->to << wopHasSubstitutes << printStorePath(i);
|
conn->to << wopHasSubstitutes << printStorePath(i);
|
||||||
conn.processStderr();
|
conn.processStderr();
|
||||||
if (readInt(conn->from)) res.insert(i.clone());
|
if (readInt(conn->from)) res.insert(i);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
|
@ -329,7 +329,7 @@ void RemoteStore::querySubstitutablePathInfos(const StorePathSet & paths,
|
||||||
info.references = readStorePaths<StorePathSet>(*this, conn->from);
|
info.references = readStorePaths<StorePathSet>(*this, conn->from);
|
||||||
info.downloadSize = readLongLong(conn->from);
|
info.downloadSize = readLongLong(conn->from);
|
||||||
info.narSize = readLongLong(conn->from);
|
info.narSize = readLongLong(conn->from);
|
||||||
infos.insert_or_assign(i.clone(), std::move(info));
|
infos.insert_or_assign(i, std::move(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -365,17 +365,17 @@ void RemoteStore::queryPathInfoUncached(const StorePath & path,
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
// Ugly backwards compatibility hack.
|
// Ugly backwards compatibility hack.
|
||||||
if (e.msg().find("is not valid") != std::string::npos)
|
if (e.msg().find("is not valid") != std::string::npos)
|
||||||
throw InvalidPath(e.what());
|
throw InvalidPath(e.info());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 17) {
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 17) {
|
||||||
bool valid; conn->from >> valid;
|
bool valid; conn->from >> valid;
|
||||||
if (!valid) throw InvalidPath("path '%s' is not valid", printStorePath(path));
|
if (!valid) throw InvalidPath("path '%s' is not valid", printStorePath(path));
|
||||||
}
|
}
|
||||||
info = std::make_shared<ValidPathInfo>(path.clone());
|
info = std::make_shared<ValidPathInfo>(StorePath(path));
|
||||||
auto deriver = readString(conn->from);
|
auto deriver = readString(conn->from);
|
||||||
if (deriver != "") info->deriver = parseStorePath(deriver);
|
if (deriver != "") info->deriver = parseStorePath(deriver);
|
||||||
info->narHash = Hash(readString(conn->from), HashType::SHA256);
|
info->narHash = Hash(readString(conn->from), htSHA256);
|
||||||
info->references = readStorePaths<StorePathSet>(*this, conn->from);
|
info->references = readStorePaths<StorePathSet>(*this, conn->from);
|
||||||
conn->from >> info->registrationTime >> info->narSize;
|
conn->from >> info->registrationTime >> info->narSize;
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) {
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) {
|
||||||
|
@ -396,7 +396,7 @@ void RemoteStore::queryReferrers(const StorePath & path,
|
||||||
conn->to << wopQueryReferrers << printStorePath(path);
|
conn->to << wopQueryReferrers << printStorePath(path);
|
||||||
conn.processStderr();
|
conn.processStderr();
|
||||||
for (auto & i : readStorePaths<StorePathSet>(*this, conn->from))
|
for (auto & i : readStorePaths<StorePathSet>(*this, conn->from))
|
||||||
referrers.insert(i.clone());
|
referrers.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -418,15 +418,6 @@ StorePathSet RemoteStore::queryDerivationOutputs(const StorePath & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PathSet RemoteStore::queryDerivationOutputNames(const StorePath & path)
|
|
||||||
{
|
|
||||||
auto conn(getConnection());
|
|
||||||
conn->to << wopQueryDerivationOutputNames << printStorePath(path);
|
|
||||||
conn.processStderr();
|
|
||||||
return readStrings<PathSet>(conn->from);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::optional<StorePath> RemoteStore::queryPathFromHashPart(const std::string & hashPart)
|
std::optional<StorePath> RemoteStore::queryPathFromHashPart(const std::string & hashPart)
|
||||||
{
|
{
|
||||||
auto conn(getConnection());
|
auto conn(getConnection());
|
||||||
|
@ -471,7 +462,7 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
conn->to << wopAddToStoreNar
|
conn->to << wopAddToStoreNar
|
||||||
<< printStorePath(info.path)
|
<< printStorePath(info.path)
|
||||||
<< (info.deriver ? printStorePath(*info.deriver) : "")
|
<< (info.deriver ? printStorePath(*info.deriver) : "")
|
||||||
<< info.narHash.to_string(Base::Base16, false);
|
<< info.narHash.to_string(Base16, false);
|
||||||
writeStorePaths(*this, conn->to, info.references);
|
writeStorePaths(*this, conn->to, info.references);
|
||||||
conn->to << info.registrationTime << info.narSize
|
conn->to << info.registrationTime << info.narSize
|
||||||
<< info.ultimate << info.sigs << renderContentAddress(info.ca)
|
<< info.ultimate << info.sigs << renderContentAddress(info.ca)
|
||||||
|
@ -495,7 +486,7 @@ StorePath RemoteStore::addToStore(const string & name, const Path & _srcPath,
|
||||||
conn->to
|
conn->to
|
||||||
<< wopAddToStore
|
<< wopAddToStore
|
||||||
<< name
|
<< name
|
||||||
<< ((hashAlgo == HashType::SHA256 && method == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */
|
<< ((hashAlgo == htSHA256 && method == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */
|
||||||
<< (method == FileIngestionMethod::Recursive ? 1 : 0)
|
<< (method == FileIngestionMethod::Recursive ? 1 : 0)
|
||||||
<< printHashType(hashAlgo);
|
<< printHashType(hashAlgo);
|
||||||
|
|
||||||
|
|
|
@ -51,8 +51,6 @@ public:
|
||||||
|
|
||||||
StorePathSet queryDerivationOutputs(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;
|
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override;
|
||||||
|
|
||||||
StorePathSet querySubstitutablePaths(const StorePathSet & paths) override;
|
StorePathSet querySubstitutablePaths(const StorePathSet & paths) override;
|
||||||
|
@ -65,7 +63,7 @@ public:
|
||||||
std::shared_ptr<FSAccessor> accessor) override;
|
std::shared_ptr<FSAccessor> accessor) override;
|
||||||
|
|
||||||
StorePath addToStore(const string & name, const Path & srcPath,
|
StorePath addToStore(const string & name, const Path & srcPath,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = HashType::SHA256,
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
|
||||||
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override;
|
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override;
|
||||||
|
|
||||||
StorePath addTextToStore(const string & name, const string & s,
|
StorePath addTextToStore(const string & name, const string & s,
|
||||||
|
|
|
@ -32,8 +32,10 @@ namespace nix {
|
||||||
struct S3Error : public Error
|
struct S3Error : public Error
|
||||||
{
|
{
|
||||||
Aws::S3::S3Errors err;
|
Aws::S3::S3Errors err;
|
||||||
S3Error(Aws::S3::S3Errors err, const FormatOrString & fs)
|
|
||||||
: Error(fs), err(err) { };
|
template<typename... Args>
|
||||||
|
S3Error(Aws::S3::S3Errors err, const Args & ... args)
|
||||||
|
: Error(args...), err(err) { };
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Helper: given an Outcome<R, E>, return R in case of success, or
|
/* Helper: given an Outcome<R, E>, return R in case of success, or
|
||||||
|
@ -68,9 +70,9 @@ static void initAWS()
|
||||||
shared.cc), so don't let aws-sdk-cpp override it. */
|
shared.cc), so don't let aws-sdk-cpp override it. */
|
||||||
options.cryptoOptions.initAndCleanupOpenSSL = false;
|
options.cryptoOptions.initAndCleanupOpenSSL = false;
|
||||||
|
|
||||||
if (verbosity >= Verbosity::Debug) {
|
if (verbosity >= lvlDebug) {
|
||||||
options.loggingOptions.logLevel =
|
options.loggingOptions.logLevel =
|
||||||
verbosity == Verbosity::Debug
|
verbosity == lvlDebug
|
||||||
? Aws::Utils::Logging::LogLevel::Debug
|
? Aws::Utils::Logging::LogLevel::Debug
|
||||||
: Aws::Utils::Logging::LogLevel::Trace;
|
: Aws::Utils::Logging::LogLevel::Trace;
|
||||||
options.loggingOptions.logger_create_fn = [options]() {
|
options.loggingOptions.logger_create_fn = [options]() {
|
||||||
|
@ -109,7 +111,9 @@ class RetryStrategy : public Aws::Client::DefaultRetryStrategy
|
||||||
auto retry = Aws::Client::DefaultRetryStrategy::ShouldRetry(error, attemptedRetries);
|
auto retry = Aws::Client::DefaultRetryStrategy::ShouldRetry(error, attemptedRetries);
|
||||||
if (retry)
|
if (retry)
|
||||||
printError("AWS error '%s' (%s), will retry in %d ms",
|
printError("AWS error '%s' (%s), will retry in %d ms",
|
||||||
error.GetExceptionName(), error.GetMessage(), CalculateDelayBeforeNextRetry(error, attemptedRetries));
|
error.GetExceptionName(),
|
||||||
|
error.GetMessage(),
|
||||||
|
CalculateDelayBeforeNextRetry(error, attemptedRetries));
|
||||||
return retry;
|
return retry;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -249,7 +253,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
|
||||||
// If bucket listing is disabled, 404s turn into 403s
|
// If bucket listing is disabled, 404s turn into 403s
|
||||||
|| error.GetErrorType() == Aws::S3::S3Errors::ACCESS_DENIED)
|
|| error.GetErrorType() == Aws::S3::S3Errors::ACCESS_DENIED)
|
||||||
return false;
|
return false;
|
||||||
throw Error(format("AWS error fetching '%s': %s") % path % error.GetMessage());
|
throw Error("AWS error fetching '%s': %s", path, error.GetMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -29,7 +29,7 @@ SQLite::SQLite(const Path & path, bool create)
|
||||||
{
|
{
|
||||||
if (sqlite3_open_v2(path.c_str(), &db,
|
if (sqlite3_open_v2(path.c_str(), &db,
|
||||||
SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
|
SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
|
||||||
throw Error(format("cannot open SQLite database '%s'") % path);
|
throw Error("cannot open SQLite database '%s'", path);
|
||||||
|
|
||||||
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "setting timeout");
|
throwSQLiteError(db, "setting timeout");
|
||||||
|
@ -204,7 +204,10 @@ void handleSQLiteBusy(const SQLiteBusy & e)
|
||||||
|
|
||||||
if (now > lastWarned + 10) {
|
if (now > lastWarned + 10) {
|
||||||
lastWarned = now;
|
lastWarned = now;
|
||||||
printError("warning: %s", e.what());
|
logWarning({
|
||||||
|
.name = "Sqlite busy",
|
||||||
|
.hint = hintfmt(e.what())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sleep for a while since retrying the transaction right away
|
/* Sleep for a while since retrying the transaction right away
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "types.hh"
|
#include "error.hh"
|
||||||
|
|
||||||
struct sqlite3;
|
struct sqlite3;
|
||||||
struct sqlite3_stmt;
|
struct sqlite3_stmt;
|
||||||
|
|
|
@ -58,7 +58,7 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(const std::string
|
||||||
addCommonSSHOpts(args);
|
addCommonSSHOpts(args);
|
||||||
if (socketPath != "")
|
if (socketPath != "")
|
||||||
args.insert(args.end(), {"-S", socketPath});
|
args.insert(args.end(), {"-S", socketPath});
|
||||||
if (verbosity >= Verbosity::Chatty)
|
if (verbosity >= lvlChatty)
|
||||||
args.push_back("-v");
|
args.push_back("-v");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ Path SSHMaster::startMaster()
|
||||||
, "-o", "LocalCommand=echo started"
|
, "-o", "LocalCommand=echo started"
|
||||||
, "-o", "PermitLocalCommand=yes"
|
, "-o", "PermitLocalCommand=yes"
|
||||||
};
|
};
|
||||||
if (verbosity >= Verbosity::Chatty)
|
if (verbosity >= lvlChatty)
|
||||||
args.push_back("-v");
|
args.push_back("-v");
|
||||||
addCommonSSHOpts(args);
|
addCommonSSHOpts(args);
|
||||||
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
|
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
|
||||||
|
|
|
@ -23,7 +23,7 @@ bool Store::isInStore(const Path & path) const
|
||||||
Path Store::toStorePath(const Path & path) const
|
Path Store::toStorePath(const Path & path) const
|
||||||
{
|
{
|
||||||
if (!isInStore(path))
|
if (!isInStore(path))
|
||||||
throw Error(format("path '%1%' is not in the Nix store") % path);
|
throw Error("path '%1%' is not in the Nix store", path);
|
||||||
Path::size_type slash = path.find('/', storeDir.size() + 1);
|
Path::size_type slash = path.find('/', storeDir.size() + 1);
|
||||||
if (slash == Path::npos)
|
if (slash == Path::npos)
|
||||||
return path;
|
return path;
|
||||||
|
@ -55,21 +55,13 @@ StorePath Store::followLinksToStorePath(std::string_view path) const
|
||||||
StorePathWithOutputs Store::followLinksToStorePathWithOutputs(std::string_view path) const
|
StorePathWithOutputs Store::followLinksToStorePathWithOutputs(std::string_view path) const
|
||||||
{
|
{
|
||||||
auto [path2, outputs] = nix::parsePathWithOutputs(path);
|
auto [path2, outputs] = nix::parsePathWithOutputs(path);
|
||||||
return StorePathWithOutputs(followLinksToStorePath(path2), std::move(outputs));
|
return StorePathWithOutputs { followLinksToStorePath(path2), std::move(outputs) };
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string storePathToHash(const Path & path)
|
|
||||||
{
|
|
||||||
auto base = baseNameOf(path);
|
|
||||||
assert(base.size() >= storePathHashLen);
|
|
||||||
return string(base, 0, storePathHashLen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Store paths have the following form:
|
/* Store paths have the following form:
|
||||||
|
|
||||||
<store>/<h>-<name>
|
<realized-path> = <store>/<h>-<name>
|
||||||
|
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -93,11 +85,14 @@ string storePathToHash(const Path & path)
|
||||||
<type> = one of:
|
<type> = one of:
|
||||||
"text:<r1>:<r2>:...<rN>"
|
"text:<r1>:<r2>:...<rN>"
|
||||||
for plain text files written to the store using
|
for plain text files written to the store using
|
||||||
addTextToStore(); <r1> ... <rN> are the references of the
|
addTextToStore(); <r1> ... <rN> are the store paths referenced
|
||||||
path.
|
by this path, in the form described by <realized-path>
|
||||||
"source"
|
"source:<r1>:<r2>:...:<rN>:self"
|
||||||
for paths copied to the store using addToStore() when recursive
|
for paths copied to the store using addToStore() when recursive
|
||||||
= true and hashAlgo = "sha256"
|
= true and hashAlgo = "sha256". Just like in the text case, we
|
||||||
|
can have the store paths referenced by the path.
|
||||||
|
Additionally, we can have an optional :self label to denote self
|
||||||
|
reference.
|
||||||
"output:<id>"
|
"output:<id>"
|
||||||
for either the outputs created by derivations, OR paths copied
|
for either the outputs created by derivations, OR paths copied
|
||||||
to the store using addToStore() with recursive != true or
|
to the store using addToStore() with recursive != true or
|
||||||
|
@ -125,6 +120,12 @@ string storePathToHash(const Path & path)
|
||||||
the contents of the path (or expected contents of the
|
the contents of the path (or expected contents of the
|
||||||
path for fixed-output derivations)
|
path for fixed-output derivations)
|
||||||
|
|
||||||
|
Note that since an output derivation has always type output, while
|
||||||
|
something added by addToStore can have type output or source depending
|
||||||
|
on the hash, this means that the same input can be hashed differently
|
||||||
|
if added to the store via addToStore or via a derivation, in the sha256
|
||||||
|
recursive case.
|
||||||
|
|
||||||
It would have been nicer to handle fixed-output derivations under
|
It would have been nicer to handle fixed-output derivations under
|
||||||
"source", e.g. have something like "source:<rec><algo>", but we're
|
"source", e.g. have something like "source:<rec><algo>", but we're
|
||||||
stuck with this for now...
|
stuck with this for now...
|
||||||
|
@ -142,9 +143,9 @@ StorePath Store::makeStorePath(const string & type,
|
||||||
const Hash & hash, std::string_view name) const
|
const Hash & hash, std::string_view name) const
|
||||||
{
|
{
|
||||||
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
|
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
|
||||||
string s = type + ":" + hash.to_string(Base::Base16) + ":" + storeDir + ":" + std::string(name);
|
string s = type + ":" + hash.to_string(Base16, true) + ":" + storeDir + ":" + std::string(name);
|
||||||
auto h = compressHash(hashString(HashType::SHA256, s), 20);
|
auto h = compressHash(hashString(htSHA256, s), 20);
|
||||||
return StorePath::make(h.hash, name);
|
return StorePath(h, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -178,13 +179,16 @@ StorePath Store::makeFixedOutputPath(
|
||||||
const StorePathSet & references,
|
const StorePathSet & references,
|
||||||
bool hasSelfReference) const
|
bool hasSelfReference) const
|
||||||
{
|
{
|
||||||
if (hash.type == HashType::SHA256 && method == FileIngestionMethod::Recursive) {
|
if (hash.type == htSHA256 && method == FileIngestionMethod::Recursive) {
|
||||||
return makeStorePath(makeType(*this, "source", references, hasSelfReference), hash, name);
|
return makeStorePath(makeType(*this, "source", references, hasSelfReference), hash, name);
|
||||||
} else {
|
} else {
|
||||||
assert(references.empty());
|
assert(references.empty());
|
||||||
return makeStorePath("output:out", hashString(HashType::SHA256,
|
return makeStorePath("output:out",
|
||||||
"fixed:out:" + makeFileIngestionPrefix(method) +
|
hashString(htSHA256,
|
||||||
hash.to_string(Base::Base16) + ":"), name);
|
"fixed:out:"
|
||||||
|
+ makeFileIngestionPrefix(method)
|
||||||
|
+ hash.to_string(Base16, true) + ":"),
|
||||||
|
name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +196,7 @@ StorePath Store::makeFixedOutputPath(
|
||||||
StorePath Store::makeTextPath(std::string_view name, const Hash & hash,
|
StorePath Store::makeTextPath(std::string_view name, const Hash & hash,
|
||||||
const StorePathSet & references) const
|
const StorePathSet & references) const
|
||||||
{
|
{
|
||||||
assert(hash.type == HashType::SHA256);
|
assert(hash.type == htSHA256);
|
||||||
/* Stuff the references (if any) into the type. This is a bit
|
/* Stuff the references (if any) into the type. This is a bit
|
||||||
hacky, but we can't put them in `s' since that would be
|
hacky, but we can't put them in `s' since that would be
|
||||||
ambiguous. */
|
ambiguous. */
|
||||||
|
@ -213,7 +217,7 @@ std::pair<StorePath, Hash> Store::computeStorePathForPath(std::string_view name,
|
||||||
StorePath Store::computeStorePathForText(const string & name, const string & s,
|
StorePath Store::computeStorePathForText(const string & name, const string & s,
|
||||||
const StorePathSet & references) const
|
const StorePathSet & references) const
|
||||||
{
|
{
|
||||||
return makeTextPath(name, hashString(HashType::SHA256, s), references);
|
return makeTextPath(name, hashString(htSHA256, s), references);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -240,7 +244,7 @@ bool Store::PathInfoCacheValue::isKnownNow()
|
||||||
|
|
||||||
bool Store::isValidPath(const StorePath & storePath)
|
bool Store::isValidPath(const StorePath & storePath)
|
||||||
{
|
{
|
||||||
auto hashPart = storePathToHash(printStorePath(storePath));
|
std::string hashPart(storePath.hashPart());
|
||||||
|
|
||||||
{
|
{
|
||||||
auto state_(state.lock());
|
auto state_(state.lock());
|
||||||
|
@ -308,7 +312,7 @@ void Store::queryPathInfo(const StorePath & storePath,
|
||||||
std::string hashPart;
|
std::string hashPart;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hashPart = storePathToHash(printStorePath(storePath));
|
hashPart = storePath.hashPart();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto res = state.lock()->pathInfoCache.get(hashPart);
|
auto res = state.lock()->pathInfoCache.get(hashPart);
|
||||||
|
@ -426,7 +430,7 @@ string Store::makeValidityRegistration(const StorePathSet & paths,
|
||||||
auto info = queryPathInfo(i);
|
auto info = queryPathInfo(i);
|
||||||
|
|
||||||
if (showHash) {
|
if (showHash) {
|
||||||
s += info->narHash.to_string(Base::Base16, false) + "\n";
|
s += info->narHash.to_string(Base16, false) + "\n";
|
||||||
s += (format("%1%\n") % info->narSize).str();
|
s += (format("%1%\n") % info->narSize).str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +462,7 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store
|
||||||
auto info = queryPathInfo(storePath);
|
auto info = queryPathInfo(storePath);
|
||||||
|
|
||||||
jsonPath
|
jsonPath
|
||||||
.attr("narHash", info->narHash.to_string(hashBase))
|
.attr("narHash", info->narHash.to_string(hashBase, true))
|
||||||
.attr("narSize", info->narSize);
|
.attr("narSize", info->narSize);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -501,7 +505,7 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store
|
||||||
if (!narInfo->url.empty())
|
if (!narInfo->url.empty())
|
||||||
jsonPath.attr("url", narInfo->url);
|
jsonPath.attr("url", narInfo->url);
|
||||||
if (narInfo->fileHash)
|
if (narInfo->fileHash)
|
||||||
jsonPath.attr("downloadHash", narInfo->fileHash.to_string());
|
jsonPath.attr("downloadHash", narInfo->fileHash.to_string(Base32, true));
|
||||||
if (narInfo->fileSize)
|
if (narInfo->fileSize)
|
||||||
jsonPath.attr("downloadSize", narInfo->fileSize);
|
jsonPath.attr("downloadSize", narInfo->fileSize);
|
||||||
if (showClosureSize)
|
if (showClosureSize)
|
||||||
|
@ -550,7 +554,7 @@ void Store::buildPaths(const std::vector<StorePathWithOutputs> & paths, BuildMod
|
||||||
for (auto & path : paths) {
|
for (auto & path : paths) {
|
||||||
if (path.path.isDerivation())
|
if (path.path.isDerivation())
|
||||||
unsupported("buildPaths");
|
unsupported("buildPaths");
|
||||||
paths2.insert(path.path.clone());
|
paths2.insert(path.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryValidPaths(paths2).size() != paths2.size())
|
if (queryValidPaths(paths2).size() != paths2.size())
|
||||||
|
@ -564,7 +568,7 @@ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
|
||||||
auto srcUri = srcStore->getUri();
|
auto srcUri = srcStore->getUri();
|
||||||
auto dstUri = dstStore->getUri();
|
auto dstUri = dstStore->getUri();
|
||||||
|
|
||||||
Activity act(*logger, Verbosity::Info, ActivityType::CopyPath,
|
Activity act(*logger, lvlInfo, actCopyPath,
|
||||||
srcUri == "local" || srcUri == "daemon"
|
srcUri == "local" || srcUri == "daemon"
|
||||||
? fmt("copying path '%s' to '%s'", srcStore->printStorePath(storePath), dstUri)
|
? fmt("copying path '%s' to '%s'", srcStore->printStorePath(storePath), dstUri)
|
||||||
: dstUri == "local" || dstUri == "daemon"
|
: dstUri == "local" || dstUri == "daemon"
|
||||||
|
@ -581,7 +585,7 @@ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
srcStore->narFromPath({storePath}, sink);
|
srcStore->narFromPath({storePath}, sink);
|
||||||
auto info2 = make_ref<ValidPathInfo>(*info);
|
auto info2 = make_ref<ValidPathInfo>(*info);
|
||||||
info2->narHash = hashString(HashType::SHA256, *sink.s);
|
info2->narHash = hashString(htSHA256, *sink.s);
|
||||||
if (!info->narSize) info2->narSize = sink.s->size();
|
if (!info->narSize) info2->narSize = sink.s->size();
|
||||||
if (info->ultimate) info2->ultimate = false;
|
if (info->ultimate) info2->ultimate = false;
|
||||||
info = info2;
|
info = info2;
|
||||||
|
@ -623,7 +627,7 @@ void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & st
|
||||||
|
|
||||||
if (missing.empty()) return;
|
if (missing.empty()) return;
|
||||||
|
|
||||||
Activity act(*logger, Verbosity::Info, ActivityType::CopyPaths, fmt("copying %d paths", missing.size()));
|
Activity act(*logger, lvlInfo, actCopyPaths, fmt("copying %d paths", missing.size()));
|
||||||
|
|
||||||
std::atomic<size_t> nrDone{0};
|
std::atomic<size_t> nrDone{0};
|
||||||
std::atomic<size_t> nrFailed{0};
|
std::atomic<size_t> nrFailed{0};
|
||||||
|
@ -649,7 +653,7 @@ void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & st
|
||||||
auto info = srcStore->queryPathInfo(srcStore->parseStorePath(storePath));
|
auto info = srcStore->queryPathInfo(srcStore->parseStorePath(storePath));
|
||||||
|
|
||||||
bytesExpected += info->narSize;
|
bytesExpected += info->narSize;
|
||||||
act.setExpected(ActivityType::CopyPath, bytesExpected);
|
act.setExpected(actCopyPath, bytesExpected);
|
||||||
|
|
||||||
return srcStore->printStorePathSet(info->references);
|
return srcStore->printStorePathSet(info->references);
|
||||||
},
|
},
|
||||||
|
@ -668,7 +672,7 @@ void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & st
|
||||||
nrFailed++;
|
nrFailed++;
|
||||||
if (!settings.keepGoing)
|
if (!settings.keepGoing)
|
||||||
throw e;
|
throw e;
|
||||||
logger->log(Verbosity::Error, fmt("could not copy %s: %s", storePathS, e.what()));
|
logger->log(lvlError, fmt("could not copy %s: %s", storePathS, e.what()));
|
||||||
showProgress();
|
showProgress();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -699,7 +703,7 @@ std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istre
|
||||||
if (hashGiven) {
|
if (hashGiven) {
|
||||||
string s;
|
string s;
|
||||||
getline(str, s);
|
getline(str, s);
|
||||||
info.narHash = Hash(s, HashType::SHA256);
|
info.narHash = Hash(s, htSHA256);
|
||||||
getline(str, s);
|
getline(str, s);
|
||||||
if (!string2Int(s, info.narSize)) throw Error("number expected");
|
if (!string2Int(s, info.narSize)) throw Error("number expected");
|
||||||
}
|
}
|
||||||
|
@ -742,7 +746,7 @@ std::string ValidPathInfo::fingerprint(const Store & store) const
|
||||||
store.printStorePath(path));
|
store.printStorePath(path));
|
||||||
return
|
return
|
||||||
"1;" + store.printStorePath(path) + ";"
|
"1;" + store.printStorePath(path) + ";"
|
||||||
+ narHash.to_string(Base::Base32) + ";"
|
+ narHash.to_string(Base32, true) + ";"
|
||||||
+ std::to_string(narSize) + ";"
|
+ std::to_string(narSize) + ";"
|
||||||
+ concatStringsSep(",", store.printStorePathSet(references));
|
+ concatStringsSep(",", store.printStorePathSet(references));
|
||||||
}
|
}
|
||||||
|
@ -766,7 +770,7 @@ bool ValidPathInfo::isContentAddressed(const Store & store) const
|
||||||
return store.makeTextPath(path.name(), th.hash, references);
|
return store.makeTextPath(path.name(), th.hash, references);
|
||||||
},
|
},
|
||||||
[&](FileSystemHash fsh) {
|
[&](FileSystemHash fsh) {
|
||||||
auto refs = cloneStorePathSet(references);
|
auto refs = references;
|
||||||
bool hasSelfReference = false;
|
bool hasSelfReference = false;
|
||||||
if (refs.count(path)) {
|
if (refs.count(path)) {
|
||||||
hasSelfReference = true;
|
hasSelfReference = true;
|
||||||
|
@ -906,7 +910,7 @@ std::list<ref<Store>> getDefaultSubstituters()
|
||||||
try {
|
try {
|
||||||
stores.push_back(openStore(uri));
|
stores.push_back(openStore(uri));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
printError("warning: %s", e.what());
|
logWarning(e.info());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -178,9 +178,10 @@ struct ValidPathInfo
|
||||||
|
|
||||||
Strings shortRefs() const;
|
Strings shortRefs() const;
|
||||||
|
|
||||||
|
ValidPathInfo(const ValidPathInfo & other) = default;
|
||||||
|
|
||||||
ValidPathInfo(StorePath && path) : path(std::move(path)) { };
|
ValidPathInfo(StorePath && path) : path(std::move(path)) { };
|
||||||
ValidPathInfo(const StorePath & path) : path(path) { };
|
ValidPathInfo(const StorePath & path) : path(path) { };
|
||||||
ValidPathInfo(const ValidPathInfo & other) = default;
|
|
||||||
|
|
||||||
virtual ~ValidPathInfo() { }
|
virtual ~ValidPathInfo() { }
|
||||||
};
|
};
|
||||||
|
@ -349,7 +350,7 @@ public:
|
||||||
path and the cryptographic hash of the contents of srcPath. */
|
path and the cryptographic hash of the contents of srcPath. */
|
||||||
std::pair<StorePath, Hash> computeStorePathForPath(std::string_view name,
|
std::pair<StorePath, Hash> computeStorePathForPath(std::string_view name,
|
||||||
const Path & srcPath, FileIngestionMethod method = FileIngestionMethod::Recursive,
|
const Path & srcPath, FileIngestionMethod method = FileIngestionMethod::Recursive,
|
||||||
HashType hashAlgo = HashType::SHA256, PathFilter & filter = defaultPathFilter) const;
|
HashType hashAlgo = htSHA256, PathFilter & filter = defaultPathFilter) const;
|
||||||
|
|
||||||
/* Preparatory part of addTextToStore().
|
/* Preparatory part of addTextToStore().
|
||||||
|
|
||||||
|
@ -420,10 +421,6 @@ public:
|
||||||
virtual StorePathSet queryDerivationOutputs(const StorePath & path)
|
virtual StorePathSet queryDerivationOutputs(const StorePath & path)
|
||||||
{ unsupported("queryDerivationOutputs"); }
|
{ unsupported("queryDerivationOutputs"); }
|
||||||
|
|
||||||
/* Query the output names of the derivation denoted by `path'. */
|
|
||||||
virtual StringSet queryDerivationOutputNames(const StorePath & path)
|
|
||||||
{ unsupported("queryDerivationOutputNames"); }
|
|
||||||
|
|
||||||
/* Query the full store path given the hash part of a valid store
|
/* Query the full store path given the hash part of a valid store
|
||||||
path, or empty if the path doesn't exist. */
|
path, or empty if the path doesn't exist. */
|
||||||
virtual std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) = 0;
|
virtual std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) = 0;
|
||||||
|
@ -447,12 +444,12 @@ public:
|
||||||
The function object `filter' can be used to exclude files (see
|
The function object `filter' can be used to exclude files (see
|
||||||
libutil/archive.hh). */
|
libutil/archive.hh). */
|
||||||
virtual StorePath addToStore(const string & name, const Path & srcPath,
|
virtual StorePath addToStore(const string & name, const Path & srcPath,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = HashType::SHA256,
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
|
||||||
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) = 0;
|
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) = 0;
|
||||||
|
|
||||||
// FIXME: remove?
|
// FIXME: remove?
|
||||||
virtual StorePath addToStoreFromDump(const string & dump, const string & name,
|
virtual StorePath addToStoreFromDump(const string & dump, const string & name,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = HashType::SHA256, RepairFlag repair = NoRepair)
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair)
|
||||||
{
|
{
|
||||||
throw Error("addToStoreFromDump() is not supported by this store");
|
throw Error("addToStoreFromDump() is not supported by this store");
|
||||||
}
|
}
|
||||||
|
@ -546,7 +543,7 @@ public:
|
||||||
each path is included. */
|
each path is included. */
|
||||||
void pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & storePaths,
|
void pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & storePaths,
|
||||||
bool includeImpureInfo, bool showClosureSize,
|
bool includeImpureInfo, bool showClosureSize,
|
||||||
Base hashBase = Base::Base32,
|
Base hashBase = Base32,
|
||||||
AllowInvalidFlag allowInvalid = DisallowInvalid);
|
AllowInvalidFlag allowInvalid = DisallowInvalid);
|
||||||
|
|
||||||
/* Return the size of the closure of the specified path, that is,
|
/* Return the size of the closure of the specified path, that is,
|
||||||
|
@ -577,6 +574,9 @@ public:
|
||||||
ensurePath(). */
|
ensurePath(). */
|
||||||
Derivation derivationFromPath(const StorePath & drvPath);
|
Derivation derivationFromPath(const StorePath & drvPath);
|
||||||
|
|
||||||
|
/* Read a derivation (which must already be valid). */
|
||||||
|
Derivation readDerivation(const StorePath & drvPath);
|
||||||
|
|
||||||
/* Place in `out' the set of all store paths in the file system
|
/* Place in `out' the set of all store paths in the file system
|
||||||
closure of `storePath'; that is, all paths than can be directly
|
closure of `storePath'; that is, all paths than can be directly
|
||||||
or indirectly reached from it. `out' is not cleared. If
|
or indirectly reached from it. `out' is not cleared. If
|
||||||
|
@ -722,10 +722,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Extract the hash part of the given store path. */
|
|
||||||
string storePathToHash(const Path & path);
|
|
||||||
|
|
||||||
|
|
||||||
/* Copy a path from one store to another. */
|
/* Copy a path from one store to another. */
|
||||||
void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
|
void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
|
||||||
const StorePath & storePath, RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs);
|
const StorePath & storePath, RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs);
|
||||||
|
|
|
@ -36,7 +36,7 @@ typedef enum {
|
||||||
wopClearFailedPaths = 25,
|
wopClearFailedPaths = 25,
|
||||||
wopQueryPathInfo = 26,
|
wopQueryPathInfo = 26,
|
||||||
wopImportPaths = 27, // obsolete
|
wopImportPaths = 27, // obsolete
|
||||||
wopQueryDerivationOutputNames = 28,
|
wopQueryDerivationOutputNames = 28, // obsolete
|
||||||
wopQueryPathFromHashPart = 29,
|
wopQueryPathFromHashPart = 29,
|
||||||
wopQuerySubstitutablePathInfos = 30,
|
wopQuerySubstitutablePathInfos = 30,
|
||||||
wopQueryValidPaths = 31,
|
wopQueryValidPaths = 31,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue