forked from lix-project/lix
Merge branch 'master' into caveman-LOCs
This commit is contained in:
commit
e6f93b94fc
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)
|
||||||
|
|
||||||
|
|
|
@ -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 ? Base32 : 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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,7 @@ 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));
|
||||||
logError({
|
logError({
|
||||||
.name = "Remote build",
|
.name = "Remote build",
|
||||||
.hint = hintfmt("cannot build on '%s': %s%s",
|
.hint = hintfmt("cannot build on '%s': %s%s",
|
||||||
bestMachine->storeUri, e.what(),
|
bestMachine->storeUri, e.what(),
|
||||||
|
@ -244,7 +244,7 @@ connected:
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -62,7 +71,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
"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,
|
attrPath,
|
||||||
showType(*v));
|
showType(*v));
|
||||||
if (attr.empty())
|
if (attr.empty())
|
||||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||||
|
|
||||||
|
@ -79,7 +88,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
"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,
|
attrPath,
|
||||||
showType(*v));
|
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,11 +76,10 @@ public:
|
||||||
{
|
{
|
||||||
auto a = get(name);
|
auto a = get(name);
|
||||||
if (!a)
|
if (!a)
|
||||||
throw Error(
|
throw Error({
|
||||||
ErrorInfo {
|
.hint = hintfmt("attribute '%s' missing", name),
|
||||||
.hint = hintfmt("attribute '%s' missing", name),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return *a;
|
return *a;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,10 @@ namespace nix {
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
|
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
|
||||||
{
|
{
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(s),
|
||||||
.hint = hintfmt(s),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
||||||
|
@ -24,11 +23,10 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
|
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
|
||||||
{
|
{
|
||||||
throw TypeError(
|
throw TypeError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(s, showType(v)),
|
||||||
.hint = hintfmt(s, showType(v)),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -506,11 +527,10 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2))
|
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2))
|
||||||
{
|
{
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(s, s2),
|
||||||
.hint = hintfmt(s, s2),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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))
|
||||||
|
@ -520,30 +540,27 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, con
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3))
|
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3))
|
||||||
{
|
{
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(s, s2, s3),
|
||||||
.hint = hintfmt(s, s2, s3),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2))
|
LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2))
|
||||||
{
|
{
|
||||||
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(s, sym, p2),
|
||||||
.hint = hintfmt(s, sym, p2),
|
.nixCode = NixCode { .errPos = p1 }
|
||||||
.nixCode = NixCode { .errPos = p1 }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
|
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
|
||||||
{
|
{
|
||||||
throw TypeError(
|
throw TypeError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(s),
|
||||||
.hint = hintfmt(s),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1))
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1))
|
||||||
|
@ -553,29 +570,26 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1))
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
|
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
|
||||||
{
|
{
|
||||||
throw TypeError(
|
throw TypeError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(s, fun.showNamePos(), s2),
|
||||||
.hint = hintfmt(s, fun.showNamePos(), s2),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
|
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
|
||||||
{
|
{
|
||||||
throw AssertionError(
|
throw AssertionError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(s, s1),
|
||||||
.hint = hintfmt(s, s1),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1))
|
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1))
|
||||||
{
|
{
|
||||||
throw UndefinedVarError(
|
throw UndefinedVarError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(s, s1),
|
||||||
.hint = hintfmt(s, s1),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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))
|
||||||
|
@ -1594,7 +1608,7 @@ 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(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
throwEvalError(pos, "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]);
|
||||||
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%')",
|
||||||
|
@ -1920,11 +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(
|
throw TypeError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("cannot coerce %1% to a string", showType()),
|
||||||
.hint = hintfmt("cannot coerce %1% to a string", showType()),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -282,12 +282,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)
|
if (withLevel == -1)
|
||||||
throw UndefinedVarError(
|
throw UndefinedVarError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("undefined variable '%1%'", name),
|
||||||
.hint = hintfmt("undefined variable '%1%'", name),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
fromWith = true;
|
fromWith = true;
|
||||||
this->level = withLevel;
|
this->level = withLevel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,11 +237,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(
|
throw ParseError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("duplicate formal function argument '%1%'", arg),
|
||||||
.hint = hintfmt("duplicate formal function argument '%1%'", arg),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
void setName(Symbol & name);
|
void setName(Symbol & name);
|
||||||
string showNamePos() const;
|
string showNamePos() const;
|
||||||
|
|
|
@ -65,23 +65,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(
|
throw ParseError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("attribute '%1%' already defined at %2%",
|
||||||
.hint = hintfmt("attribute '%1%' already defined at %2%",
|
showAttrPath(attrPath), prevPos),
|
||||||
showAttrPath(attrPath), prevPos),
|
.nixCode = NixCode { .errPos = pos },
|
||||||
.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(
|
throw ParseError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos),
|
||||||
.hint = hintfmt("attribute '%1%' already defined at %2%",
|
.nixCode = NixCode { .errPos = pos },
|
||||||
attr, prevPos),
|
});
|
||||||
.nixCode = NixCode { .errPos = pos },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -149,12 +146,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(
|
throw ParseError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("duplicate formal function argument '%1%'",
|
||||||
.hint = hintfmt("duplicate formal function argument '%1%'",
|
formal.name),
|
||||||
formal.name),
|
.nixCode = NixCode { .errPos = pos },
|
||||||
.nixCode = NixCode { .errPos = pos },
|
});
|
||||||
});
|
|
||||||
formals->formals.push_front(formal);
|
formals->formals.push_front(formal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,10 +258,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 = ErrorInfo {
|
data->error = {
|
||||||
.hint = hintfmt(error),
|
.hint = hintfmt(error),
|
||||||
.nixCode = NixCode { .errPos = makeCurPos(*loc, data) }
|
.nixCode = NixCode { .errPos = makeCurPos(*loc, data) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -342,11 +338,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(
|
throw ParseError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("dynamic attributes not allowed in let"),
|
||||||
.hint = hintfmt("dynamic attributes not allowed in let"),
|
.nixCode = NixCode { .errPos = CUR_POS },
|
||||||
.nixCode = NixCode { .errPos = CUR_POS },
|
});
|
||||||
});
|
|
||||||
$$ = new ExprLet($2, $4);
|
$$ = new ExprLet($2, $4);
|
||||||
}
|
}
|
||||||
| expr_if
|
| expr_if
|
||||||
|
@ -423,11 +418,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(
|
throw ParseError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("URL literals are disabled"),
|
||||||
.hint = hintfmt("URL literals are disabled"),
|
.nixCode = NixCode { .errPos = CUR_POS }
|
||||||
.nixCode = NixCode { .errPos = CUR_POS }
|
});
|
||||||
});
|
|
||||||
$$ = new ExprString(data->symbols.create($1));
|
$$ = new ExprString(data->symbols.create($1));
|
||||||
}
|
}
|
||||||
| '(' expr ')' { $$ = $2; }
|
| '(' expr ')' { $$ = $2; }
|
||||||
|
@ -497,11 +491,10 @@ attrs
|
||||||
$$->push_back(AttrName(str->s));
|
$$->push_back(AttrName(str->s));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
throw ParseError(
|
throw ParseError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("dynamic attributes not allowed in inherit"),
|
||||||
.hint = hintfmt("dynamic attributes not allowed in inherit"),
|
.nixCode = NixCode { .errPos = makeCurPos(@2, data) },
|
||||||
.nixCode = NixCode { .errPos = makeCurPos(@2, data) },
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
| { $$ = new AttrPath; }
|
| { $$ = new AttrPath; }
|
||||||
;
|
;
|
||||||
|
@ -707,11 +700,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);
|
||||||
}
|
}
|
||||||
throw ThrownError(
|
throw ThrownError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(evalSettings.pureEval
|
||||||
.hint = hintfmt("file '%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)", path),
|
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
||||||
.nixCode = NixCode { .errPos = pos }
|
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
||||||
});
|
path),
|
||||||
|
.nixCode = NixCode { .errPos = pos }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -727,10 +722,9 @@ 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) {
|
||||||
logWarning(
|
logWarning({
|
||||||
ErrorInfo {
|
.name = "Entry download",
|
||||||
.name = "Entry download",
|
.hint = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second)
|
||||||
.hint = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second)
|
|
||||||
});
|
});
|
||||||
res = { false, "" };
|
res = { false, "" };
|
||||||
}
|
}
|
||||||
|
@ -739,10 +733,9 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
|
||||||
if (pathExists(path))
|
if (pathExists(path))
|
||||||
res = { true, path };
|
res = { true, path };
|
||||||
else {
|
else {
|
||||||
logWarning(
|
logWarning({
|
||||||
ErrorInfo {
|
.name = "Entry not found",
|
||||||
.name = "Entry not found",
|
.hint = hintfmt("warning: Nix search path entry '%1%' does not exist, ignoring", elem.second)
|
||||||
.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,12 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
.hint = hintfmt("cannot import '%1%', since path '%2%' is not valid",
|
.nixCode = NixCode { .errPos = pos }
|
||||||
path, e.path),
|
});
|
||||||
.nixCode = NixCode { .errPos = pos }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
|
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
|
||||||
|
@ -174,13 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(
|
||||||
.hint = hintfmt(
|
"cannot import '%1%', since path '%2%' is not valid",
|
||||||
"cannot import '%1%', since path '%2%' is not valid",
|
path, e.path),
|
||||||
path, e.path),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
path = state.checkSourcePath(path);
|
path = state.checkSourcePath(path);
|
||||||
|
@ -215,11 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("at least one argument to 'exec' required"),
|
||||||
.hint = hintfmt("at least one argument to 'exec' required"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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);
|
||||||
|
@ -230,12 +227,12 @@ 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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
|
||||||
.hint = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
|
program, e.path),
|
||||||
program, e.path),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});}
|
}
|
||||||
|
|
||||||
auto output = runProgram(program, true, commandArgs);
|
auto output = runProgram(program, true, commandArgs);
|
||||||
Expr * parsed;
|
Expr * parsed;
|
||||||
|
@ -386,11 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("attribute 'startSet' required"),
|
||||||
.hint = hintfmt("attribute 'startSet' required"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
state.forceList(*startSet->value, pos);
|
state.forceList(*startSet->value, pos);
|
||||||
|
|
||||||
ValueList workSet;
|
ValueList workSet;
|
||||||
|
@ -401,11 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("attribute 'operator' required"),
|
||||||
.hint = hintfmt("attribute 'operator' required"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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
|
||||||
|
@ -424,11 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("attribute 'key' required"),
|
||||||
.hint = hintfmt("attribute 'key' required"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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;
|
||||||
|
@ -560,11 +554,10 @@ 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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("required attribute 'name' missing"),
|
||||||
.hint = hintfmt("required attribute 'name' missing"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
string drvName;
|
string drvName;
|
||||||
Pos & posDrvName(*attr->pos);
|
Pos & posDrvName(*attr->pos);
|
||||||
try {
|
try {
|
||||||
|
@ -607,42 +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
|
else
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
|
||||||
.hint = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
.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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("duplicate derivation output '%1%'", j),
|
||||||
.hint = hintfmt("duplicate derivation output '%1%'", j),
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
.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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("invalid derivation output name 'drv'" ),
|
||||||
.hint = hintfmt("invalid derivation output name 'drv'" ),
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
.nixCode = NixCode { .errPos = posDrvName }
|
});
|
||||||
});
|
|
||||||
outputs.insert(j);
|
outputs.insert(j);
|
||||||
}
|
}
|
||||||
if (outputs.empty())
|
if (outputs.empty())
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("derivation cannot have an empty set of outputs"),
|
||||||
.hint = hintfmt("derivation cannot have an empty set of outputs"),
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
.nixCode = NixCode { .errPos = posDrvName }
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -735,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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,38 +743,35 @@ 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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("required attribute 'builder' missing"),
|
||||||
.hint = hintfmt("required attribute 'builder' missing"),
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
.nixCode = NixCode { .errPos = posDrvName }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
if (drv.platform == "")
|
if (drv.platform == "")
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("required attribute 'system' missing"),
|
||||||
.hint = hintfmt("required attribute 'system' missing"),
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
.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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("derivation names are not allowed to end in '%s'", drvExtension),
|
||||||
.hint = hintfmt("derivation names are not allowed to end in '%s'", drvExtension),
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
.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(
|
throw Error({
|
||||||
ErrorInfo {
|
.hint = hintfmt("multiple outputs are not supported in fixed-output derivations"),
|
||||||
.hint = hintfmt("multiple outputs are not supported in fixed-output derivations"),
|
.nixCode = NixCode { .errPos = posDrvName }
|
||||||
.nixCode = NixCode { .errPos = posDrvName }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
HashType ht = outputHashAlgo.empty() ? htUnknown : parseHashType(outputHashAlgo);
|
HashType ht = outputHashAlgo.empty() ? htUnknown : parseHashType(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);
|
||||||
|
@ -807,7 +793,7 @@ 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(), "", ""));
|
DerivationOutput { StorePath::dummy, "", "" });
|
||||||
}
|
}
|
||||||
|
|
||||||
Hash h = hashDerivationModulo(*state.store, Derivation(drv), true);
|
Hash h = hashDerivationModulo(*state.store, Derivation(drv), true);
|
||||||
|
@ -816,7 +802,7 @@ 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), "", ""));
|
DerivationOutput { std::move(outPath), "", "" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,7 +815,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
/* 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());
|
||||||
|
@ -886,11 +872,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("path '%1%' is not in the Nix store", path),
|
||||||
.hint = hintfmt("path '%1%' is not in the Nix store", path),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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));
|
||||||
|
@ -906,13 +891,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(
|
||||||
.hint = hintfmt(
|
"cannot check the existence of '%1%', since path '%2%' is not valid",
|
||||||
"cannot check the existence of '%1%', since path '%2%' is not valid",
|
path, e.path),
|
||||||
path, e.path),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -955,13 +939,11 @@ 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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid"
|
.nixCode = NixCode { .errPos = pos }
|
||||||
, 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("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);
|
||||||
|
@ -988,11 +970,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("attribute 'path' missing"),
|
||||||
.hint = hintfmt("attribute 'path' missing"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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);
|
||||||
|
@ -1000,12 +981,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
.hint = hintfmt("cannot find '%1%', since path '%2%' is not valid",
|
.nixCode = NixCode { .errPos = pos }
|
||||||
path, e.path),
|
});
|
||||||
.nixCode = NixCode { .errPos = pos }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
searchPath.emplace_back(prefix, path);
|
searchPath.emplace_back(prefix, path);
|
||||||
|
@ -1022,11 +1001,10 @@ 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);
|
||||||
HashType ht = parseHashType(type);
|
HashType ht = parseHashType(type);
|
||||||
if (ht == htUnknown)
|
if (ht == htUnknown)
|
||||||
throw Error(
|
throw Error({
|
||||||
ErrorInfo {
|
.hint = hintfmt("unknown hash type '%1%'", type),
|
||||||
.hint = hintfmt("unknown hash type '%1%'", type),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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);
|
||||||
|
@ -1042,12 +1020,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid",
|
.nixCode = NixCode { .errPos = pos }
|
||||||
path, e.path),
|
});
|
||||||
.nixCode = NixCode { .errPos = pos }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DirEntries entries = readDirectory(state.checkSourcePath(path));
|
DirEntries entries = readDirectory(state.checkSourcePath(path));
|
||||||
|
@ -1117,15 +1093,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(
|
throw EvalError( {
|
||||||
ErrorInfo {
|
.hint = hintfmt(
|
||||||
.hint = hintfmt(
|
"in 'toFile': the file named '%1%' must not contain a reference "
|
||||||
"in 'toFile': the file named '%1%' must not contain a reference "
|
"to a derivation but contains (%2%)",
|
||||||
"to a derivation but contains (%2%)",
|
name, path),
|
||||||
name,
|
.nixCode = NixCode { .errPos = pos }
|
||||||
path),
|
});
|
||||||
.nixCode = NixCode { .errPos = pos }
|
|
||||||
});
|
|
||||||
refs.insert(state.store->parseStorePath(path));
|
refs.insert(state.store->parseStorePath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1193,21 +1167,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("string '%1%' cannot refer to other paths", path),
|
||||||
.hint = hintfmt("string '%1%' cannot refer to other paths", path),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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(
|
throw TypeError({
|
||||||
ErrorInfo {
|
.hint = hintfmt(
|
||||||
.hint = hintfmt(
|
"first argument in call to 'filterSource' is not a function but %1%",
|
||||||
"first argument in call to 'filterSource' is not a function but %1%",
|
showType(*args[0])),
|
||||||
showType(*args[0])),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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);
|
||||||
}
|
}
|
||||||
|
@ -1227,12 +1199,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("string '%1%' cannot refer to other paths", path),
|
||||||
.hint = hintfmt("string '%1%' cannot refer to other paths",
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
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") {
|
||||||
|
@ -1241,21 +1211,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), htSHA256);
|
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||||
else
|
else
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name),
|
||||||
.hint = hintfmt("unsupported argument '%1%' to 'addPath'",
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
attr.name),
|
});
|
||||||
.nixCode = NixCode { .errPos = *attr.pos }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("'path' required"),
|
||||||
.hint = hintfmt("'path' required"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
name = baseNameOf(path);
|
name = baseNameOf(path);
|
||||||
|
|
||||||
|
@ -1313,11 +1280,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("attribute '%1%' missing", attr),
|
||||||
.hint = hintfmt("attribute '%1%' missing", attr),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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);
|
||||||
|
@ -1397,22 +1363,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(
|
throw TypeError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("'name' attribute missing in a call to 'listToAttrs'"),
|
||||||
.hint = hintfmt("'name' attribute missing in a call to 'listToAttrs'"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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(
|
throw TypeError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("'value' attribute missing in a call to 'listToAttrs'"),
|
||||||
.hint = hintfmt("'value' attribute missing in a call to 'listToAttrs'"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1485,11 +1449,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(
|
throw TypeError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("'functionArgs' requires a function"),
|
||||||
.hint = hintfmt("'functionArgs' requires a function"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
if (!args[0]->lambda.fun->matchAttrs) {
|
if (!args[0]->lambda.fun->matchAttrs) {
|
||||||
state.mkAttrs(v, 0);
|
state.mkAttrs(v, 0);
|
||||||
|
@ -1542,11 +1505,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(
|
throw Error({
|
||||||
ErrorInfo {
|
.hint = hintfmt("list index %1% is out of bounds", n),
|
||||||
.hint = hintfmt("list index %1% is out of bounds", n),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
state.forceValue(*list.listElems()[n], pos);
|
state.forceValue(*list.listElems()[n], pos);
|
||||||
v = *list.listElems()[n];
|
v = *list.listElems()[n];
|
||||||
}
|
}
|
||||||
|
@ -1573,11 +1535,10 @@ 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(
|
throw Error({
|
||||||
ErrorInfo {
|
.hint = hintfmt("'tail' called on an empty list"),
|
||||||
.hint = hintfmt("'tail' called on an empty list"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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)
|
||||||
|
@ -1719,12 +1680,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("cannot create list of size %1%", len),
|
||||||
.hint = hintfmt("cannot create list of size %1%", len),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
state.mkList(v, len);
|
state.mkList(v, len);
|
||||||
|
|
||||||
|
@ -1882,12 +1841,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)
|
if (f2 == 0)
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("division by zero"),
|
||||||
.hint = hintfmt("division by zero"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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));
|
||||||
|
@ -1896,11 +1854,10 @@ 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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("overflow in integer division"),
|
||||||
.hint = hintfmt("overflow in integer division"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
mkInt(v, i1 / i2);
|
mkInt(v, i1 / i2);
|
||||||
}
|
}
|
||||||
|
@ -1957,12 +1914,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)
|
if (start < 0)
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("negative start position in 'substring'"),
|
||||||
.hint = hintfmt("negative start position in 'substring'"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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);
|
||||||
}
|
}
|
||||||
|
@ -1982,11 +1938,10 @@ 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);
|
||||||
HashType ht = parseHashType(type);
|
HashType ht = parseHashType(type);
|
||||||
if (ht == htUnknown)
|
if (ht == htUnknown)
|
||||||
throw Error(
|
throw Error({
|
||||||
ErrorInfo {
|
.hint = hintfmt("unknown hash type '%1%'", type),
|
||||||
.hint = hintfmt("unknown hash type '%1%'", type),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.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);
|
||||||
|
@ -2029,17 +1984,15 @@ 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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
|
||||||
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("invalid regular expression '%s'", re),
|
||||||
.hint = hintfmt("invalid regular expression '%s'", re),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2104,17 +2057,15 @@ 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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
|
||||||
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("invalid regular expression '%s'", re),
|
||||||
.hint = hintfmt("invalid regular expression '%s'", re),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2145,11 +2096,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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
|
||||||
.hint = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
vector<string> from;
|
vector<string> from;
|
||||||
from.reserve(args[0]->listSize());
|
from.reserve(args[0]->listSize());
|
||||||
|
@ -2252,10 +2202,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});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2447,7 +2398,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,11 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("Context key '%s' is not a store path", i.name),
|
||||||
.hint = hintfmt("Context key '%s' is not a store path", i.name),
|
.nixCode = NixCode { .errPos = *i.pos }
|
||||||
.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);
|
||||||
|
@ -164,11 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
|
||||||
.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 }
|
||||||
.nixCode = NixCode { .errPos = *i.pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
context.insert("=" + string(i.name));
|
context.insert("=" + string(i.name));
|
||||||
}
|
}
|
||||||
|
@ -178,11 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
|
||||||
.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 }
|
||||||
.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);
|
||||||
|
|
|
@ -35,19 +35,17 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("unsupported argument '%s' to 'fetchGit'", attr.name),
|
||||||
.hint = hintfmt("unsupported argument '%s' to 'fetchGit'", attr.name),
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
.nixCode = NixCode { .errPos = *attr.pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.empty())
|
if (url.empty())
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("'url' argument required"),
|
||||||
.hint = hintfmt("'url' argument required"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
} else
|
} else
|
||||||
url = state.coerceToString(pos, *args[0], context, false, false);
|
url = state.coerceToString(pos, *args[0], context, false, false);
|
||||||
|
|
|
@ -38,19 +38,17 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
|
||||||
.hint = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
.nixCode = NixCode { .errPos = *attr.pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.empty())
|
if (url.empty())
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("'url' argument required"),
|
||||||
.hint = hintfmt("'url' argument required"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
} else
|
} else
|
||||||
url = state.coerceToString(pos, *args[0], context, false, false);
|
url = state.coerceToString(pos, *args[0], context, false, false);
|
||||||
|
|
|
@ -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(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,11 +66,10 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!attrs.count("type"))
|
if (!attrs.count("type"))
|
||||||
throw Error(
|
throw Error({
|
||||||
ErrorInfo {
|
.hint = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||||
.hint = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
input = fetchers::inputFromAttrs(attrs);
|
input = fetchers::inputFromAttrs(attrs);
|
||||||
} else
|
} else
|
||||||
|
@ -107,24 +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), htSHA256);
|
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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
|
||||||
.hint = hintfmt("unsupported argument '%s' to '%s'",
|
.nixCode = NixCode { .errPos = *attr.pos }
|
||||||
attr.name, who),
|
});
|
||||||
.nixCode = NixCode { .errPos = *attr.pos }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!url)
|
if (!url)
|
||||||
throw EvalError(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("'url' argument required"),
|
||||||
.hint = hintfmt("'url' argument required"),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
} else
|
} else
|
||||||
url = state.forceStringNoCtx(*args[0], pos);
|
url = state.forceStringNoCtx(*args[0], pos);
|
||||||
|
|
||||||
|
@ -151,7 +147,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
: hashFile(htSHA256, 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,11 +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(
|
throw EvalError({
|
||||||
ErrorInfo {
|
.hint = hintfmt("while parsing a TOML string: %s", e.what()),
|
||||||
.hint = hintfmt("while parsing a TOML string: %s", e.what()),
|
.nixCode = NixCode { .errPos = pos }
|
||||||
.nixCode = NixCode { .errPos = pos }
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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, htUnknown);
|
||||||
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(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(SRI), input->narHash->to_string(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};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"], htSHA1);
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -196,9 +196,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(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(SRI));
|
url2.query.insert_or_assign("hash", hash->to_string(SRI, true));
|
||||||
return url2;
|
return url2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +207,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(SRI));
|
attrs.emplace("hash", hash->to_string(SRI, true));
|
||||||
return attrs;
|
return attrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,8 +264,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, htUnknown);
|
||||||
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 {
|
||||||
|
|
||||||
|
@ -38,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();
|
||||||
|
|
||||||
|
}
|
|
@ -106,19 +106,20 @@ 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());
|
||||||
|
@ -129,7 +130,7 @@ public:
|
||||||
{
|
{
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
|
|
||||||
std::stringstream oss;
|
std::stringstream oss;
|
||||||
oss << ei;
|
oss << ei;
|
||||||
|
|
||||||
log(*state, ei.level, oss.str());
|
log(*state, ei.level, oss.str());
|
||||||
|
@ -152,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());
|
||||||
|
@ -164,7 +165,7 @@ public:
|
||||||
state->activitiesByType[type].its.emplace(act, i);
|
state->activitiesByType[type].its.emplace(act, i);
|
||||||
|
|
||||||
if (type == actBuild) {
|
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);
|
||||||
|
@ -467,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)));
|
||||||
}
|
}
|
||||||
|
@ -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({
|
||||||
|
@ -289,7 +302,7 @@ 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 = programName;
|
ErrorInfo::programName = baseNameOf(programName);
|
||||||
|
|
||||||
string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -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());
|
||||||
|
@ -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. */
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ struct HookInstance;
|
||||||
|
|
||||||
|
|
||||||
/* A pointer to a goal. */
|
/* A pointer to a goal. */
|
||||||
class Goal;
|
struct Goal;
|
||||||
class DerivationGoal;
|
class DerivationGoal;
|
||||||
typedef std::shared_ptr<Goal> GoalPtr;
|
typedef std::shared_ptr<Goal> GoalPtr;
|
||||||
typedef std::weak_ptr<Goal> WeakGoalPtr;
|
typedef std::weak_ptr<Goal> WeakGoalPtr;
|
||||||
|
@ -104,13 +104,10 @@ typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Goal : public std::enable_shared_from_this<Goal>
|
struct Goal : public std::enable_shared_from_this<Goal>
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
|
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/* Backlink to the worker. */
|
/* Backlink to the worker. */
|
||||||
Worker & worker;
|
Worker & worker;
|
||||||
|
|
||||||
|
@ -138,6 +135,9 @@ protected:
|
||||||
/* Whether the goal is finished. */
|
/* Whether the goal is finished. */
|
||||||
ExitCode exitCode;
|
ExitCode exitCode;
|
||||||
|
|
||||||
|
/* Exception containing an error message, if any. */
|
||||||
|
std::optional<Error> ex;
|
||||||
|
|
||||||
Goal(Worker & worker) : worker(worker)
|
Goal(Worker & worker) : worker(worker)
|
||||||
{
|
{
|
||||||
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
|
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
|
||||||
|
@ -149,7 +149,6 @@ protected:
|
||||||
trace("goal destroyed");
|
trace("goal destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void work() = 0;
|
virtual void work() = 0;
|
||||||
|
|
||||||
void addWaitee(GoalPtr waitee);
|
void addWaitee(GoalPtr waitee);
|
||||||
|
@ -173,21 +172,14 @@ public:
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitCode getExitCode()
|
|
||||||
{
|
|
||||||
return exitCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback in case of a timeout. It should wake up its waiters,
|
/* Callback in case of a timeout. It should wake up its waiters,
|
||||||
get rid of any running child processes that are being monitored
|
get rid of any running child processes that are being monitored
|
||||||
by the worker (important!), etc. */
|
by the worker (important!), etc. */
|
||||||
virtual void timedOut() = 0;
|
virtual void timedOut(Error && ex) = 0;
|
||||||
|
|
||||||
virtual string key() = 0;
|
virtual string key() = 0;
|
||||||
|
|
||||||
protected:
|
void amDone(ExitCode result, std::optional<Error> ex = {});
|
||||||
|
|
||||||
virtual void amDone(ExitCode result);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -303,7 +295,7 @@ public:
|
||||||
|
|
||||||
/* Make a goal (with caching). */
|
/* Make a goal (with caching). */
|
||||||
GoalPtr makeDerivationGoal(const StorePath & drvPath, const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
GoalPtr makeDerivationGoal(const StorePath & drvPath, const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||||
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(StorePath && drvPath,
|
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(const StorePath & drvPath,
|
||||||
const BasicDerivation & drv, BuildMode buildMode = bmNormal);
|
const BasicDerivation & drv, BuildMode buildMode = bmNormal);
|
||||||
GoalPtr makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair);
|
GoalPtr makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair);
|
||||||
|
|
||||||
|
@ -355,7 +347,7 @@ public:
|
||||||
contents. */
|
contents. */
|
||||||
bool pathContentsGood(const StorePath & path);
|
bool pathContentsGood(const StorePath & path);
|
||||||
|
|
||||||
void markContentsGood(StorePath && path);
|
void markContentsGood(const StorePath & path);
|
||||||
|
|
||||||
void updateProgress()
|
void updateProgress()
|
||||||
{
|
{
|
||||||
|
@ -392,8 +384,7 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
|
||||||
assert(waitees.find(waitee) != waitees.end());
|
assert(waitees.find(waitee) != waitees.end());
|
||||||
waitees.erase(waitee);
|
waitees.erase(waitee);
|
||||||
|
|
||||||
trace(format("waitee '%1%' done; %2% left") %
|
trace(fmt("waitee '%s' done; %d left", waitee->name, waitees.size()));
|
||||||
waitee->name % waitees.size());
|
|
||||||
|
|
||||||
if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed;
|
if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed;
|
||||||
|
|
||||||
|
@ -418,12 +409,20 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Goal::amDone(ExitCode result)
|
void Goal::amDone(ExitCode result, std::optional<Error> ex)
|
||||||
{
|
{
|
||||||
trace("done");
|
trace("done");
|
||||||
assert(exitCode == ecBusy);
|
assert(exitCode == ecBusy);
|
||||||
assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure);
|
assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure);
|
||||||
exitCode = result;
|
exitCode = result;
|
||||||
|
|
||||||
|
if (ex) {
|
||||||
|
if (!waiters.empty())
|
||||||
|
logError(ex->info());
|
||||||
|
else
|
||||||
|
this->ex = std::move(*ex);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto & i : waiters) {
|
for (auto & i : waiters) {
|
||||||
GoalPtr goal = i.lock();
|
GoalPtr goal = i.lock();
|
||||||
if (goal) goal->waiteeDone(shared_from_this(), result);
|
if (goal) goal->waiteeDone(shared_from_this(), result);
|
||||||
|
@ -869,6 +868,9 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<Activity> act;
|
std::unique_ptr<Activity> act;
|
||||||
|
|
||||||
|
/* Activity that denotes waiting for a lock. */
|
||||||
|
std::unique_ptr<Activity> actLock;
|
||||||
|
|
||||||
std::map<ActivityId, Activity> builderActivities;
|
std::map<ActivityId, Activity> builderActivities;
|
||||||
|
|
||||||
/* The remote machine on which we're building. */
|
/* The remote machine on which we're building. */
|
||||||
|
@ -898,16 +900,16 @@ private:
|
||||||
friend struct RestrictedStore;
|
friend struct RestrictedStore;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DerivationGoal(StorePath && drvPath, const StringSet & wantedOutputs,
|
DerivationGoal(const StorePath & drvPath, const StringSet & wantedOutputs,
|
||||||
Worker & worker, BuildMode buildMode = bmNormal);
|
Worker & worker, BuildMode buildMode = bmNormal);
|
||||||
DerivationGoal(StorePath && drvPath, const BasicDerivation & drv,
|
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
Worker & worker, BuildMode buildMode = bmNormal);
|
Worker & worker, BuildMode buildMode = bmNormal);
|
||||||
~DerivationGoal();
|
~DerivationGoal();
|
||||||
|
|
||||||
/* Whether we need to perform hash rewriting if there are valid output paths. */
|
/* Whether we need to perform hash rewriting if there are valid output paths. */
|
||||||
bool needsHashRewrite();
|
bool needsHashRewrite();
|
||||||
|
|
||||||
void timedOut() override;
|
void timedOut(Error && ex) override;
|
||||||
|
|
||||||
string key() override
|
string key() override
|
||||||
{
|
{
|
||||||
|
@ -922,7 +924,7 @@ public:
|
||||||
|
|
||||||
StorePath getDrvPath()
|
StorePath getDrvPath()
|
||||||
{
|
{
|
||||||
return drvPath.clone();
|
return drvPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add wanted outputs to an already existing derivation goal. */
|
/* Add wanted outputs to an already existing derivation goal. */
|
||||||
|
@ -1006,14 +1008,11 @@ private:
|
||||||
|
|
||||||
void repairClosure();
|
void repairClosure();
|
||||||
|
|
||||||
void amDone(ExitCode result) override
|
|
||||||
{
|
|
||||||
Goal::amDone(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void started();
|
void started();
|
||||||
|
|
||||||
void done(BuildResult::Status status, const string & msg = "");
|
void done(
|
||||||
|
BuildResult::Status status,
|
||||||
|
std::optional<Error> ex = {});
|
||||||
|
|
||||||
StorePathSet exportReferences(const StorePathSet & storePaths);
|
StorePathSet exportReferences(const StorePathSet & storePaths);
|
||||||
};
|
};
|
||||||
|
@ -1022,11 +1021,11 @@ private:
|
||||||
const Path DerivationGoal::homeDir = "/homeless-shelter";
|
const Path DerivationGoal::homeDir = "/homeless-shelter";
|
||||||
|
|
||||||
|
|
||||||
DerivationGoal::DerivationGoal(StorePath && drvPath, const StringSet & wantedOutputs,
|
DerivationGoal::DerivationGoal(const StorePath & drvPath, const StringSet & wantedOutputs,
|
||||||
Worker & worker, BuildMode buildMode)
|
Worker & worker, BuildMode buildMode)
|
||||||
: Goal(worker)
|
: Goal(worker)
|
||||||
, useDerivation(true)
|
, useDerivation(true)
|
||||||
, drvPath(std::move(drvPath))
|
, drvPath(drvPath)
|
||||||
, wantedOutputs(wantedOutputs)
|
, wantedOutputs(wantedOutputs)
|
||||||
, buildMode(buildMode)
|
, buildMode(buildMode)
|
||||||
{
|
{
|
||||||
|
@ -1039,11 +1038,11 @@ DerivationGoal::DerivationGoal(StorePath && drvPath, const StringSet & wantedOut
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DerivationGoal::DerivationGoal(StorePath && drvPath, const BasicDerivation & drv,
|
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
Worker & worker, BuildMode buildMode)
|
Worker & worker, BuildMode buildMode)
|
||||||
: Goal(worker)
|
: Goal(worker)
|
||||||
, useDerivation(false)
|
, useDerivation(false)
|
||||||
, drvPath(std::move(drvPath))
|
, drvPath(drvPath)
|
||||||
, buildMode(buildMode)
|
, buildMode(buildMode)
|
||||||
{
|
{
|
||||||
this->drv = std::make_unique<BasicDerivation>(BasicDerivation(drv));
|
this->drv = std::make_unique<BasicDerivation>(BasicDerivation(drv));
|
||||||
|
@ -1107,10 +1106,10 @@ void DerivationGoal::killChild()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::timedOut()
|
void DerivationGoal::timedOut(Error && ex)
|
||||||
{
|
{
|
||||||
killChild();
|
killChild();
|
||||||
done(BuildResult::TimedOut);
|
done(BuildResult::TimedOut, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1158,11 +1157,7 @@ void DerivationGoal::loadDerivation()
|
||||||
trace("loading derivation");
|
trace("loading derivation");
|
||||||
|
|
||||||
if (nrFailed != 0) {
|
if (nrFailed != 0) {
|
||||||
logError({
|
done(BuildResult::MiscFailure, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
|
||||||
.name = "missing derivation during build",
|
|
||||||
.hint = hintfmt("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))
|
|
||||||
});
|
|
||||||
done(BuildResult::MiscFailure);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1198,7 +1193,13 @@ void DerivationGoal::haveDerivation()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedDrv = std::make_unique<ParsedDerivation>(drvPath.clone(), *drv);
|
parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
|
||||||
|
|
||||||
|
if (parsedDrv->contentAddressed()) {
|
||||||
|
settings.requireExperimentalFeature("ca-derivations");
|
||||||
|
throw Error("ca-derivations isn't implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* We are first going to try to create the invalid output paths
|
/* We are first going to try to create the invalid output paths
|
||||||
through substitutes. If that doesn't work, we'll build
|
through substitutes. If that doesn't work, we'll build
|
||||||
|
@ -1306,7 +1307,7 @@ void DerivationGoal::repairClosure()
|
||||||
if (i.isDerivation()) {
|
if (i.isDerivation()) {
|
||||||
Derivation drv = worker.store.derivationFromPath(i);
|
Derivation drv = worker.store.derivationFromPath(i);
|
||||||
for (auto & j : drv.outputs)
|
for (auto & j : drv.outputs)
|
||||||
outputsToDrv.insert_or_assign(j.second.path.clone(), i.clone());
|
outputsToDrv.insert_or_assign(j.second.path, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check each path (slow!). */
|
/* Check each path (slow!). */
|
||||||
|
@ -1351,13 +1352,9 @@ void DerivationGoal::inputsRealised()
|
||||||
if (nrFailed != 0) {
|
if (nrFailed != 0) {
|
||||||
if (!useDerivation)
|
if (!useDerivation)
|
||||||
throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath));
|
throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath));
|
||||||
logError({
|
done(BuildResult::DependencyFailed, Error(
|
||||||
.name = "Dependencies could not be built",
|
"%s dependencies of derivation '%s' failed to build",
|
||||||
.hint = hintfmt(
|
nrFailed, worker.store.printStorePath(drvPath)));
|
||||||
"cannot build derivation '%s': %s dependencies couldn't be built",
|
|
||||||
worker.store.printStorePath(drvPath), nrFailed)
|
|
||||||
});
|
|
||||||
done(BuildResult::DependencyFailed);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1439,10 +1436,15 @@ void DerivationGoal::tryToBuild()
|
||||||
lockFiles.insert(worker.store.Store::toRealPath(outPath));
|
lockFiles.insert(worker.store.Store::toRealPath(outPath));
|
||||||
|
|
||||||
if (!outputLocks.lockPaths(lockFiles, "", false)) {
|
if (!outputLocks.lockPaths(lockFiles, "", false)) {
|
||||||
|
if (!actLock)
|
||||||
|
actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting,
|
||||||
|
fmt("waiting for lock on %s", yellowtxt(showPaths(lockFiles))));
|
||||||
worker.waitForAWhile(shared_from_this());
|
worker.waitForAWhile(shared_from_this());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actLock.reset();
|
||||||
|
|
||||||
/* Now check again whether the outputs are valid. This is because
|
/* Now check again whether the outputs are valid. This is because
|
||||||
another process may have started building in parallel. After
|
another process may have started building in parallel. After
|
||||||
it has finished and released the locks, we can (and should)
|
it has finished and released the locks, we can (and should)
|
||||||
|
@ -1458,7 +1460,7 @@ void DerivationGoal::tryToBuild()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
missingPaths = cloneStorePathSet(drv->outputPaths());
|
missingPaths = drv->outputPaths();
|
||||||
if (buildMode != bmCheck)
|
if (buildMode != bmCheck)
|
||||||
for (auto & i : validPaths) missingPaths.erase(i);
|
for (auto & i : validPaths) missingPaths.erase(i);
|
||||||
|
|
||||||
|
@ -1481,6 +1483,7 @@ void DerivationGoal::tryToBuild()
|
||||||
case rpAccept:
|
case rpAccept:
|
||||||
/* Yes, it has started doing so. Wait until we get
|
/* Yes, it has started doing so. Wait until we get
|
||||||
EOF from the hook. */
|
EOF from the hook. */
|
||||||
|
actLock.reset();
|
||||||
result.startTime = time(0); // inexact
|
result.startTime = time(0); // inexact
|
||||||
state = &DerivationGoal::buildDone;
|
state = &DerivationGoal::buildDone;
|
||||||
started();
|
started();
|
||||||
|
@ -1488,6 +1491,9 @@ void DerivationGoal::tryToBuild()
|
||||||
case rpPostpone:
|
case rpPostpone:
|
||||||
/* Not now; wait until at least one child finishes or
|
/* Not now; wait until at least one child finishes or
|
||||||
the wake-up timeout expires. */
|
the wake-up timeout expires. */
|
||||||
|
if (!actLock)
|
||||||
|
actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting,
|
||||||
|
fmt("waiting for a machine to build '%s'", yellowtxt(worker.store.printStorePath(drvPath))));
|
||||||
worker.waitForAWhile(shared_from_this());
|
worker.waitForAWhile(shared_from_this());
|
||||||
outputLocks.unlock();
|
outputLocks.unlock();
|
||||||
return;
|
return;
|
||||||
|
@ -1497,6 +1503,8 @@ void DerivationGoal::tryToBuild()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actLock.reset();
|
||||||
|
|
||||||
/* Make sure that we are allowed to start a build. If this
|
/* Make sure that we are allowed to start a build. If this
|
||||||
derivation prefers to be done locally, do it even if
|
derivation prefers to be done locally, do it even if
|
||||||
maxBuildJobs is 0. */
|
maxBuildJobs is 0. */
|
||||||
|
@ -1524,7 +1532,9 @@ void DerivationGoal::tryLocalBuild() {
|
||||||
uid. */
|
uid. */
|
||||||
buildUser->kill();
|
buildUser->kill();
|
||||||
} else {
|
} else {
|
||||||
debug("waiting for build users");
|
if (!actLock)
|
||||||
|
actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting,
|
||||||
|
fmt("waiting for UID to build '%s'", yellowtxt(worker.store.printStorePath(drvPath))));
|
||||||
worker.waitForAWhile(shared_from_this());
|
worker.waitForAWhile(shared_from_this());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1535,17 +1545,18 @@ void DerivationGoal::tryLocalBuild() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actLock.reset();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
/* Okay, we have to build. */
|
/* Okay, we have to build. */
|
||||||
startBuilder();
|
startBuilder();
|
||||||
|
|
||||||
} catch (BuildError & e) {
|
} catch (BuildError & e) {
|
||||||
logError(e.info());
|
|
||||||
outputLocks.unlock();
|
outputLocks.unlock();
|
||||||
buildUser.reset();
|
buildUser.reset();
|
||||||
worker.permanentFailure = true;
|
worker.permanentFailure = true;
|
||||||
done(BuildResult::InputRejected, e.msg());
|
done(BuildResult::InputRejected, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1657,10 +1668,10 @@ void DerivationGoal::buildDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
auto msg = fmt("builder for '%s' %s",
|
auto msg = fmt("builder for '%s' %s",
|
||||||
worker.store.printStorePath(drvPath),
|
yellowtxt(worker.store.printStorePath(drvPath)),
|
||||||
statusToString(status));
|
statusToString(status));
|
||||||
|
|
||||||
if (!settings.verboseBuild && !logTail.empty()) {
|
if (!logger->isVerbose() && !logTail.empty()) {
|
||||||
msg += (format("; last %d log lines:") % logTail.size()).str();
|
msg += (format("; last %d log lines:") % logTail.size()).str();
|
||||||
for (auto & line : logTail)
|
for (auto & line : logTail)
|
||||||
msg += "\n " + line;
|
msg += "\n " + line;
|
||||||
|
@ -1709,11 +1720,7 @@ void DerivationGoal::buildDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
void flushLine() {
|
void flushLine() {
|
||||||
if (settings.verboseBuild) {
|
act.result(resPostBuildLogLine, currentLine);
|
||||||
printError("post-build-hook: " + currentLine);
|
|
||||||
} else {
|
|
||||||
act.result(resPostBuildLogLine, currentLine);
|
|
||||||
}
|
|
||||||
currentLine.clear();
|
currentLine.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1762,8 +1769,6 @@ void DerivationGoal::buildDone()
|
||||||
outputLocks.unlock();
|
outputLocks.unlock();
|
||||||
|
|
||||||
} catch (BuildError & e) {
|
} catch (BuildError & e) {
|
||||||
logError(e.info());
|
|
||||||
|
|
||||||
outputLocks.unlock();
|
outputLocks.unlock();
|
||||||
|
|
||||||
BuildResult::Status st = BuildResult::MiscFailure;
|
BuildResult::Status st = BuildResult::MiscFailure;
|
||||||
|
@ -1782,7 +1787,7 @@ void DerivationGoal::buildDone()
|
||||||
BuildResult::PermanentFailure;
|
BuildResult::PermanentFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
done(st, e.msg());
|
done(st, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1902,14 +1907,14 @@ StorePathSet DerivationGoal::exportReferences(const StorePathSet & storePaths)
|
||||||
if (!inputPaths.count(storePath))
|
if (!inputPaths.count(storePath))
|
||||||
throw BuildError("cannot export references of path '%s' because it is not in the input closure of the derivation", worker.store.printStorePath(storePath));
|
throw BuildError("cannot export references of path '%s' because it is not in the input closure of the derivation", worker.store.printStorePath(storePath));
|
||||||
|
|
||||||
worker.store.computeFSClosure(singleton(storePath), paths);
|
worker.store.computeFSClosure({storePath}, paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If there are derivations in the graph, then include their
|
/* If there are derivations in the graph, then include their
|
||||||
outputs as well. This is useful if you want to do things
|
outputs as well. This is useful if you want to do things
|
||||||
like passing all build-time dependencies of some path to a
|
like passing all build-time dependencies of some path to a
|
||||||
derivation that builds a NixOS DVD image. */
|
derivation that builds a NixOS DVD image. */
|
||||||
auto paths2 = cloneStorePathSet(paths);
|
auto paths2 = paths;
|
||||||
|
|
||||||
for (auto & j : paths2) {
|
for (auto & j : paths2) {
|
||||||
if (j.isDerivation()) {
|
if (j.isDerivation()) {
|
||||||
|
@ -2038,7 +2043,7 @@ void DerivationGoal::startBuilder()
|
||||||
/* Write closure info to <fileName>. */
|
/* Write closure info to <fileName>. */
|
||||||
writeFile(tmpDir + "/" + fileName,
|
writeFile(tmpDir + "/" + fileName,
|
||||||
worker.store.makeValidityRegistration(
|
worker.store.makeValidityRegistration(
|
||||||
exportReferences(singleton(storePath)), false, false));
|
exportReferences({storePath}), false, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2223,7 +2228,7 @@ void DerivationGoal::startBuilder()
|
||||||
for (auto & i : missingPaths)
|
for (auto & i : missingPaths)
|
||||||
if (worker.store.isValidPath(i) && pathExists(worker.store.printStorePath(i))) {
|
if (worker.store.isValidPath(i) && pathExists(worker.store.printStorePath(i))) {
|
||||||
addHashRewrite(i);
|
addHashRewrite(i);
|
||||||
redirectedBadOutputs.insert(i.clone());
|
redirectedBadOutputs.insert(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2718,8 +2723,8 @@ struct RestrictedStore : public LocalFSStore
|
||||||
StorePathSet queryAllValidPaths() override
|
StorePathSet queryAllValidPaths() override
|
||||||
{
|
{
|
||||||
StorePathSet paths;
|
StorePathSet paths;
|
||||||
for (auto & p : goal.inputPaths) paths.insert(p.clone());
|
for (auto & p : goal.inputPaths) paths.insert(p);
|
||||||
for (auto & p : goal.addedPaths) paths.insert(p.clone());
|
for (auto & p : goal.addedPaths) paths.insert(p);
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2748,9 +2753,6 @@ struct RestrictedStore : public LocalFSStore
|
||||||
StorePathSet queryDerivationOutputs(const StorePath & path) override
|
StorePathSet queryDerivationOutputs(const StorePath & path) override
|
||||||
{ throw Error("queryDerivationOutputs"); }
|
{ throw Error("queryDerivationOutputs"); }
|
||||||
|
|
||||||
StringSet queryDerivationOutputNames(const StorePath & path) override
|
|
||||||
{ throw Error("queryDerivationOutputNames"); }
|
|
||||||
|
|
||||||
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
|
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
|
||||||
{ throw Error("queryPathFromHashPart"); }
|
{ throw Error("queryPathFromHashPart"); }
|
||||||
|
|
||||||
|
@ -2810,7 +2812,7 @@ struct RestrictedStore : public LocalFSStore
|
||||||
auto drv = derivationFromPath(path.path);
|
auto drv = derivationFromPath(path.path);
|
||||||
for (auto & output : drv.outputs)
|
for (auto & output : drv.outputs)
|
||||||
if (wantOutput(output.first, path.outputs))
|
if (wantOutput(output.first, path.outputs))
|
||||||
newPaths.insert(output.second.path.clone());
|
newPaths.insert(output.second.path);
|
||||||
} else if (!goal.isAllowed(path.path))
|
} else if (!goal.isAllowed(path.path))
|
||||||
throw InvalidPath("cannot build unknown path '%s' in recursive Nix", printStorePath(path.path));
|
throw InvalidPath("cannot build unknown path '%s' in recursive Nix", printStorePath(path.path));
|
||||||
}
|
}
|
||||||
|
@ -2855,7 +2857,7 @@ struct RestrictedStore : public LocalFSStore
|
||||||
if (goal.isAllowed(path.path))
|
if (goal.isAllowed(path.path))
|
||||||
allowed.emplace_back(path);
|
allowed.emplace_back(path);
|
||||||
else
|
else
|
||||||
unknown.insert(path.path.clone());
|
unknown.insert(path.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
next->queryMissing(allowed, willBuild, willSubstitute,
|
next->queryMissing(allowed, willBuild, willSubstitute,
|
||||||
|
@ -2950,7 +2952,7 @@ void DerivationGoal::addDependency(const StorePath & path)
|
||||||
{
|
{
|
||||||
if (isAllowed(path)) return;
|
if (isAllowed(path)) return;
|
||||||
|
|
||||||
addedPaths.insert(path.clone());
|
addedPaths.insert(path);
|
||||||
|
|
||||||
/* If we're doing a sandbox build, then we have to make the path
|
/* If we're doing a sandbox build, then we have to make the path
|
||||||
appear in the sandbox. */
|
appear in the sandbox. */
|
||||||
|
@ -3572,7 +3574,7 @@ StorePathSet parseReferenceSpecifiers(Store & store, const BasicDerivation & drv
|
||||||
if (store.isStorePath(i))
|
if (store.isStorePath(i))
|
||||||
result.insert(store.parseStorePath(i));
|
result.insert(store.parseStorePath(i));
|
||||||
else if (drv.outputs.count(i))
|
else if (drv.outputs.count(i))
|
||||||
result.insert(drv.outputs.find(i)->second.path.clone());
|
result.insert(drv.outputs.find(i)->second.path);
|
||||||
else throw BuildError("derivation contains an illegal reference specifier '%s'", i);
|
else throw BuildError("derivation contains an illegal reference specifier '%s'", i);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -3630,9 +3632,9 @@ void DerivationGoal::registerOutputs()
|
||||||
output paths, and any paths that have been built via recursive
|
output paths, and any paths that have been built via recursive
|
||||||
Nix calls. */
|
Nix calls. */
|
||||||
StorePathSet referenceablePaths;
|
StorePathSet referenceablePaths;
|
||||||
for (auto & p : inputPaths) referenceablePaths.insert(p.clone());
|
for (auto & p : inputPaths) referenceablePaths.insert(p);
|
||||||
for (auto & i : drv->outputs) referenceablePaths.insert(i.second.path.clone());
|
for (auto & i : drv->outputs) referenceablePaths.insert(i.second.path);
|
||||||
for (auto & p : addedPaths) referenceablePaths.insert(p.clone());
|
for (auto & p : addedPaths) referenceablePaths.insert(p);
|
||||||
|
|
||||||
/* Check whether the output paths were created, and grep each
|
/* Check whether the output paths were created, and grep each
|
||||||
output path to determine what other paths it references. Also make all
|
output path to determine what other paths it references. Also make all
|
||||||
|
@ -3743,7 +3745,7 @@ void DerivationGoal::registerOutputs()
|
||||||
worker.hashMismatch = true;
|
worker.hashMismatch = true;
|
||||||
delayedException = std::make_exception_ptr(
|
delayedException = std::make_exception_ptr(
|
||||||
BuildError("hash mismatch in fixed-output derivation '%s':\n wanted: %s\n got: %s",
|
BuildError("hash mismatch in fixed-output derivation '%s':\n wanted: %s\n got: %s",
|
||||||
worker.store.printStorePath(dest), h.to_string(SRI), h2.to_string(SRI)));
|
worker.store.printStorePath(dest), h.to_string(SRI, true), h2.to_string(SRI, true)));
|
||||||
|
|
||||||
Path actualDest = worker.store.Store::toRealPath(dest);
|
Path actualDest = worker.store.Store::toRealPath(dest);
|
||||||
|
|
||||||
|
@ -3831,7 +3833,7 @@ void DerivationGoal::registerOutputs()
|
||||||
info.narHash = hash.first;
|
info.narHash = hash.first;
|
||||||
info.narSize = hash.second;
|
info.narSize = hash.second;
|
||||||
info.references = std::move(references);
|
info.references = std::move(references);
|
||||||
info.deriver = drvPath.clone();
|
info.deriver = drvPath;
|
||||||
info.ultimate = true;
|
info.ultimate = true;
|
||||||
info.ca = ca;
|
info.ca = ca;
|
||||||
worker.store.signPathInfo(info);
|
worker.store.signPathInfo(info);
|
||||||
|
@ -3875,7 +3877,6 @@ void DerivationGoal::registerOutputs()
|
||||||
.hint = hint
|
.hint = hint
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
curRound = nrRounds; // we know enough, bail out early
|
curRound = nrRounds; // we know enough, bail out early
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3947,23 +3948,23 @@ void DerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs)
|
||||||
uint64_t closureSize = 0;
|
uint64_t closureSize = 0;
|
||||||
StorePathSet pathsDone;
|
StorePathSet pathsDone;
|
||||||
std::queue<StorePath> pathsLeft;
|
std::queue<StorePath> pathsLeft;
|
||||||
pathsLeft.push(path.clone());
|
pathsLeft.push(path);
|
||||||
|
|
||||||
while (!pathsLeft.empty()) {
|
while (!pathsLeft.empty()) {
|
||||||
auto path = pathsLeft.front().clone();
|
auto path = pathsLeft.front();
|
||||||
pathsLeft.pop();
|
pathsLeft.pop();
|
||||||
if (!pathsDone.insert(path.clone()).second) continue;
|
if (!pathsDone.insert(path).second) continue;
|
||||||
|
|
||||||
auto i = outputsByPath.find(worker.store.printStorePath(path));
|
auto i = outputsByPath.find(worker.store.printStorePath(path));
|
||||||
if (i != outputsByPath.end()) {
|
if (i != outputsByPath.end()) {
|
||||||
closureSize += i->second.narSize;
|
closureSize += i->second.narSize;
|
||||||
for (auto & ref : i->second.references)
|
for (auto & ref : i->second.references)
|
||||||
pathsLeft.push(ref.clone());
|
pathsLeft.push(ref);
|
||||||
} else {
|
} else {
|
||||||
auto info = worker.store.queryPathInfo(path);
|
auto info = worker.store.queryPathInfo(path);
|
||||||
closureSize += info->narSize;
|
closureSize += info->narSize;
|
||||||
for (auto & ref : info->references)
|
for (auto & ref : info->references)
|
||||||
pathsLeft.push(ref.clone());
|
pathsLeft.push(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3990,8 +3991,8 @@ void DerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs)
|
||||||
auto spec = parseReferenceSpecifiers(worker.store, *drv, *value);
|
auto spec = parseReferenceSpecifiers(worker.store, *drv, *value);
|
||||||
|
|
||||||
auto used = recursive
|
auto used = recursive
|
||||||
? cloneStorePathSet(getClosure(info.path).first)
|
? getClosure(info.path).first
|
||||||
: cloneStorePathSet(info.references);
|
: info.references;
|
||||||
|
|
||||||
if (recursive && checks.ignoreSelfRefs)
|
if (recursive && checks.ignoreSelfRefs)
|
||||||
used.erase(info.path);
|
used.erase(info.path);
|
||||||
|
@ -4001,10 +4002,10 @@ void DerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs)
|
||||||
for (auto & i : used)
|
for (auto & i : used)
|
||||||
if (allowed) {
|
if (allowed) {
|
||||||
if (!spec.count(i))
|
if (!spec.count(i))
|
||||||
badPaths.insert(i.clone());
|
badPaths.insert(i);
|
||||||
} else {
|
} else {
|
||||||
if (spec.count(i))
|
if (spec.count(i))
|
||||||
badPaths.insert(i.clone());
|
badPaths.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!badPaths.empty()) {
|
if (!badPaths.empty()) {
|
||||||
|
@ -4139,14 +4140,11 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
|
||||||
{
|
{
|
||||||
logSize += data.size();
|
logSize += data.size();
|
||||||
if (settings.maxLogSize && logSize > settings.maxLogSize) {
|
if (settings.maxLogSize && logSize > settings.maxLogSize) {
|
||||||
logError({
|
|
||||||
.name = "Max log size exceeded",
|
|
||||||
.hint = hintfmt(
|
|
||||||
"%1% killed after writing more than %2% bytes of log output",
|
|
||||||
getName(), settings.maxLogSize)
|
|
||||||
});
|
|
||||||
killChild();
|
killChild();
|
||||||
done(BuildResult::LogLimitExceeded);
|
done(
|
||||||
|
BuildResult::LogLimitExceeded,
|
||||||
|
Error("%s killed after writing more than %d bytes of log output",
|
||||||
|
getName(), settings.maxLogSize));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4188,13 +4186,8 @@ void DerivationGoal::flushLine()
|
||||||
;
|
;
|
||||||
|
|
||||||
else {
|
else {
|
||||||
if (settings.verboseBuild &&
|
logTail.push_back(currentLogLine);
|
||||||
(settings.printRepeatedBuilds || curRound == 1))
|
if (logTail.size() > settings.logLines) logTail.pop_front();
|
||||||
printError(currentLogLine);
|
|
||||||
else {
|
|
||||||
logTail.push_back(currentLogLine);
|
|
||||||
if (logTail.size() > settings.logLines) logTail.pop_front();
|
|
||||||
}
|
|
||||||
|
|
||||||
act->result(resBuildLogLine, currentLogLine);
|
act->result(resBuildLogLine, currentLogLine);
|
||||||
}
|
}
|
||||||
|
@ -4212,7 +4205,7 @@ StorePathSet DerivationGoal::checkPathValidity(bool returnValid, bool checkHash)
|
||||||
bool good =
|
bool good =
|
||||||
worker.store.isValidPath(i.second.path) &&
|
worker.store.isValidPath(i.second.path) &&
|
||||||
(!checkHash || worker.pathContentsGood(i.second.path));
|
(!checkHash || worker.pathContentsGood(i.second.path));
|
||||||
if (good == returnValid) result.insert(i.second.path.clone());
|
if (good == returnValid) result.insert(i.second.path);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -4228,15 +4221,16 @@ void DerivationGoal::addHashRewrite(const StorePath & path)
|
||||||
deletePath(worker.store.printStorePath(p));
|
deletePath(worker.store.printStorePath(p));
|
||||||
inputRewrites[h1] = h2;
|
inputRewrites[h1] = h2;
|
||||||
outputRewrites[h2] = h1;
|
outputRewrites[h2] = h1;
|
||||||
redirectedOutputs.insert_or_assign(path.clone(), std::move(p));
|
redirectedOutputs.insert_or_assign(path, std::move(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::done(BuildResult::Status status, const string & msg)
|
void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
|
||||||
{
|
{
|
||||||
result.status = status;
|
result.status = status;
|
||||||
result.errorMsg = msg;
|
if (ex)
|
||||||
amDone(result.success() ? ecSuccess : ecFailed);
|
result.errorMsg = ex->what();
|
||||||
|
amDone(result.success() ? ecSuccess : ecFailed, ex);
|
||||||
if (result.status == BuildResult::TimedOut)
|
if (result.status == BuildResult::TimedOut)
|
||||||
worker.timedOut = true;
|
worker.timedOut = true;
|
||||||
if (result.status == BuildResult::PermanentFailure)
|
if (result.status == BuildResult::PermanentFailure)
|
||||||
|
@ -4302,10 +4296,10 @@ private:
|
||||||
GoalState state;
|
GoalState state;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SubstitutionGoal(StorePath && storePath, Worker & worker, RepairFlag repair = NoRepair);
|
SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair);
|
||||||
~SubstitutionGoal();
|
~SubstitutionGoal();
|
||||||
|
|
||||||
void timedOut() override { abort(); };
|
void timedOut(Error && ex) override { abort(); };
|
||||||
|
|
||||||
string key() override
|
string key() override
|
||||||
{
|
{
|
||||||
|
@ -4328,18 +4322,13 @@ public:
|
||||||
void handleChildOutput(int fd, const string & data) override;
|
void handleChildOutput(int fd, const string & data) override;
|
||||||
void handleEOF(int fd) override;
|
void handleEOF(int fd) override;
|
||||||
|
|
||||||
StorePath getStorePath() { return storePath.clone(); }
|
StorePath getStorePath() { return storePath; }
|
||||||
|
|
||||||
void amDone(ExitCode result) override
|
|
||||||
{
|
|
||||||
Goal::amDone(result);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
SubstitutionGoal::SubstitutionGoal(StorePath && storePath, Worker & worker, RepairFlag repair)
|
SubstitutionGoal::SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair)
|
||||||
: Goal(worker)
|
: Goal(worker)
|
||||||
, storePath(std::move(storePath))
|
, storePath(storePath)
|
||||||
, repair(repair)
|
, repair(repair)
|
||||||
{
|
{
|
||||||
state = &SubstitutionGoal::init;
|
state = &SubstitutionGoal::init;
|
||||||
|
@ -4573,7 +4562,7 @@ void SubstitutionGoal::finished()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
worker.markContentsGood(storePath.clone());
|
worker.markContentsGood(storePath);
|
||||||
|
|
||||||
printMsg(lvlChatty, "substitution of path '%s' succeeded", worker.store.printStorePath(storePath));
|
printMsg(lvlChatty, "substitution of path '%s' succeeded", worker.store.printStorePath(storePath));
|
||||||
|
|
||||||
|
@ -4643,10 +4632,10 @@ Worker::~Worker()
|
||||||
GoalPtr Worker::makeDerivationGoal(const StorePath & path,
|
GoalPtr Worker::makeDerivationGoal(const StorePath & path,
|
||||||
const StringSet & wantedOutputs, BuildMode buildMode)
|
const StringSet & wantedOutputs, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
GoalPtr goal = derivationGoals[path.clone()].lock(); // FIXME
|
GoalPtr goal = derivationGoals[path].lock(); // FIXME
|
||||||
if (!goal) {
|
if (!goal) {
|
||||||
goal = std::make_shared<DerivationGoal>(path.clone(), wantedOutputs, *this, buildMode);
|
goal = std::make_shared<DerivationGoal>(path, wantedOutputs, *this, buildMode);
|
||||||
derivationGoals.insert_or_assign(path.clone(), goal);
|
derivationGoals.insert_or_assign(path, goal);
|
||||||
wakeUp(goal);
|
wakeUp(goal);
|
||||||
} else
|
} else
|
||||||
(dynamic_cast<DerivationGoal *>(goal.get()))->addWantedOutputs(wantedOutputs);
|
(dynamic_cast<DerivationGoal *>(goal.get()))->addWantedOutputs(wantedOutputs);
|
||||||
|
@ -4654,10 +4643,10 @@ GoalPtr Worker::makeDerivationGoal(const StorePath & path,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(StorePath && drvPath,
|
std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath & drvPath,
|
||||||
const BasicDerivation & drv, BuildMode buildMode)
|
const BasicDerivation & drv, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
auto goal = std::make_shared<DerivationGoal>(std::move(drvPath), drv, *this, buildMode);
|
auto goal = std::make_shared<DerivationGoal>(drvPath, drv, *this, buildMode);
|
||||||
wakeUp(goal);
|
wakeUp(goal);
|
||||||
return goal;
|
return goal;
|
||||||
}
|
}
|
||||||
|
@ -4665,10 +4654,10 @@ std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(StorePath && drv
|
||||||
|
|
||||||
GoalPtr Worker::makeSubstitutionGoal(const StorePath & path, RepairFlag repair)
|
GoalPtr Worker::makeSubstitutionGoal(const StorePath & path, RepairFlag repair)
|
||||||
{
|
{
|
||||||
GoalPtr goal = substitutionGoals[path.clone()].lock(); // FIXME
|
GoalPtr goal = substitutionGoals[path].lock(); // FIXME
|
||||||
if (!goal) {
|
if (!goal) {
|
||||||
goal = std::make_shared<SubstitutionGoal>(path.clone(), *this, repair);
|
goal = std::make_shared<SubstitutionGoal>(path, *this, repair);
|
||||||
substitutionGoals.insert_or_assign(path.clone(), goal);
|
substitutionGoals.insert_or_assign(path, goal);
|
||||||
wakeUp(goal);
|
wakeUp(goal);
|
||||||
}
|
}
|
||||||
return goal;
|
return goal;
|
||||||
|
@ -4697,7 +4686,7 @@ void Worker::removeGoal(GoalPtr goal)
|
||||||
topGoals.erase(goal);
|
topGoals.erase(goal);
|
||||||
/* If a top-level goal failed, then kill all other goals
|
/* If a top-level goal failed, then kill all other goals
|
||||||
(unless keepGoing was set). */
|
(unless keepGoing was set). */
|
||||||
if (goal->getExitCode() == Goal::ecFailed && !settings.keepGoing)
|
if (goal->exitCode == Goal::ecFailed && !settings.keepGoing)
|
||||||
topGoals.clear();
|
topGoals.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4875,8 +4864,6 @@ void Worker::waitForInput()
|
||||||
up after a few seconds at most. */
|
up after a few seconds at most. */
|
||||||
if (!waitingForAWhile.empty()) {
|
if (!waitingForAWhile.empty()) {
|
||||||
useTimeout = true;
|
useTimeout = true;
|
||||||
if (lastWokenUp == steady_time_point::min())
|
|
||||||
printInfo("waiting for locks, build slots or build users...");
|
|
||||||
if (lastWokenUp == steady_time_point::min() || lastWokenUp > before) lastWokenUp = before;
|
if (lastWokenUp == steady_time_point::min() || lastWokenUp > before) lastWokenUp = before;
|
||||||
timeout = std::max(1L,
|
timeout = std::max(1L,
|
||||||
(long) std::chrono::duration_cast<std::chrono::seconds>(
|
(long) std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
@ -4941,32 +4928,24 @@ void Worker::waitForInput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (goal->getExitCode() == Goal::ecBusy &&
|
if (goal->exitCode == Goal::ecBusy &&
|
||||||
0 != settings.maxSilentTime &&
|
0 != settings.maxSilentTime &&
|
||||||
j->respectTimeouts &&
|
j->respectTimeouts &&
|
||||||
after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime))
|
after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime))
|
||||||
{
|
{
|
||||||
logError({
|
goal->timedOut(Error(
|
||||||
.name = "Silent build timeout",
|
|
||||||
.hint = hintfmt(
|
|
||||||
"%1% timed out after %2% seconds of silence",
|
"%1% timed out after %2% seconds of silence",
|
||||||
goal->getName(), settings.maxSilentTime)
|
goal->getName(), settings.maxSilentTime));
|
||||||
});
|
|
||||||
goal->timedOut();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (goal->getExitCode() == Goal::ecBusy &&
|
else if (goal->exitCode == Goal::ecBusy &&
|
||||||
0 != settings.buildTimeout &&
|
0 != settings.buildTimeout &&
|
||||||
j->respectTimeouts &&
|
j->respectTimeouts &&
|
||||||
after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout))
|
after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout))
|
||||||
{
|
{
|
||||||
logError({
|
goal->timedOut(Error(
|
||||||
.name = "Build timeout",
|
|
||||||
.hint = hintfmt(
|
|
||||||
"%1% timed out after %2% seconds",
|
"%1% timed out after %2% seconds",
|
||||||
goal->getName(), settings.buildTimeout)
|
goal->getName(), settings.buildTimeout));
|
||||||
});
|
|
||||||
goal->timedOut();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5023,7 +5002,7 @@ bool Worker::pathContentsGood(const StorePath & path)
|
||||||
Hash nullHash(htSHA256);
|
Hash nullHash(htSHA256);
|
||||||
res = info->narHash == nullHash || info->narHash == current.first;
|
res = info->narHash == nullHash || info->narHash == current.first;
|
||||||
}
|
}
|
||||||
pathContentsGoodCache.insert_or_assign(path.clone(), res);
|
pathContentsGoodCache.insert_or_assign(path, res);
|
||||||
if (!res)
|
if (!res)
|
||||||
logError({
|
logError({
|
||||||
.name = "Corrupted path",
|
.name = "Corrupted path",
|
||||||
|
@ -5033,9 +5012,9 @@ bool Worker::pathContentsGood(const StorePath & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Worker::markContentsGood(StorePath && path)
|
void Worker::markContentsGood(const StorePath & path)
|
||||||
{
|
{
|
||||||
pathContentsGoodCache.insert_or_assign(std::move(path), true);
|
pathContentsGoodCache.insert_or_assign(path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5072,23 +5051,35 @@ void LocalStore::buildPaths(const std::vector<StorePathWithOutputs> & drvPaths,
|
||||||
worker.run(goals);
|
worker.run(goals);
|
||||||
|
|
||||||
StorePathSet failed;
|
StorePathSet failed;
|
||||||
|
std::optional<Error> ex;
|
||||||
for (auto & i : goals) {
|
for (auto & i : goals) {
|
||||||
if (i->getExitCode() != Goal::ecSuccess) {
|
if (i->ex) {
|
||||||
|
if (ex)
|
||||||
|
logError(i->ex->info());
|
||||||
|
else
|
||||||
|
ex = i->ex;
|
||||||
|
}
|
||||||
|
if (i->exitCode != Goal::ecSuccess) {
|
||||||
DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i.get());
|
DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i.get());
|
||||||
if (i2) failed.insert(i2->getDrvPath());
|
if (i2) failed.insert(i2->getDrvPath());
|
||||||
else failed.insert(dynamic_cast<SubstitutionGoal *>(i.get())->getStorePath());
|
else failed.insert(dynamic_cast<SubstitutionGoal *>(i.get())->getStorePath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!failed.empty())
|
if (failed.size() == 1 && ex) {
|
||||||
|
ex->status = worker.exitStatus();
|
||||||
|
throw *ex;
|
||||||
|
} else if (!failed.empty()) {
|
||||||
|
if (ex) logError(ex->info());
|
||||||
throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed));
|
throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildResult LocalStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
BuildResult LocalStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
BuildMode buildMode)
|
BuildMode buildMode)
|
||||||
{
|
{
|
||||||
Worker worker(*this);
|
Worker worker(*this);
|
||||||
auto goal = worker.makeBasicDerivationGoal(drvPath.clone(), drv, buildMode);
|
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, buildMode);
|
||||||
|
|
||||||
BuildResult result;
|
BuildResult result;
|
||||||
|
|
||||||
|
@ -5109,7 +5100,7 @@ void LocalStore::ensurePath(const StorePath & path)
|
||||||
/* If the path is already valid, we're done. */
|
/* If the path is already valid, we're done. */
|
||||||
if (isValidPath(path)) return;
|
if (isValidPath(path)) return;
|
||||||
|
|
||||||
primeCache(*this, {StorePathWithOutputs(path)});
|
primeCache(*this, {{path}});
|
||||||
|
|
||||||
Worker worker(*this);
|
Worker worker(*this);
|
||||||
GoalPtr goal = worker.makeSubstitutionGoal(path);
|
GoalPtr goal = worker.makeSubstitutionGoal(path);
|
||||||
|
@ -5117,8 +5108,13 @@ void LocalStore::ensurePath(const StorePath & path)
|
||||||
|
|
||||||
worker.run(goals);
|
worker.run(goals);
|
||||||
|
|
||||||
if (goal->getExitCode() != Goal::ecSuccess)
|
if (goal->exitCode != Goal::ecSuccess) {
|
||||||
throw Error(worker.exitStatus(), "path '%s' does not exist and cannot be created", printStorePath(path));
|
if (goal->ex) {
|
||||||
|
goal->ex->status = worker.exitStatus();
|
||||||
|
throw *goal->ex;
|
||||||
|
} else
|
||||||
|
throw Error(worker.exitStatus(), "path '%s' does not exist and cannot be created", printStorePath(path));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5130,7 +5126,7 @@ void LocalStore::repairPath(const StorePath & path)
|
||||||
|
|
||||||
worker.run(goals);
|
worker.run(goals);
|
||||||
|
|
||||||
if (goal->getExitCode() != Goal::ecSuccess) {
|
if (goal->exitCode != Goal::ecSuccess) {
|
||||||
/* Since substituting the path didn't work, if we have a valid
|
/* Since substituting the path didn't work, if we have a valid
|
||||||
deriver, then rebuild the deriver. */
|
deriver, then rebuild the deriver. */
|
||||||
auto info = queryPathInfo(path);
|
auto info = queryPathInfo(path);
|
||||||
|
|
|
@ -22,10 +22,9 @@ 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) {
|
||||||
logWarning(
|
logWarning({
|
||||||
ErrorInfo {
|
.name = "Create links - directory",
|
||||||
.name = "Create links - directory",
|
.hint = hintfmt("not including '%s' in the user environment because it's not a directory", srcDir)
|
||||||
.hint = hintfmt("not including '%s' in the user environment because it's not a directory", srcDir)
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -45,10 +44,9 @@ 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) {
|
||||||
logWarning(
|
logWarning({
|
||||||
ErrorInfo {
|
.name = "Create links - skipping symlink",
|
||||||
.name = "Create links - skipping symlink",
|
.hint = hintfmt("skipping dangling symlink '%s'", dstFile)
|
||||||
.hint = hintfmt("skipping dangling symlink '%s'", dstFile)
|
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ struct TunnelLogger : public Logger
|
||||||
{
|
{
|
||||||
if (ei.level > verbosity) return;
|
if (ei.level > verbosity) return;
|
||||||
|
|
||||||
std::stringstream oss;
|
std::stringstream oss;
|
||||||
oss << ei;
|
oss << ei;
|
||||||
|
|
||||||
StringSink buf;
|
StringSink buf;
|
||||||
|
@ -293,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);
|
||||||
|
@ -327,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)
|
||||||
|
@ -341,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;
|
||||||
|
@ -370,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) {
|
||||||
|
@ -592,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())
|
||||||
|
|
|
@ -27,28 +27,6 @@ void DerivationOutput::parseHashInfo(FileIngestionMethod & recursive, Hash & has
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BasicDerivation::BasicDerivation(const BasicDerivation & other)
|
|
||||||
: platform(other.platform)
|
|
||||||
, builder(other.builder)
|
|
||||||
, args(other.args)
|
|
||||||
, env(other.env)
|
|
||||||
{
|
|
||||||
for (auto & i : other.outputs)
|
|
||||||
outputs.insert_or_assign(i.first,
|
|
||||||
DerivationOutput(i.second.path.clone(), std::string(i.second.hashAlgo), std::string(i.second.hash)));
|
|
||||||
for (auto & i : other.inputSrcs)
|
|
||||||
inputSrcs.insert(i.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Derivation::Derivation(const Derivation & other)
|
|
||||||
: BasicDerivation(other)
|
|
||||||
{
|
|
||||||
for (auto & i : other.inputDrvs)
|
|
||||||
inputDrvs.insert_or_assign(i.first.clone(), i.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const StorePath & BasicDerivation::findOutput(const string & id) const
|
const StorePath & BasicDerivation::findOutput(const string & id) const
|
||||||
{
|
{
|
||||||
auto i = outputs.find(id);
|
auto i = outputs.find(id);
|
||||||
|
@ -67,9 +45,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). */
|
||||||
|
@ -155,7 +133,11 @@ static Derivation parseDerivation(const Store & store, const string & s)
|
||||||
expect(str, ","); auto hashAlgo = parseString(str);
|
expect(str, ","); auto hashAlgo = parseString(str);
|
||||||
expect(str, ","); auto hash = parseString(str);
|
expect(str, ","); auto hash = parseString(str);
|
||||||
expect(str, ")");
|
expect(str, ")");
|
||||||
drv.outputs.emplace(id, DerivationOutput(std::move(path), std::move(hashAlgo), std::move(hash)));
|
drv.outputs.emplace(id, DerivationOutput {
|
||||||
|
.path = std::move(path),
|
||||||
|
.hashAlgo = std::move(hashAlgo),
|
||||||
|
.hash = std::move(hash)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse the list of input derivations. */
|
/* Parse the list of input derivations. */
|
||||||
|
@ -204,6 +186,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)));
|
||||||
|
@ -377,8 +365,8 @@ 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(Base16, false), i.second);
|
inputs2.insert_or_assign(h->second.to_string(Base16, false), i.second);
|
||||||
}
|
}
|
||||||
|
@ -405,11 +393,20 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
@ -419,7 +416,11 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv)
|
||||||
auto path = store.parseStorePath(readString(in));
|
auto path = store.parseStorePath(readString(in));
|
||||||
auto hashAlgo = readString(in);
|
auto hashAlgo = readString(in);
|
||||||
auto hash = readString(in);
|
auto hash = readString(in);
|
||||||
drv.outputs.emplace(name, DerivationOutput(std::move(path), std::move(hashAlgo), std::move(hash)));
|
drv.outputs.emplace(name, DerivationOutput {
|
||||||
|
.path = std::move(path),
|
||||||
|
.hashAlgo = std::move(hashAlgo),
|
||||||
|
.hash = std::move(hash)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
drv.inputSrcs = readStorePaths<StorePathSet>(store, in);
|
drv.inputSrcs = readStorePaths<StorePathSet>(store, in);
|
||||||
|
|
|
@ -17,11 +17,6 @@ struct DerivationOutput
|
||||||
StorePath path;
|
StorePath path;
|
||||||
std::string hashAlgo; /* hash used for expected hash computation */
|
std::string hashAlgo; /* hash used for expected hash computation */
|
||||||
std::string hash; /* expected hash, may be null */
|
std::string hash; /* expected hash, may be null */
|
||||||
DerivationOutput(StorePath && path, std::string && hashAlgo, std::string && hash)
|
|
||||||
: path(std::move(path))
|
|
||||||
, hashAlgo(std::move(hashAlgo))
|
|
||||||
, hash(std::move(hash))
|
|
||||||
{ }
|
|
||||||
void parseHashInfo(FileIngestionMethod & recursive, Hash & hash) const;
|
void parseHashInfo(FileIngestionMethod & recursive, Hash & hash) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,7 +38,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
|
||||||
|
@ -58,6 +52,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
|
||||||
|
@ -69,8 +65,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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
@ -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,6 +72,18 @@ 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)
|
||||||
|
@ -83,12 +95,11 @@ struct curlFileTransfer : public FileTransfer
|
||||||
, 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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -599,9 +608,9 @@ struct curlFileTransfer : public FileTransfer
|
||||||
workerThreadMain();
|
workerThreadMain();
|
||||||
} catch (nix::Interrupted & e) {
|
} catch (nix::Interrupted & e) {
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
logError({
|
logError({
|
||||||
.name = "File transfer",
|
.name = "File transfer",
|
||||||
.hint = hintfmt("unexpected error in download thread: %s",
|
.hint = hintfmt("unexpected error in download thread: %s",
|
||||||
e.what())
|
e.what())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,13 +128,12 @@ 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)
|
||||||
logWarning(
|
logWarning({
|
||||||
ErrorInfo {
|
.name = "GC root",
|
||||||
.name = "GC root",
|
.hint = hintfmt("warning: '%1%' is not in a directory where the garbage collector looks for roots; "
|
||||||
.hint = hintfmt("warning: '%1%' is not in a directory where the garbage collector looks for roots; "
|
"therefore, '%2%' might be removed by the garbage collector",
|
||||||
"therefore, '%2%' might be removed by the garbage collector",
|
gcRoot, printStorePath(storePath))
|
||||||
gcRoot, printStorePath(storePath))
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,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;
|
||||||
|
|
||||||
|
@ -611,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
|
||||||
|
@ -619,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,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);
|
||||||
}
|
}
|
||||||
|
@ -755,7 +754,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
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
|
||||||
|
@ -764,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
|
||||||
|
|
|
@ -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 "
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,7 +87,7 @@ 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)
|
||||||
logError({
|
logError({
|
||||||
.name = "'build-users-group' not found",
|
.name = "'build-users-group' not found",
|
||||||
.hint = hintfmt(
|
.hint = hintfmt(
|
||||||
"warning: the group '%1%' specified in 'build-users-group' does not exist",
|
"warning: the group '%1%' specified in 'build-users-group' does not exist",
|
||||||
|
@ -584,7 +584,7 @@ uint64_t LocalStore::addValidPath(State & state,
|
||||||
|
|
||||||
state.stmtRegisterValidPath.use()
|
state.stmtRegisterValidPath.use()
|
||||||
(printStorePath(info.path))
|
(printStorePath(info.path))
|
||||||
(info.narHash.to_string(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)
|
||||||
|
@ -599,7 +599,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
|
||||||
|
@ -619,7 +619,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) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,7 +631,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());
|
||||||
|
@ -684,7 +684,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(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())
|
||||||
(info.ca, !info.ca.empty())
|
(info.ca, !info.ca.empty())
|
||||||
|
@ -721,7 +721,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -789,26 +789,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;
|
||||||
|
|
||||||
|
@ -833,7 +816,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;
|
||||||
|
|
||||||
|
@ -847,9 +830,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);
|
||||||
}
|
}
|
||||||
|
@ -871,9 +854,9 @@ 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 &) {
|
||||||
|
@ -917,7 +900,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
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) {
|
||||||
|
@ -932,8 +915,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
|
||||||
|
@ -960,7 +942,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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,7 +994,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
if (info.ca == "" || !info.references.count(info.path))
|
if (info.ca == "" || !info.references.count(info.path))
|
||||||
hashSink = std::make_unique<HashSink>(htSHA256);
|
hashSink = std::make_unique<HashSink>(htSHA256);
|
||||||
else
|
else
|
||||||
hashSink = std::make_unique<HashModuloSink>(htSHA256, 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);
|
||||||
|
@ -1026,7 +1008,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",
|
||||||
|
@ -1092,7 +1074,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||||
|
|
||||||
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 = makeFixedOutputCA(method, h);
|
info.ca = makeFixedOutputCA(method, h);
|
||||||
|
@ -1155,11 +1137,11 @@ StorePath LocalStore::addTextToStore(const string & name, const string & 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 = "text:" + hash.to_string();
|
info.ca = "text:" + hash.to_string(Base32, true);
|
||||||
registerValidPath(info);
|
registerValidPath(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1241,7 +1223,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||||
Path linkPath = linksDir + "/" + link.name;
|
Path linkPath = linksDir + "/" + link.name;
|
||||||
string hash = hashPath(htSHA256, linkPath).first.to_string(Base32, false);
|
string hash = hashPath(htSHA256, linkPath).first.to_string(Base32, false);
|
||||||
if (hash != link.name) {
|
if (hash != link.name) {
|
||||||
logError({
|
logError({
|
||||||
.name = "Invalid hash",
|
.name = "Invalid hash",
|
||||||
.hint = hintfmt(
|
.hint = hintfmt(
|
||||||
"link '%s' was modified! expected hash '%s', got '%s'",
|
"link '%s' was modified! expected hash '%s', got '%s'",
|
||||||
|
@ -1273,16 +1255,16 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||||
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) {
|
||||||
logError({
|
logError({
|
||||||
.name = "Invalid hash - path modified",
|
.name = "Invalid hash - path modified",
|
||||||
.hint = hintfmt("path '%s' was modified! expected hash '%s', got '%s'",
|
.hint = hintfmt("path '%s' was modified! expected hash '%s', got '%s'",
|
||||||
printStorePath(i), info->narHash.to_string(), current.first.to_string())
|
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 {
|
||||||
|
@ -1334,7 +1316,7 @@ 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)) {
|
||||||
logError({
|
logError({
|
||||||
.name = "Nix path not found",
|
.name = "Nix path not found",
|
||||||
.hint = hintfmt("path '%s' is not in the Nix store", pathS)
|
.hint = hintfmt("path '%s' is not in the Nix store", pathS)
|
||||||
});
|
});
|
||||||
|
@ -1360,7 +1342,7 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store,
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
invalidatePath(*state, path);
|
invalidatePath(*state, path);
|
||||||
} else {
|
} else {
|
||||||
logError({
|
logError({
|
||||||
.name = "Missing path with referrers",
|
.name = "Missing path with referrers",
|
||||||
.hint = hintfmt("path '%s' disappeared, but it still has valid referrers!", pathS)
|
.hint = hintfmt("path '%s' disappeared, but it still has valid referrers!", pathS)
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 = queryNAR.getStr(11);
|
narInfo->ca = 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,7 +4,7 @@
|
||||||
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.clone()) // FIXME: hack
|
: ValidPathInfo(StorePath(StorePath::dummy)) // FIXME: hack
|
||||||
{
|
{
|
||||||
auto corrupt = [&]() {
|
auto corrupt = [&]() {
|
||||||
throw Error("NAR info file '%1%' is corrupt", whence);
|
throw Error("NAR info file '%1%' is corrupt", whence);
|
||||||
|
@ -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,10 +87,10 @@ std::string NarInfo::to_string(const Store & store) const
|
||||||
assert(compression != "");
|
assert(compression != "");
|
||||||
res += "Compression: " + compression + "\n";
|
res += "Compression: " + compression + "\n";
|
||||||
assert(fileHash.type == htSHA256);
|
assert(fileHash.type == htSHA256);
|
||||||
res += "FileHash: " + fileHash.to_string(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 == htSHA256);
|
assert(narHash.type == htSHA256);
|
||||||
res += "NarHash: " + narHash.to_string(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";
|
||||||
|
|
|
@ -130,7 +130,7 @@ 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)) {
|
||||||
logWarning({
|
logWarning({
|
||||||
.name = "Suspicious file",
|
.name = "Suspicious file",
|
||||||
.hint = hintfmt("skipping suspicious writable file '%1%'", path)
|
.hint = hintfmt("skipping suspicious writable file '%1%'", path)
|
||||||
});
|
});
|
||||||
|
@ -153,7 +153,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
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(htSHA256, 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(Base32, false);
|
Path linkPath = linksDir + "/" + hash.to_string(Base32, false);
|
||||||
|
@ -197,7 +197,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.st_size != stLink.st_size) {
|
if (st.st_size != stLink.st_size) {
|
||||||
logWarning({
|
logWarning({
|
||||||
.name = "Corrupted link",
|
.name = "Corrupted link",
|
||||||
.hint = hintfmt("removing corrupted link '%1%'", linkPath)
|
.hint = hintfmt("removing corrupted link '%1%'", linkPath)
|
||||||
});
|
});
|
||||||
|
@ -235,7 +235,7 @@ 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)
|
||||||
logError({
|
logError({
|
||||||
.name = "Unlink error",
|
.name = "Unlink error",
|
||||||
.hint = hintfmt("unable to unlink '%1%'", tempLink)
|
.hint = hintfmt("unable to unlink '%1%'", tempLink)
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,59 +1,59 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "rust-ffi.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);
|
|
||||||
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 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;
|
||||||
|
@ -62,14 +62,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";
|
||||||
|
|
||||||
|
@ -83,18 +75,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -107,7 +87,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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -372,7 +372,7 @@ void RemoteStore::queryPathInfoUncached(const StorePath & path,
|
||||||
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), htSHA256);
|
info->narHash = Hash(readString(conn->from), htSHA256);
|
||||||
|
@ -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());
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -204,10 +204,10 @@ void handleSQLiteBusy(const SQLiteBusy & e)
|
||||||
|
|
||||||
if (now > lastWarned + 10) {
|
if (now > lastWarned + 10) {
|
||||||
lastWarned = now;
|
lastWarned = now;
|
||||||
logWarning(
|
logWarning({
|
||||||
ErrorInfo { .name = "Sqlite busy",
|
.name = "Sqlite busy",
|
||||||
.hint = hintfmt(e.what())
|
.hint = hintfmt(e.what())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sleep for a while since retrying the transaction right away
|
/* Sleep for a while since retrying the transaction right away
|
||||||
|
|
|
@ -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(Base16) + ":" + storeDir + ":" + std::string(name);
|
string s = type + ":" + hash.to_string(Base16, true) + ":" + storeDir + ":" + std::string(name);
|
||||||
auto h = compressHash(hashString(htSHA256, s), 20);
|
auto h = compressHash(hashString(htSHA256, s), 20);
|
||||||
return StorePath::make(h.hash, name);
|
return StorePath(h, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -186,7 +187,7 @@ StorePath Store::makeFixedOutputPath(
|
||||||
hashString(htSHA256,
|
hashString(htSHA256,
|
||||||
"fixed:out:"
|
"fixed:out:"
|
||||||
+ (recursive == FileIngestionMethod::Recursive ? (string) "r:" : "")
|
+ (recursive == FileIngestionMethod::Recursive ? (string) "r:" : "")
|
||||||
+ hash.to_string(Base16) + ":"),
|
+ hash.to_string(Base16, true) + ":"),
|
||||||
name);
|
name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -243,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());
|
||||||
|
@ -311,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);
|
||||||
|
@ -461,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);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -504,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)
|
||||||
|
@ -553,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())
|
||||||
|
@ -693,21 +694,6 @@ void copyClosure(ref<Store> srcStore, ref<Store> dstStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ValidPathInfo::ValidPathInfo(const ValidPathInfo & other)
|
|
||||||
: path(other.path.clone())
|
|
||||||
, deriver(other.deriver ? other.deriver->clone(): std::optional<StorePath>{})
|
|
||||||
, narHash(other.narHash)
|
|
||||||
, references(cloneStorePathSet(other.references))
|
|
||||||
, registrationTime(other.registrationTime)
|
|
||||||
, narSize(other.narSize)
|
|
||||||
, id(other.id)
|
|
||||||
, ultimate(other.ultimate)
|
|
||||||
, sigs(other.sigs)
|
|
||||||
, ca(other.ca)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istream & str, bool hashGiven)
|
std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istream & str, bool hashGiven)
|
||||||
{
|
{
|
||||||
std::string path;
|
std::string path;
|
||||||
|
@ -760,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(Base32) + ";"
|
+ narHash.to_string(Base32, true) + ";"
|
||||||
+ std::to_string(narSize) + ";"
|
+ std::to_string(narSize) + ";"
|
||||||
+ concatStringsSep(",", store.printStorePathSet(references));
|
+ concatStringsSep(",", store.printStorePathSet(references));
|
||||||
}
|
}
|
||||||
|
@ -777,13 +763,13 @@ bool ValidPathInfo::isContentAddressed(const Store & store) const
|
||||||
auto warn = [&]() {
|
auto warn = [&]() {
|
||||||
logWarning(
|
logWarning(
|
||||||
ErrorInfo{
|
ErrorInfo{
|
||||||
.name = "Path not content-addressed",
|
.name = "Path not content-addressed",
|
||||||
.hint = hintfmt("path '%s' claims to be content-addressed but isn't", store.printStorePath(path))
|
.hint = hintfmt("path '%s' claims to be content-addressed but isn't", store.printStorePath(path))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hasPrefix(ca, "text:")) {
|
if (hasPrefix(ca, "text:")) {
|
||||||
Hash hash(std::string(ca, 5));
|
Hash hash(ca.substr(5));
|
||||||
if (store.makeTextPath(path.name(), hash, references) == path)
|
if (store.makeTextPath(path.name(), hash, references) == path)
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
|
@ -792,8 +778,8 @@ bool ValidPathInfo::isContentAddressed(const Store & store) const
|
||||||
|
|
||||||
else if (hasPrefix(ca, "fixed:")) {
|
else if (hasPrefix(ca, "fixed:")) {
|
||||||
FileIngestionMethod recursive { ca.compare(6, 2, "r:") == 0 };
|
FileIngestionMethod recursive { ca.compare(6, 2, "r:") == 0 };
|
||||||
Hash hash(std::string(ca, recursive == FileIngestionMethod::Recursive ? 8 : 6));
|
Hash hash(ca.substr(recursive == FileIngestionMethod::Recursive ? 8 : 6));
|
||||||
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;
|
||||||
|
@ -840,7 +826,7 @@ std::string makeFixedOutputCA(FileIngestionMethod recursive, const Hash & hash)
|
||||||
{
|
{
|
||||||
return "fixed:"
|
return "fixed:"
|
||||||
+ (recursive == FileIngestionMethod::Recursive ? (std::string) "r:" : "")
|
+ (recursive == FileIngestionMethod::Recursive ? (std::string) "r:" : "")
|
||||||
+ hash.to_string();
|
+ hash.to_string(Base32, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -189,8 +189,9 @@ struct ValidPathInfo
|
||||||
|
|
||||||
Strings shortRefs() const;
|
Strings shortRefs() const;
|
||||||
|
|
||||||
|
ValidPathInfo(const StorePath & path) : path(path) { }
|
||||||
|
|
||||||
ValidPathInfo(StorePath && path) : path(std::move(path)) { }
|
ValidPathInfo(StorePath && path) : path(std::move(path)) { }
|
||||||
explicit ValidPathInfo(const ValidPathInfo & other);
|
|
||||||
|
|
||||||
virtual ~ValidPathInfo() { }
|
virtual ~ValidPathInfo() { }
|
||||||
};
|
};
|
||||||
|
@ -430,10 +431,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;
|
||||||
|
@ -587,6 +584,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
|
||||||
|
@ -732,10 +732,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,
|
||||||
|
|
|
@ -11,5 +11,7 @@ namespace nix {
|
||||||
#define ANSI_GREEN "\e[32;1m"
|
#define ANSI_GREEN "\e[32;1m"
|
||||||
#define ANSI_YELLOW "\e[33;1m"
|
#define ANSI_YELLOW "\e[33;1m"
|
||||||
#define ANSI_BLUE "\e[34;1m"
|
#define ANSI_BLUE "\e[34;1m"
|
||||||
|
#define ANSI_MAGENTA "\e[35m;1m"
|
||||||
|
#define ANSI_CYAN "\e[36m;1m"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,10 +217,15 @@ MultiCommand::MultiCommand(const Commands & commands)
|
||||||
{
|
{
|
||||||
expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) {
|
expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) {
|
||||||
assert(!command);
|
assert(!command);
|
||||||
auto i = commands.find(ss[0]);
|
auto cmd = ss[0];
|
||||||
|
if (auto alias = get(deprecatedAliases, cmd)) {
|
||||||
|
warn("'%s' is a deprecated alias for '%s'", cmd, *alias);
|
||||||
|
cmd = *alias;
|
||||||
|
}
|
||||||
|
auto i = commands.find(cmd);
|
||||||
if (i == commands.end())
|
if (i == commands.end())
|
||||||
throw UsageError("'%s' is not a recognised command", ss[0]);
|
throw UsageError("'%s' is not a recognised command", cmd);
|
||||||
command = {ss[0], i->second()};
|
command = {cmd, i->second()};
|
||||||
}});
|
}});
|
||||||
|
|
||||||
categories[Command::catDefault] = "Available commands";
|
categories[Command::catDefault] = "Available commands";
|
||||||
|
|
|
@ -234,6 +234,8 @@ public:
|
||||||
|
|
||||||
std::map<Command::Category, std::string> categories;
|
std::map<Command::Category, std::string> categories;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> deprecatedAliases;
|
||||||
|
|
||||||
// Selected command, if any.
|
// Selected command, if any.
|
||||||
std::optional<std::pair<std::string, ref<Command>>> command;
|
std::optional<std::pair<std::string, ref<Command>>> command;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <cassert>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ void printCodeLines(std::ostream &out, const string &prefix, const NixCode &nixC
|
||||||
{
|
{
|
||||||
// previous line of code.
|
// previous line of code.
|
||||||
if (nixCode.prevLineOfCode.has_value()) {
|
if (nixCode.prevLineOfCode.has_value()) {
|
||||||
out << std::endl
|
out << std::endl
|
||||||
<< fmt("%1% %|2$5d|| %3%",
|
<< fmt("%1% %|2$5d|| %3%",
|
||||||
prefix,
|
prefix,
|
||||||
(nixCode.errPos.line - 1),
|
(nixCode.errPos.line - 1),
|
||||||
|
@ -176,7 +176,7 @@ void printCodeLines(std::ostream &out, const string &prefix, const NixCode &nixC
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
|
std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
|
||||||
{
|
{
|
||||||
int errwidth = 80;
|
auto errwidth = std::max<size_t>(getWindowSize().second, 20);
|
||||||
string prefix = "";
|
string prefix = "";
|
||||||
|
|
||||||
string levelString;
|
string levelString;
|
||||||
|
@ -229,12 +229,10 @@ std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ndl = prefix.length() + levelString.length() + 3 + einfo.name.length() + einfo.programName.value_or("").length();
|
auto ndl = prefix.length() + levelString.length() + 3 + einfo.name.length() + einfo.programName.value_or("").length();
|
||||||
int dashwidth = ndl > (errwidth - 3) ? 3 : errwidth - ndl;
|
auto dashwidth = ndl > (errwidth - 3) ? 3 : errwidth - ndl;
|
||||||
|
|
||||||
string dashes;
|
std::string dashes(dashwidth, '-');
|
||||||
for (int i = 0; i < dashwidth; ++i)
|
|
||||||
dashes.append("-");
|
|
||||||
|
|
||||||
// divider.
|
// divider.
|
||||||
if (einfo.name != "")
|
if (einfo.name != "")
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "ref.hh"
|
#include "ref.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -22,7 +23,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
This file defines two main structs/classes used in nix error handling.
|
This file defines two main structs/classes used in nix error handling.
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ protected:
|
||||||
|
|
||||||
mutable std::optional<string> what_;
|
mutable std::optional<string> what_;
|
||||||
const string& calcWhat() const;
|
const string& calcWhat() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
unsigned int status = 1; // exit status
|
unsigned int status = 1; // exit status
|
||||||
|
|
||||||
|
@ -127,9 +128,9 @@ public:
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
BaseError(const Args & ... args)
|
BaseError(const std::string & fs, const Args & ... args)
|
||||||
: err { .level = lvlError,
|
: err { .level = lvlError,
|
||||||
.hint = hintfmt(args...)
|
.hint = hintfmt(fs, args...)
|
||||||
}
|
}
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
@ -139,7 +140,11 @@ public:
|
||||||
}
|
}
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
BaseError(ErrorInfo e)
|
BaseError(ErrorInfo && e)
|
||||||
|
: err(std::move(e))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BaseError(const ErrorInfo & e)
|
||||||
: err(e)
|
: err(e)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ std::string Hash::to_string(Base base, bool includeType) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Hash::Hash(const std::string & s, HashType type)
|
Hash::Hash(std::string_view s, HashType type)
|
||||||
: type(type)
|
: type(type)
|
||||||
{
|
{
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
@ -194,7 +194,7 @@ Hash::Hash(const std::string & s, HashType type)
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (isSRI || size == base64Len()) {
|
else if (isSRI || size == base64Len()) {
|
||||||
auto d = base64Decode(std::string(s, pos));
|
auto d = base64Decode(s.substr(pos));
|
||||||
if (d.size() != hashSize)
|
if (d.size() != hashSize)
|
||||||
throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", s);
|
throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", s);
|
||||||
assert(hashSize);
|
assert(hashSize);
|
||||||
|
@ -205,6 +205,16 @@ Hash::Hash(const std::string & s, HashType type)
|
||||||
throw BadHash("hash '%s' has wrong length for hash type '%s'", s, printHashType(type));
|
throw BadHash("hash '%s' has wrong length for hash type '%s'", s, printHashType(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Hash newHashAllowEmpty(std::string hashStr, HashType ht)
|
||||||
|
{
|
||||||
|
if (hashStr.empty()) {
|
||||||
|
Hash h(ht);
|
||||||
|
warn("found empty hash, assuming '%s'", h.to_string(SRI, true));
|
||||||
|
return h;
|
||||||
|
} else
|
||||||
|
return Hash(hashStr, ht);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
union Ctx
|
union Ctx
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,7 +42,7 @@ struct Hash
|
||||||
Subresource Integrity hash expression). If the 'type' argument
|
Subresource Integrity hash expression). If the 'type' argument
|
||||||
is htUnknown, then the hash type must be specified in the
|
is htUnknown, then the hash type must be specified in the
|
||||||
string. */
|
string. */
|
||||||
Hash(const std::string & s, HashType type = htUnknown);
|
Hash(std::string_view s, HashType type = htUnknown);
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ struct Hash
|
||||||
/* Return a string representation of the hash, in base-16, base-32
|
/* Return a string representation of the hash, in base-16, base-32
|
||||||
or base-64. By default, this is prefixed by the hash type
|
or base-64. By default, this is prefixed by the hash type
|
||||||
(e.g. "sha256:"). */
|
(e.g. "sha256:"). */
|
||||||
std::string to_string(Base base = Base32, bool includeType = true) const;
|
std::string to_string(Base base, bool includeType) const;
|
||||||
|
|
||||||
std::string gitRev() const
|
std::string gitRev() const
|
||||||
{
|
{
|
||||||
|
@ -94,6 +94,8 @@ struct Hash
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Helper that defaults empty hashes to the 0 hash. */
|
||||||
|
Hash newHashAllowEmpty(std::string hashStr, HashType ht);
|
||||||
|
|
||||||
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
|
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
|
||||||
string printHash16or32(const Hash & hash);
|
string printHash16or32(const Hash & hash);
|
||||||
|
|
|
@ -173,7 +173,7 @@ JSONObject JSONPlaceholder::object()
|
||||||
|
|
||||||
JSONPlaceholder::~JSONPlaceholder()
|
JSONPlaceholder::~JSONPlaceholder()
|
||||||
{
|
{
|
||||||
assert(!first || std::uncaught_exception());
|
assert(!first || std::uncaught_exceptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,3 @@ libutil_DIR := $(d)
|
||||||
libutil_SOURCES := $(wildcard $(d)/*.cc)
|
libutil_SOURCES := $(wildcard $(d)/*.cc)
|
||||||
|
|
||||||
libutil_LDFLAGS = $(LIBLZMA_LIBS) -lbz2 -pthread $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
|
libutil_LDFLAGS = $(LIBLZMA_LIBS) -lbz2 -pthread $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
|
||||||
|
|
||||||
libutil_LIBS = libnixrust
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ void setCurActivity(const ActivityId activityId)
|
||||||
curActivity = activityId;
|
curActivity = activityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger * logger = makeDefaultLogger();
|
Logger * logger = makeSimpleLogger(true);
|
||||||
|
|
||||||
void Logger::warn(const std::string & msg)
|
void Logger::warn(const std::string & msg)
|
||||||
{
|
{
|
||||||
|
@ -35,13 +35,19 @@ class SimpleLogger : public Logger
|
||||||
public:
|
public:
|
||||||
|
|
||||||
bool systemd, tty;
|
bool systemd, tty;
|
||||||
|
bool printBuildLogs;
|
||||||
|
|
||||||
SimpleLogger()
|
SimpleLogger(bool printBuildLogs)
|
||||||
|
: printBuildLogs(printBuildLogs)
|
||||||
{
|
{
|
||||||
systemd = getEnv("IN_SYSTEMD") == "1";
|
systemd = getEnv("IN_SYSTEMD") == "1";
|
||||||
tty = isatty(STDERR_FILENO);
|
tty = isatty(STDERR_FILENO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isVerbose() override {
|
||||||
|
return printBuildLogs;
|
||||||
|
}
|
||||||
|
|
||||||
void log(Verbosity lvl, const FormatOrString & fs) override
|
void log(Verbosity lvl, const FormatOrString & fs) override
|
||||||
{
|
{
|
||||||
if (lvl > verbosity) return;
|
if (lvl > verbosity) return;
|
||||||
|
@ -78,6 +84,18 @@ public:
|
||||||
if (lvl <= verbosity && !s.empty())
|
if (lvl <= verbosity && !s.empty())
|
||||||
log(lvl, s + "...");
|
log(lvl, s + "...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void result(ActivityId act, ResultType type, const Fields & fields) override
|
||||||
|
{
|
||||||
|
if (type == resBuildLogLine && printBuildLogs) {
|
||||||
|
auto lastLine = fields[0].s;
|
||||||
|
printError(lastLine);
|
||||||
|
}
|
||||||
|
else if (type == resPostBuildLogLine && printBuildLogs) {
|
||||||
|
auto lastLine = fields[0].s;
|
||||||
|
printError("post-build-hook: " + lastLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Verbosity verbosity = lvlInfo;
|
Verbosity verbosity = lvlInfo;
|
||||||
|
@ -102,9 +120,9 @@ void writeToStderr(const string & s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger * makeDefaultLogger()
|
Logger * makeSimpleLogger(bool printBuildLogs)
|
||||||
{
|
{
|
||||||
return new SimpleLogger();
|
return new SimpleLogger(printBuildLogs);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<uint64_t> nextId{(uint64_t) getpid() << 32};
|
std::atomic<uint64_t> nextId{(uint64_t) getpid() << 32};
|
||||||
|
@ -121,6 +139,10 @@ struct JSONLogger : Logger {
|
||||||
|
|
||||||
JSONLogger(Logger & prevLogger) : prevLogger(prevLogger) { }
|
JSONLogger(Logger & prevLogger) : prevLogger(prevLogger) { }
|
||||||
|
|
||||||
|
bool isVerbose() override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void addFields(nlohmann::json & json, const Fields & fields)
|
void addFields(nlohmann::json & json, const Fields & fields)
|
||||||
{
|
{
|
||||||
if (fields.empty()) return;
|
if (fields.empty()) return;
|
||||||
|
@ -251,7 +273,7 @@ bool handleJSONLogMessage(const std::string & msg,
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
logError({
|
logError({
|
||||||
.name = "Json log message",
|
.name = "Json log message",
|
||||||
.hint = hintfmt("bad log message from builder: %s", e.what())
|
.hint = hintfmt("bad log message from builder: %s", e.what())
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,6 +18,7 @@ typedef enum {
|
||||||
actSubstitute = 108,
|
actSubstitute = 108,
|
||||||
actQueryPathInfo = 109,
|
actQueryPathInfo = 109,
|
||||||
actPostBuildHook = 110,
|
actPostBuildHook = 110,
|
||||||
|
actBuildWaiting = 111,
|
||||||
} ActivityType;
|
} ActivityType;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -54,6 +55,11 @@ public:
|
||||||
|
|
||||||
virtual ~Logger() { }
|
virtual ~Logger() { }
|
||||||
|
|
||||||
|
virtual void stop() { };
|
||||||
|
|
||||||
|
// Whether the logger prints the whole build log
|
||||||
|
virtual bool isVerbose() { return false; }
|
||||||
|
|
||||||
virtual void log(Verbosity lvl, const FormatOrString & fs) = 0;
|
virtual void log(Verbosity lvl, const FormatOrString & fs) = 0;
|
||||||
|
|
||||||
void log(const FormatOrString & fs)
|
void log(const FormatOrString & fs)
|
||||||
|
@ -140,7 +146,7 @@ struct PushActivity
|
||||||
|
|
||||||
extern Logger * logger;
|
extern Logger * logger;
|
||||||
|
|
||||||
Logger * makeDefaultLogger();
|
Logger * makeSimpleLogger(bool printBuildLogs = true);
|
||||||
|
|
||||||
Logger * makeJSONLogger(Logger & prevLogger);
|
Logger * makeJSONLogger(Logger & prevLogger);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#if 0
|
||||||
#include "logging.hh"
|
#include "logging.hh"
|
||||||
#include "rust-ffi.hh"
|
#include "rust-ffi.hh"
|
||||||
|
|
||||||
|
@ -20,3 +21,4 @@ std::ostream & operator << (std::ostream & str, const String & s)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#if 0
|
||||||
|
|
||||||
#include "serialise.hh"
|
#include "serialise.hh"
|
||||||
|
|
||||||
|
@ -185,3 +186,4 @@ struct Result
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -52,10 +52,10 @@ size_t threshold = 256 * 1024 * 1024;
|
||||||
|
|
||||||
static void warnLargeDump()
|
static void warnLargeDump()
|
||||||
{
|
{
|
||||||
logWarning(ErrorInfo {
|
logWarning({
|
||||||
.name = "Large path",
|
.name = "Large path",
|
||||||
.description = "dumping very large path (> 256 MiB); this may run out of memory"
|
.description = "dumping very large path (> 256 MiB); this may run out of memory"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,28 +11,28 @@ namespace nix {
|
||||||
// values taken from: https://tools.ietf.org/html/rfc1321
|
// values taken from: https://tools.ietf.org/html/rfc1321
|
||||||
auto s1 = "";
|
auto s1 = "";
|
||||||
auto hash = hashString(HashType::htMD5, s1);
|
auto hash = hashString(HashType::htMD5, s1);
|
||||||
ASSERT_EQ(hash.to_string(Base::Base16), "md5:d41d8cd98f00b204e9800998ecf8427e");
|
ASSERT_EQ(hash.to_string(Base::Base16, true), "md5:d41d8cd98f00b204e9800998ecf8427e");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(hashString, testKnownMD5Hashes2) {
|
TEST(hashString, testKnownMD5Hashes2) {
|
||||||
// values taken from: https://tools.ietf.org/html/rfc1321
|
// values taken from: https://tools.ietf.org/html/rfc1321
|
||||||
auto s2 = "abc";
|
auto s2 = "abc";
|
||||||
auto hash = hashString(HashType::htMD5, s2);
|
auto hash = hashString(HashType::htMD5, s2);
|
||||||
ASSERT_EQ(hash.to_string(Base::Base16), "md5:900150983cd24fb0d6963f7d28e17f72");
|
ASSERT_EQ(hash.to_string(Base::Base16, true), "md5:900150983cd24fb0d6963f7d28e17f72");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(hashString, testKnownSHA1Hashes1) {
|
TEST(hashString, testKnownSHA1Hashes1) {
|
||||||
// values taken from: https://tools.ietf.org/html/rfc3174
|
// values taken from: https://tools.ietf.org/html/rfc3174
|
||||||
auto s = "abc";
|
auto s = "abc";
|
||||||
auto hash = hashString(HashType::htSHA1, s);
|
auto hash = hashString(HashType::htSHA1, s);
|
||||||
ASSERT_EQ(hash.to_string(Base::Base16),"sha1:a9993e364706816aba3e25717850c26c9cd0d89d");
|
ASSERT_EQ(hash.to_string(Base::Base16, true),"sha1:a9993e364706816aba3e25717850c26c9cd0d89d");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(hashString, testKnownSHA1Hashes2) {
|
TEST(hashString, testKnownSHA1Hashes2) {
|
||||||
// values taken from: https://tools.ietf.org/html/rfc3174
|
// values taken from: https://tools.ietf.org/html/rfc3174
|
||||||
auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
|
auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
|
||||||
auto hash = hashString(HashType::htSHA1, s);
|
auto hash = hashString(HashType::htSHA1, s);
|
||||||
ASSERT_EQ(hash.to_string(Base::Base16),"sha1:84983e441c3bd26ebaae4aa1f95129e5e54670f1");
|
ASSERT_EQ(hash.to_string(Base::Base16, true),"sha1:84983e441c3bd26ebaae4aa1f95129e5e54670f1");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(hashString, testKnownSHA256Hashes1) {
|
TEST(hashString, testKnownSHA256Hashes1) {
|
||||||
|
@ -40,7 +40,7 @@ namespace nix {
|
||||||
auto s = "abc";
|
auto s = "abc";
|
||||||
|
|
||||||
auto hash = hashString(HashType::htSHA256, s);
|
auto hash = hashString(HashType::htSHA256, s);
|
||||||
ASSERT_EQ(hash.to_string(Base::Base16),
|
ASSERT_EQ(hash.to_string(Base::Base16, true),
|
||||||
"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
|
"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ namespace nix {
|
||||||
// values taken from: https://tools.ietf.org/html/rfc4634
|
// values taken from: https://tools.ietf.org/html/rfc4634
|
||||||
auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
|
auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
|
||||||
auto hash = hashString(HashType::htSHA256, s);
|
auto hash = hashString(HashType::htSHA256, s);
|
||||||
ASSERT_EQ(hash.to_string(Base::Base16),
|
ASSERT_EQ(hash.to_string(Base::Base16, true),
|
||||||
"sha256:248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
|
"sha256:248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ namespace nix {
|
||||||
// values taken from: https://tools.ietf.org/html/rfc4634
|
// values taken from: https://tools.ietf.org/html/rfc4634
|
||||||
auto s = "abc";
|
auto s = "abc";
|
||||||
auto hash = hashString(HashType::htSHA512, s);
|
auto hash = hashString(HashType::htSHA512, s);
|
||||||
ASSERT_EQ(hash.to_string(Base::Base16),
|
ASSERT_EQ(hash.to_string(Base::Base16, true),
|
||||||
"sha512:ddaf35a193617abacc417349ae20413112e6fa4e89a9"
|
"sha512:ddaf35a193617abacc417349ae20413112e6fa4e89a9"
|
||||||
"7ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd"
|
"7ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd"
|
||||||
"454d4423643ce80e2a9ac94fa54ca49f");
|
"454d4423643ce80e2a9ac94fa54ca49f");
|
||||||
|
@ -67,7 +67,7 @@ namespace nix {
|
||||||
auto s = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
|
auto s = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
|
||||||
|
|
||||||
auto hash = hashString(HashType::htSHA512, s);
|
auto hash = hashString(HashType::htSHA512, s);
|
||||||
ASSERT_EQ(hash.to_string(Base::Base16),
|
ASSERT_EQ(hash.to_string(Base::Base16, true),
|
||||||
"sha512:8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa1"
|
"sha512:8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa1"
|
||||||
"7299aeadb6889018501d289e4900f7e4331b99dec4b5433a"
|
"7299aeadb6889018501d289e4900f7e4331b99dec4b5433a"
|
||||||
"c7d329eeb6dd26545e96e55b874be909");
|
"c7d329eeb6dd26545e96e55b874be909");
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace nix {
|
||||||
logger->logEI(e.info());
|
logger->logEI(e.info());
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
|
|
||||||
ASSERT_STREQ(str.c_str(),"\x1B[31;1merror:\x1B[0m\x1B[34;1m --- TestError ------------------------------------ error-unit-test\x1B[0m\nan error for testing purposes\n");
|
ASSERT_STREQ(str.c_str(),"\x1B[31;1merror:\x1B[0m\x1B[34;1m --- TestError --- error-unit-test\x1B[0m\nan error for testing purposes\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace nix {
|
||||||
logger->logEI(ei);
|
logger->logEI(ei);
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
|
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- TestError ------------------------------------ error-unit-test\x1B[0m\n\x1B[33;1m\x1B[0minitial error\x1B[0m; subsequent error message.\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- TestError --- error-unit-test\x1B[0m\n\x1B[33;1m\x1B[0minitial error\x1B[0m; subsequent error message.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ namespace nix {
|
||||||
logError(e.info());
|
logError(e.info());
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
|
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError ------------------------------------- error-unit-test\x1B[0m\n\x1B[33;1m\x1B[0mstatting file\x1B[0m: \x1B[33;1mBad file descriptor\x1B[0m\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\n\x1B[33;1m\x1B[0mstatting file\x1B[0m: \x1B[33;1mBad file descriptor\x1B[0m\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ namespace nix {
|
||||||
});
|
});
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[32;1minfo:\x1B[0m\x1B[34;1m --- Info name ------------------------------------- error-unit-test\x1B[0m\nInfo description\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[32;1minfo:\x1B[0m\x1B[34;1m --- Info name --- error-unit-test\x1B[0m\nInfo description\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(logEI, loggingErrorOnTalkativeLevel) {
|
TEST(logEI, loggingErrorOnTalkativeLevel) {
|
||||||
|
@ -88,7 +88,7 @@ namespace nix {
|
||||||
});
|
});
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[32;1mtalk:\x1B[0m\x1B[34;1m --- Talkative name -------------------------------- error-unit-test\x1B[0m\nTalkative description\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[32;1mtalk:\x1B[0m\x1B[34;1m --- Talkative name --- error-unit-test\x1B[0m\nTalkative description\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(logEI, loggingErrorOnChattyLevel) {
|
TEST(logEI, loggingErrorOnChattyLevel) {
|
||||||
|
@ -102,7 +102,7 @@ namespace nix {
|
||||||
});
|
});
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[32;1mchat:\x1B[0m\x1B[34;1m --- Chatty name ----------------------------------- error-unit-test\x1B[0m\nTalkative description\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[32;1mchat:\x1B[0m\x1B[34;1m --- Chatty name --- error-unit-test\x1B[0m\nTalkative description\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(logEI, loggingErrorOnDebugLevel) {
|
TEST(logEI, loggingErrorOnDebugLevel) {
|
||||||
|
@ -116,7 +116,7 @@ namespace nix {
|
||||||
});
|
});
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[33;1mdebug:\x1B[0m\x1B[34;1m --- Debug name ----------------------------------- error-unit-test\x1B[0m\nDebug description\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[33;1mdebug:\x1B[0m\x1B[34;1m --- Debug name --- error-unit-test\x1B[0m\nDebug description\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(logEI, loggingErrorOnVomitLevel) {
|
TEST(logEI, loggingErrorOnVomitLevel) {
|
||||||
|
@ -130,7 +130,7 @@ namespace nix {
|
||||||
});
|
});
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[32;1mvomit:\x1B[0m\x1B[34;1m --- Vomit name ----------------------------------- error-unit-test\x1B[0m\nVomit description\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[32;1mvomit:\x1B[0m\x1B[34;1m --- Vomit name --- error-unit-test\x1B[0m\nVomit description\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
|
@ -147,7 +147,7 @@ namespace nix {
|
||||||
});
|
});
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- name ----------------------------------------- error-unit-test\x1B[0m\nerror description\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- name --- error-unit-test\x1B[0m\nerror description\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(logError, logErrorWithPreviousAndNextLinesOfCode) {
|
TEST(logError, logErrorWithPreviousAndNextLinesOfCode) {
|
||||||
|
@ -171,7 +171,7 @@ namespace nix {
|
||||||
|
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name ----------------------------------- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nerror with code lines\n\n 39| previous line of code\n 40| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 41| next line of code\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nerror with code lines\n\n 39| previous line of code\n 40| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 41| next line of code\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(logError, logErrorWithoutLinesOfCode) {
|
TEST(logError, logErrorWithoutLinesOfCode) {
|
||||||
|
@ -190,7 +190,7 @@ namespace nix {
|
||||||
}});
|
}});
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name ----------------------------------- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nerror without any code lines.\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nerror without any code lines.\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(logError, logErrorWithOnlyHintAndName) {
|
TEST(logError, logErrorWithOnlyHintAndName) {
|
||||||
|
@ -206,7 +206,7 @@ namespace nix {
|
||||||
}});
|
}});
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name ----------------------------------- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nhint \x1B[33;1monly\x1B[0m\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nhint \x1B[33;1monly\x1B[0m\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ namespace nix {
|
||||||
});
|
});
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- name --------------------------------------- error-unit-test\x1B[0m\nerror description\n\nthere was a \x1B[33;1mwarning\x1B[0m\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- name --- error-unit-test\x1B[0m\nerror description\n\nthere was a \x1B[33;1mwarning\x1B[0m\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(logWarning, logWarningWithFileLineNumAndCode) {
|
TEST(logWarning, logWarningWithFileLineNumAndCode) {
|
||||||
|
@ -249,7 +249,7 @@ namespace nix {
|
||||||
|
|
||||||
|
|
||||||
auto str = testing::internal::GetCapturedStderr();
|
auto str = testing::internal::GetCapturedStderr();
|
||||||
ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- warning name ------------------------------- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nwarning description\n\n 40| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
|
ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- warning name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nwarning description\n\n 40| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
127
src/libutil/tests/pool.cc
Normal file
127
src/libutil/tests/pool.cc
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
#include "pool.hh"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct TestResource
|
||||||
|
{
|
||||||
|
|
||||||
|
TestResource() {
|
||||||
|
static int counter = 0;
|
||||||
|
num = counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dummyValue = 1;
|
||||||
|
bool good = true;
|
||||||
|
int num;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* Pool
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
TEST(Pool, freshPoolHasZeroCountAndSpecifiedCapacity) {
|
||||||
|
auto isGood = [](const ref<TestResource> & r) { return r->good; };
|
||||||
|
auto createResource = []() { return make_ref<TestResource>(); };
|
||||||
|
|
||||||
|
Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
|
||||||
|
|
||||||
|
ASSERT_EQ(pool.count(), 0);
|
||||||
|
ASSERT_EQ(pool.capacity(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Pool, freshPoolCanGetAResource) {
|
||||||
|
auto isGood = [](const ref<TestResource> & r) { return r->good; };
|
||||||
|
auto createResource = []() { return make_ref<TestResource>(); };
|
||||||
|
|
||||||
|
Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
|
||||||
|
ASSERT_EQ(pool.count(), 0);
|
||||||
|
|
||||||
|
TestResource r = *(pool.get());
|
||||||
|
|
||||||
|
ASSERT_EQ(pool.count(), 1);
|
||||||
|
ASSERT_EQ(pool.capacity(), 1);
|
||||||
|
ASSERT_EQ(r.dummyValue, 1);
|
||||||
|
ASSERT_EQ(r.good, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Pool, capacityCanBeIncremented) {
|
||||||
|
auto isGood = [](const ref<TestResource> & r) { return r->good; };
|
||||||
|
auto createResource = []() { return make_ref<TestResource>(); };
|
||||||
|
|
||||||
|
Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
|
||||||
|
ASSERT_EQ(pool.capacity(), 1);
|
||||||
|
pool.incCapacity();
|
||||||
|
ASSERT_EQ(pool.capacity(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Pool, capacityCanBeDecremented) {
|
||||||
|
auto isGood = [](const ref<TestResource> & r) { return r->good; };
|
||||||
|
auto createResource = []() { return make_ref<TestResource>(); };
|
||||||
|
|
||||||
|
Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
|
||||||
|
ASSERT_EQ(pool.capacity(), 1);
|
||||||
|
pool.decCapacity();
|
||||||
|
ASSERT_EQ(pool.capacity(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Pool, flushBadDropsOutOfScopeResources) {
|
||||||
|
auto isGood = [](const ref<TestResource> & r) { return false; };
|
||||||
|
auto createResource = []() { return make_ref<TestResource>(); };
|
||||||
|
|
||||||
|
Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto _r = pool.get();
|
||||||
|
ASSERT_EQ(pool.count(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.flushBad();
|
||||||
|
ASSERT_EQ(pool.count(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that the resources we allocate are being reused when they are still good.
|
||||||
|
TEST(Pool, reuseResource) {
|
||||||
|
auto isGood = [](const ref<TestResource> & r) { return true; };
|
||||||
|
auto createResource = []() { return make_ref<TestResource>(); };
|
||||||
|
|
||||||
|
Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
|
||||||
|
|
||||||
|
// Compare the instance counter between the two handles. We expect them to be equal
|
||||||
|
// as the pool should hand out the same (still) good one again.
|
||||||
|
int counter = -1;
|
||||||
|
{
|
||||||
|
Pool<TestResource>::Handle h = pool.get();
|
||||||
|
counter = h->num;
|
||||||
|
} // the first handle goes out of scope
|
||||||
|
|
||||||
|
{ // the second handle should contain the same resource (with the same counter value)
|
||||||
|
Pool<TestResource>::Handle h = pool.get();
|
||||||
|
ASSERT_EQ(h->num, counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that the resources we allocate are being thrown away when they are no longer good.
|
||||||
|
TEST(Pool, badResourceIsNotReused) {
|
||||||
|
auto isGood = [](const ref<TestResource> & r) { return false; };
|
||||||
|
auto createResource = []() { return make_ref<TestResource>(); };
|
||||||
|
|
||||||
|
Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
|
||||||
|
|
||||||
|
// Compare the instance counter between the two handles. We expect them
|
||||||
|
// to *not* be equal as the pool should hand out a new instance after
|
||||||
|
// the first one was returned.
|
||||||
|
int counter = -1;
|
||||||
|
{
|
||||||
|
Pool<TestResource>::Handle h = pool.get();
|
||||||
|
counter = h->num;
|
||||||
|
} // the first handle goes out of scope
|
||||||
|
|
||||||
|
{
|
||||||
|
// the second handle should contain a different resource (with a
|
||||||
|
//different counter value)
|
||||||
|
Pool<TestResource>::Handle h = pool.get();
|
||||||
|
ASSERT_NE(h->num, counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
#include "ref.hh"
|
#include "ref.hh"
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
@ -25,7 +24,6 @@ typedef string Path;
|
||||||
typedef list<Path> Paths;
|
typedef list<Path> Paths;
|
||||||
typedef set<Path> PathSet;
|
typedef set<Path> PathSet;
|
||||||
|
|
||||||
|
|
||||||
/* Helper class to run code at startup. */
|
/* Helper class to run code at startup. */
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct OnStartup
|
struct OnStartup
|
||||||
|
@ -33,5 +31,4 @@ struct OnStartup
|
||||||
OnStartup(T && t) { t(); }
|
OnStartup(T && t) { t(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
extern char * * environ;
|
extern char * * environ __attribute__((weak));
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -314,7 +314,7 @@ string readFile(const Path & path)
|
||||||
void readFile(const Path & path, Sink & sink)
|
void readFile(const Path & path, Sink & sink)
|
||||||
{
|
{
|
||||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("opening file '%s'", path);
|
throw SysError("opening file '%s'", path);
|
||||||
drainFD(fd.get(), sink);
|
drainFD(fd.get(), sink);
|
||||||
}
|
}
|
||||||
|
@ -972,7 +972,7 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
||||||
{
|
{
|
||||||
auto wrapper = [&]() {
|
auto wrapper = [&]() {
|
||||||
if (!options.allowVfork)
|
if (!options.allowVfork)
|
||||||
logger = makeDefaultLogger();
|
logger = makeSimpleLogger();
|
||||||
try {
|
try {
|
||||||
#if __linux__
|
#if __linux__
|
||||||
if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
|
if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
|
||||||
|
@ -1199,7 +1199,7 @@ void _interrupted()
|
||||||
/* Block user interrupts while an exception is being handled.
|
/* Block user interrupts while an exception is being handled.
|
||||||
Throwing an exception while another exception is being handled
|
Throwing an exception while another exception is being handled
|
||||||
kills the program! */
|
kills the program! */
|
||||||
if (!interruptThrown && !std::uncaught_exception()) {
|
if (!interruptThrown && !std::uncaught_exceptions()) {
|
||||||
interruptThrown = true;
|
interruptThrown = true;
|
||||||
throw Interrupted("interrupted by the user");
|
throw Interrupted("interrupted by the user");
|
||||||
}
|
}
|
||||||
|
@ -1297,7 +1297,7 @@ bool statusOk(int status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool hasPrefix(const string & s, const string & prefix)
|
bool hasPrefix(std::string_view s, std::string_view prefix)
|
||||||
{
|
{
|
||||||
return s.compare(0, prefix.size(), prefix) == 0;
|
return s.compare(0, prefix.size(), prefix) == 0;
|
||||||
}
|
}
|
||||||
|
@ -1391,7 +1391,7 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in
|
||||||
static char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
static char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
|
||||||
string base64Encode(const string & s)
|
string base64Encode(std::string_view s)
|
||||||
{
|
{
|
||||||
string res;
|
string res;
|
||||||
int data = 0, nbits = 0;
|
int data = 0, nbits = 0;
|
||||||
|
@ -1412,7 +1412,7 @@ string base64Encode(const string & s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string base64Decode(const string & s)
|
string base64Decode(std::string_view s)
|
||||||
{
|
{
|
||||||
bool init = false;
|
bool init = false;
|
||||||
char decode[256];
|
char decode[256];
|
||||||
|
|
|
@ -417,7 +417,7 @@ template<class N> bool string2Float(const string & s, N & n)
|
||||||
|
|
||||||
|
|
||||||
/* Return true iff `s' starts with `prefix'. */
|
/* Return true iff `s' starts with `prefix'. */
|
||||||
bool hasPrefix(const string & s, const string & prefix);
|
bool hasPrefix(std::string_view s, std::string_view prefix);
|
||||||
|
|
||||||
|
|
||||||
/* Return true iff `s' ends in `suffix'. */
|
/* Return true iff `s' ends in `suffix'. */
|
||||||
|
@ -456,12 +456,11 @@ std::string filterANSIEscapes(const std::string & s,
|
||||||
|
|
||||||
|
|
||||||
/* Base64 encoding/decoding. */
|
/* Base64 encoding/decoding. */
|
||||||
string base64Encode(const string & s);
|
string base64Encode(std::string_view s);
|
||||||
string base64Decode(const string & s);
|
string base64Decode(std::string_view s);
|
||||||
|
|
||||||
|
|
||||||
/* Get a value for the specified key from an associate container, or a
|
/* Get a value for the specified key from an associate container. */
|
||||||
default value if the key doesn't exist. */
|
|
||||||
template <class T>
|
template <class T>
|
||||||
std::optional<typename T::mapped_type> get(const T & map, const typename T::key_type & key)
|
std::optional<typename T::mapped_type> get(const T & map, const typename T::key_type & key)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
extern char * * environ;
|
extern char * * environ __attribute__((weak));
|
||||||
|
|
||||||
/* Recreate the effect of the perl shellwords function, breaking up a
|
/* Recreate the effect of the perl shellwords function, breaking up a
|
||||||
* string into arguments like a shell word, including escapes
|
* string into arguments like a shell word, including escapes
|
||||||
|
@ -363,17 +363,16 @@ static void _main(int argc, char * * argv)
|
||||||
if (!drv)
|
if (!drv)
|
||||||
throw Error("the 'bashInteractive' attribute in <nixpkgs> did not evaluate to a derivation");
|
throw Error("the 'bashInteractive' attribute in <nixpkgs> did not evaluate to a derivation");
|
||||||
|
|
||||||
pathsToBuild.emplace_back(store->parseStorePath(drv->queryDrvPath()));
|
pathsToBuild.push_back({store->parseStorePath(drv->queryDrvPath())});
|
||||||
|
|
||||||
shell = drv->queryOutPath() + "/bin/bash";
|
shell = drv->queryOutPath() + "/bin/bash";
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
logWarning(
|
logWarning({
|
||||||
ErrorInfo {
|
.name = "bashInteractive",
|
||||||
.name = "bashInteractive",
|
.hint = hintfmt("%s; will use bash from your environment",
|
||||||
.hint = hintfmt("%s; will use bash from your environment",
|
(e.info().hint ? e.info().hint->str() : ""))
|
||||||
(e.info().hint ? e.info().hint->str() : ""))
|
});
|
||||||
});
|
|
||||||
shell = "bash";
|
shell = "bash";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -382,9 +381,9 @@ static void _main(int argc, char * * argv)
|
||||||
for (const auto & input : drv.inputDrvs)
|
for (const auto & input : drv.inputDrvs)
|
||||||
if (std::all_of(envExclude.cbegin(), envExclude.cend(),
|
if (std::all_of(envExclude.cbegin(), envExclude.cend(),
|
||||||
[&](const string & exclude) { return !std::regex_search(store->printStorePath(input.first), std::regex(exclude)); }))
|
[&](const string & exclude) { return !std::regex_search(store->printStorePath(input.first), std::regex(exclude)); }))
|
||||||
pathsToBuild.emplace_back(input.first, input.second);
|
pathsToBuild.push_back({input.first, input.second});
|
||||||
for (const auto & src : drv.inputSrcs)
|
for (const auto & src : drv.inputSrcs)
|
||||||
pathsToBuild.emplace_back(src);
|
pathsToBuild.push_back({src});
|
||||||
|
|
||||||
buildPaths(pathsToBuild);
|
buildPaths(pathsToBuild);
|
||||||
|
|
||||||
|
@ -477,6 +476,8 @@ static void _main(int argc, char * * argv)
|
||||||
|
|
||||||
restoreSignals();
|
restoreSignals();
|
||||||
|
|
||||||
|
logger->stop();
|
||||||
|
|
||||||
execvp(shell->c_str(), argPtrs.data());
|
execvp(shell->c_str(), argPtrs.data());
|
||||||
|
|
||||||
throw SysError("executing shell '%s'", *shell);
|
throw SysError("executing shell '%s'", *shell);
|
||||||
|
@ -498,7 +499,7 @@ static void _main(int argc, char * * argv)
|
||||||
if (outputName == "")
|
if (outputName == "")
|
||||||
throw Error("derivation '%s' lacks an 'outputName' attribute", drvPath);
|
throw Error("derivation '%s' lacks an 'outputName' attribute", drvPath);
|
||||||
|
|
||||||
pathsToBuild.emplace_back(store->parseStorePath(drvPath), StringSet{outputName});
|
pathsToBuild.push_back({store->parseStorePath(drvPath), {outputName}});
|
||||||
|
|
||||||
std::string drvPrefix;
|
std::string drvPrefix;
|
||||||
auto i = drvPrefixes.find(drvPath);
|
auto i = drvPrefixes.find(drvPath);
|
||||||
|
@ -526,6 +527,8 @@ static void _main(int argc, char * * argv)
|
||||||
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
|
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
|
||||||
store2->addPermRoot(store->parseStorePath(symlink.second), absPath(symlink.first), true);
|
store2->addPermRoot(store->parseStorePath(symlink.second), absPath(symlink.first), true);
|
||||||
|
|
||||||
|
logger->stop();
|
||||||
|
|
||||||
for (auto & path : outPaths)
|
for (auto & path : outPaths)
|
||||||
std::cout << path << '\n';
|
std::cout << path << '\n';
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue