mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-22 05:56:15 +02:00
Merge remote-tracking branch 'nixos/master'
This commit is contained in:
commit
8cc8551afd
31 changed files with 736 additions and 404 deletions
|
@ -1,75 +0,0 @@
|
||||||
diff --git a/darwin_stop_world.c b/darwin_stop_world.c
|
|
||||||
index 3dbaa3fb..36a1d1f7 100644
|
|
||||||
--- a/darwin_stop_world.c
|
|
||||||
+++ b/darwin_stop_world.c
|
|
||||||
@@ -352,6 +352,7 @@ GC_INNER void GC_push_all_stacks(void)
|
|
||||||
int nthreads = 0;
|
|
||||||
word total_size = 0;
|
|
||||||
mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ;
|
|
||||||
+ size_t stack_limit;
|
|
||||||
if (!EXPECT(GC_thr_initialized, TRUE))
|
|
||||||
GC_thr_init();
|
|
||||||
|
|
||||||
@@ -407,6 +408,19 @@ GC_INNER void GC_push_all_stacks(void)
|
|
||||||
GC_push_all_stack_sections(lo, hi, p->traced_stack_sect);
|
|
||||||
}
|
|
||||||
if (altstack_lo) {
|
|
||||||
+ // When a thread goes into a coroutine, we lose its original sp until
|
|
||||||
+ // control flow returns to the thread.
|
|
||||||
+ // While in the coroutine, the sp points outside the thread stack,
|
|
||||||
+ // so we can detect this and push the entire thread stack instead,
|
|
||||||
+ // as an approximation.
|
|
||||||
+ // We assume that the coroutine has similarly added its entire stack.
|
|
||||||
+ // This could be made accurate by cooperating with the application
|
|
||||||
+ // via new functions and/or callbacks.
|
|
||||||
+ stack_limit = pthread_get_stacksize_np(p->id);
|
|
||||||
+ if (altstack_lo >= altstack_hi || altstack_lo < altstack_hi - stack_limit) { // sp outside stack
|
|
||||||
+ altstack_lo = altstack_hi - stack_limit;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
total_size += altstack_hi - altstack_lo;
|
|
||||||
GC_push_all_stack(altstack_lo, altstack_hi);
|
|
||||||
}
|
|
||||||
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
|
|
||||||
index 4b2c429..1fb4c52 100644
|
|
||||||
--- a/pthread_stop_world.c
|
|
||||||
+++ b/pthread_stop_world.c
|
|
||||||
@@ -781,4 +781,6 @@ GC_INNER void GC_push_all_stacks(void)
|
|
||||||
pthread_t self = pthread_self();
|
|
||||||
word total_size = 0;
|
|
||||||
+ size_t stack_limit;
|
|
||||||
+ pthread_attr_t pattr;
|
|
||||||
|
|
||||||
if (!EXPECT(GC_thr_initialized, TRUE))
|
|
||||||
@@ -722,6 +724,31 @@ GC_INNER void GC_push_all_stacks(void)
|
|
||||||
hi = p->altstack + p->altstack_size;
|
|
||||||
/* FIXME: Need to scan the normal stack too, but how ? */
|
|
||||||
/* FIXME: Assume stack grows down */
|
|
||||||
+ } else {
|
|
||||||
+ if (pthread_getattr_np(p->id, &pattr)) {
|
|
||||||
+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
|
|
||||||
+ }
|
|
||||||
+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) {
|
|
||||||
+ ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!");
|
|
||||||
+ }
|
|
||||||
+ if (pthread_attr_destroy(&pattr)) {
|
|
||||||
+ ABORT("GC_push_all_stacks: pthread_attr_destroy failed!");
|
|
||||||
+ }
|
|
||||||
+ // When a thread goes into a coroutine, we lose its original sp until
|
|
||||||
+ // control flow returns to the thread.
|
|
||||||
+ // While in the coroutine, the sp points outside the thread stack,
|
|
||||||
+ // so we can detect this and push the entire thread stack instead,
|
|
||||||
+ // as an approximation.
|
|
||||||
+ // We assume that the coroutine has similarly added its entire stack.
|
|
||||||
+ // This could be made accurate by cooperating with the application
|
|
||||||
+ // via new functions and/or callbacks.
|
|
||||||
+ #ifndef STACK_GROWS_UP
|
|
||||||
+ if (lo >= hi || lo < hi - stack_limit) { // sp outside stack
|
|
||||||
+ lo = hi - stack_limit;
|
|
||||||
+ }
|
|
||||||
+ #else
|
|
||||||
+ #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix."
|
|
||||||
+ #endif
|
|
||||||
}
|
|
||||||
GC_push_all_stack_sections(lo, hi, traced_stack_sect);
|
|
||||||
# ifdef STACK_GROWS_UP
|
|
|
@ -1,6 +1,11 @@
|
||||||
|
[book]
|
||||||
|
title = "Nix Reference Manual"
|
||||||
|
|
||||||
[output.html]
|
[output.html]
|
||||||
additional-css = ["custom.css"]
|
additional-css = ["custom.css"]
|
||||||
additional-js = ["redirects.js"]
|
additional-js = ["redirects.js"]
|
||||||
|
edit-url-template = "https://github.com/NixOS/nix/tree/master/doc/manual/{path}"
|
||||||
|
git-repository-url = "https://github.com/NixOS/nix"
|
||||||
|
|
||||||
[preprocessor.anchors]
|
[preprocessor.anchors]
|
||||||
renderers = ["html"]
|
renderers = ["html"]
|
||||||
|
|
|
@ -3,18 +3,18 @@
|
||||||
- [derivation]{#gloss-derivation}\
|
- [derivation]{#gloss-derivation}\
|
||||||
A description of a build task. The result of a derivation is a
|
A description of a build task. The result of a derivation is a
|
||||||
store object. Derivations are typically specified in Nix expressions
|
store object. Derivations are typically specified in Nix expressions
|
||||||
using the [`derivation` primitive](language/derivations.md). These are
|
using the [`derivation` primitive](./language/derivations.md). These are
|
||||||
translated into low-level *store derivations* (implicitly by
|
translated into low-level *store derivations* (implicitly by
|
||||||
`nix-env` and `nix-build`, or explicitly by `nix-instantiate`).
|
`nix-env` and `nix-build`, or explicitly by `nix-instantiate`).
|
||||||
|
|
||||||
- [content-addressed derivation]{#gloss-content-addressed-derivation}\
|
- [content-addressed derivation]{#gloss-content-addressed-derivation}\
|
||||||
A derivation which has the
|
A derivation which has the
|
||||||
[`__contentAddressed`](language/advanced-attributes.md#adv-attr-__contentAddressed)
|
[`__contentAddressed`](./language/advanced-attributes.md#adv-attr-__contentAddressed)
|
||||||
attribute set to `true`.
|
attribute set to `true`.
|
||||||
|
|
||||||
- [fixed-output derivation]{#gloss-fixed-output-derivation}\
|
- [fixed-output derivation]{#gloss-fixed-output-derivation}\
|
||||||
A derivation which includes the
|
A derivation which includes the
|
||||||
[`outputHash`](language/advanced-attributes.md#adv-attr-outputHash) attribute.
|
[`outputHash`](./language/advanced-attributes.md#adv-attr-outputHash) attribute.
|
||||||
|
|
||||||
- [store]{#gloss-store}\
|
- [store]{#gloss-store}\
|
||||||
The location in the file system where store objects live. Typically
|
The location in the file system where store objects live. Typically
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
- [substituter]{#gloss-substituter}\
|
- [substituter]{#gloss-substituter}\
|
||||||
A *substituter* is an additional store from which Nix will
|
A *substituter* is an additional store from which Nix will
|
||||||
copy store objects it doesn't have. For details, see the
|
copy store objects it doesn't have. For details, see the
|
||||||
[`substituters` option](command-ref/conf-file.html#conf-substituters).
|
[`substituters` option](./command-ref/conf-file.md#conf-substituters).
|
||||||
|
|
||||||
- [purity]{#gloss-purity}\
|
- [purity]{#gloss-purity}\
|
||||||
The assumption that equal Nix derivations when run always produce
|
The assumption that equal Nix derivations when run always produce
|
||||||
|
@ -139,7 +139,7 @@
|
||||||
An automatically generated store object that consists of a set of
|
An automatically generated store object that consists of a set of
|
||||||
symlinks to “active” applications, i.e., other store paths. These
|
symlinks to “active” applications, i.e., other store paths. These
|
||||||
are generated automatically by
|
are generated automatically by
|
||||||
[`nix-env`](command-ref/nix-env.md). See *profiles*.
|
[`nix-env`](./command-ref/nix-env.md). See *profiles*.
|
||||||
|
|
||||||
- [profile]{#gloss-profile}\
|
- [profile]{#gloss-profile}\
|
||||||
A symlink to the current *user environment* of a user, e.g.,
|
A symlink to the current *user environment* of a user, e.g.,
|
||||||
|
@ -150,7 +150,9 @@
|
||||||
store. It can contain regular files, directories and symbolic
|
store. It can contain regular files, directories and symbolic
|
||||||
links. NARs are generated and unpacked using `nix-store --dump`
|
links. NARs are generated and unpacked using `nix-store --dump`
|
||||||
and `nix-store --restore`.
|
and `nix-store --restore`.
|
||||||
|
|
||||||
- [`∅`]{#gloss-emtpy-set}\
|
- [`∅`]{#gloss-emtpy-set}\
|
||||||
The empty set symbol. In the context of profile history, this denotes a package is not present in a particular version of the profile.
|
The empty set symbol. In the context of profile history, this denotes a package is not present in a particular version of the profile.
|
||||||
|
|
||||||
- [`ε`]{#gloss-epsilon}\
|
- [`ε`]{#gloss-epsilon}\
|
||||||
The epsilon symbol. In the context of a package, this means the version is empty. More precisely, the derivation does not have a version attribute.
|
The epsilon symbol. In the context of a package, this means the version is empty. More precisely, the derivation does not have a version attribute.
|
||||||
|
|
|
@ -18,16 +18,16 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1657693803,
|
"lastModified": 1670461440,
|
||||||
"narHash": "sha256-G++2CJ9u0E7NNTAi9n5G8TdDmGJXcIjkJ3NF8cetQB8=",
|
"narHash": "sha256-jy1LB8HOMKGJEGXgzFRLDU1CBGL0/LlkolgnqIsF0D8=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "365e1b3a859281cf11b94f87231adeabbdd878a2",
|
"rev": "04a75b2eecc0acf6239acf9dd04485ff8d14f425",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-22.05-small",
|
"ref": "nixos-22.11-small",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
10
flake.nix
10
flake.nix
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
description = "The purely functional package manager - but super!";
|
description = "The purely functional package manager - but super!";
|
||||||
|
|
||||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.05-small";
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.11-small";
|
||||||
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||||
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
|
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
|
||||||
|
|
||||||
|
@ -127,13 +127,9 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
propagatedDeps =
|
propagatedDeps =
|
||||||
[ ((boehmgc.override {
|
[ (boehmgc.override {
|
||||||
enableLargeConfig = true;
|
enableLargeConfig = true;
|
||||||
}).overrideAttrs(o: {
|
})
|
||||||
patches = (o.patches or []) ++ [
|
|
||||||
./boehmgc-coroutine-sp-fallback.diff
|
|
||||||
];
|
|
||||||
}))
|
|
||||||
nlohmann_json
|
nlohmann_json
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
|
@ -115,10 +115,6 @@ sub downloadFile {
|
||||||
|
|
||||||
write_file("$tmpFile.sha256", $sha256_actual);
|
write_file("$tmpFile.sha256", $sha256_actual);
|
||||||
|
|
||||||
if (! -e "$tmpFile.asc") {
|
|
||||||
system("gpg2 --detach-sign --armor $tmpFile") == 0 or die "unable to sign $tmpFile\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sha256_expected;
|
return $sha256_expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +190,7 @@ for my $fn (glob "$tmpDir/*") {
|
||||||
my $configuration = ();
|
my $configuration = ();
|
||||||
$configuration->{content_type} = "application/octet-stream";
|
$configuration->{content_type} = "application/octet-stream";
|
||||||
|
|
||||||
if ($fn =~ /.sha256|.asc|install/) {
|
if ($fn =~ /.sha256|install/) {
|
||||||
# Text files
|
# Text files
|
||||||
$configuration->{content_type} = "text/plain";
|
$configuration->{content_type} = "text/plain";
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,17 +215,15 @@ static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positi
|
||||||
out << dt.hint.str() << "\n";
|
out << dt.hint.str() << "\n";
|
||||||
|
|
||||||
// prefer direct pos, but if noPos then try the expr.
|
// prefer direct pos, but if noPos then try the expr.
|
||||||
auto pos = *dt.pos
|
auto pos = dt.pos
|
||||||
? *dt.pos
|
? dt.pos
|
||||||
: positions[dt.expr.getPos() ? dt.expr.getPos() : noPos];
|
: static_cast<std::shared_ptr<AbstractPos>>(positions[dt.expr.getPos() ? dt.expr.getPos() : noPos]);
|
||||||
|
|
||||||
if (pos) {
|
if (pos) {
|
||||||
printAtPos(pos, out);
|
out << pos;
|
||||||
|
if (auto loc = pos->getCodeLines()) {
|
||||||
auto loc = getCodeLines(pos);
|
|
||||||
if (loc.has_value()) {
|
|
||||||
out << "\n";
|
out << "\n";
|
||||||
printCodeLines(out, "", pos, *loc);
|
printCodeLines(out, "", *pos, *loc);
|
||||||
out << "\n";
|
out << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -589,15 +587,17 @@ bool NixRepl::processLine(std::string line)
|
||||||
Value v;
|
Value v;
|
||||||
evalString(arg, v);
|
evalString(arg, v);
|
||||||
|
|
||||||
const auto [file, line] = [&] () -> std::pair<std::string, uint32_t> {
|
const auto [path, line] = [&] () -> std::pair<Path, uint32_t> {
|
||||||
if (v.type() == nPath || v.type() == nString) {
|
if (v.type() == nPath || v.type() == nString) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto filename = state->coerceToString(noPos, v, context).toOwned();
|
auto path = state->coerceToPath(noPos, v, context);
|
||||||
state->symbols.create(filename);
|
return {path, 0};
|
||||||
return {filename, 0};
|
|
||||||
} else if (v.isLambda()) {
|
} else if (v.isLambda()) {
|
||||||
auto pos = state->positions[v.lambda.fun->pos];
|
auto pos = state->positions[v.lambda.fun->pos];
|
||||||
return {pos.file, pos.line};
|
if (auto path = std::get_if<Path>(&pos.origin))
|
||||||
|
return {*path, pos.line};
|
||||||
|
else
|
||||||
|
throw Error("'%s' cannot be shown in an editor", pos);
|
||||||
} else {
|
} else {
|
||||||
// assume it's a derivation
|
// assume it's a derivation
|
||||||
return findPackageFilename(*state, v, arg);
|
return findPackageFilename(*state, v, arg);
|
||||||
|
@ -605,7 +605,7 @@ bool NixRepl::processLine(std::string line)
|
||||||
}();
|
}();
|
||||||
|
|
||||||
// Open in EDITOR
|
// Open in EDITOR
|
||||||
auto args = editorFor(file, line);
|
auto args = editorFor(path, line);
|
||||||
auto editor = args.front();
|
auto editor = args.front();
|
||||||
args.pop_front();
|
args.pop_front();
|
||||||
|
|
||||||
|
|
|
@ -820,7 +820,7 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
|
||||||
? std::make_unique<DebugTraceStacker>(
|
? std::make_unique<DebugTraceStacker>(
|
||||||
*this,
|
*this,
|
||||||
DebugTrace {
|
DebugTrace {
|
||||||
.pos = error->info().errPos ? *error->info().errPos : positions[expr.getPos()],
|
.pos = error->info().errPos ? error->info().errPos : static_cast<std::shared_ptr<AbstractPos>>(positions[expr.getPos()]),
|
||||||
.expr = expr,
|
.expr = expr,
|
||||||
.env = env,
|
.env = env,
|
||||||
.hint = error->info().msg,
|
.hint = error->info().msg,
|
||||||
|
@ -1009,7 +1009,7 @@ void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, cons
|
||||||
|
|
||||||
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
|
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
e.addTrace(std::nullopt, s, s2);
|
e.addTrace(nullptr, s, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
|
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
|
||||||
|
@ -1021,13 +1021,13 @@ static std::unique_ptr<DebugTraceStacker> makeDebugTraceStacker(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
Expr & expr,
|
Expr & expr,
|
||||||
Env & env,
|
Env & env,
|
||||||
std::optional<ErrPos> pos,
|
std::shared_ptr<AbstractPos> && pos,
|
||||||
const char * s,
|
const char * s,
|
||||||
const std::string & s2)
|
const std::string & s2)
|
||||||
{
|
{
|
||||||
return std::make_unique<DebugTraceStacker>(state,
|
return std::make_unique<DebugTraceStacker>(state,
|
||||||
DebugTrace {
|
DebugTrace {
|
||||||
.pos = pos,
|
.pos = std::move(pos),
|
||||||
.expr = expr,
|
.expr = expr,
|
||||||
.env = env,
|
.env = env,
|
||||||
.hint = hintfmt(s, s2),
|
.hint = hintfmt(s, s2),
|
||||||
|
@ -1133,9 +1133,9 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
|
||||||
void EvalState::mkPos(Value & v, PosIdx p)
|
void EvalState::mkPos(Value & v, PosIdx p)
|
||||||
{
|
{
|
||||||
auto pos = positions[p];
|
auto pos = positions[p];
|
||||||
if (!pos.file.empty()) {
|
if (auto path = std::get_if<Path>(&pos.origin)) {
|
||||||
auto attrs = buildBindings(3);
|
auto attrs = buildBindings(3);
|
||||||
attrs.alloc(sFile).mkString(pos.file);
|
attrs.alloc(sFile).mkString(*path);
|
||||||
attrs.alloc(sLine).mkInt(pos.line);
|
attrs.alloc(sLine).mkInt(pos.line);
|
||||||
attrs.alloc(sColumn).mkInt(pos.column);
|
attrs.alloc(sColumn).mkInt(pos.column);
|
||||||
v.mkAttrs(attrs);
|
v.mkAttrs(attrs);
|
||||||
|
@ -1243,7 +1243,7 @@ void EvalState::cacheFile(
|
||||||
*this,
|
*this,
|
||||||
*e,
|
*e,
|
||||||
this->baseEnv,
|
this->baseEnv,
|
||||||
e->getPos() ? std::optional(ErrPos(positions[e->getPos()])) : std::nullopt,
|
e->getPos() ? static_cast<std::shared_ptr<AbstractPos>>(positions[e->getPos()]) : nullptr,
|
||||||
"while evaluating the file '%1%':", resolvedPath)
|
"while evaluating the file '%1%':", resolvedPath)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
|
||||||
|
@ -1514,10 +1514,13 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
|
state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
|
if (pos2) {
|
||||||
auto pos2r = state.positions[pos2];
|
auto pos2r = state.positions[pos2];
|
||||||
if (pos2 && pos2r.file != state.derivationNixPath)
|
auto origin = std::get_if<Path>(&pos2r.origin);
|
||||||
|
if (!(origin && *origin == state.derivationNixPath))
|
||||||
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
|
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
|
||||||
showAttrPath(state, env, attrPath));
|
showAttrPath(state, env, attrPath));
|
||||||
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2494,7 +2497,8 @@ void EvalState::printStats()
|
||||||
else
|
else
|
||||||
obj["name"] = nullptr;
|
obj["name"] = nullptr;
|
||||||
if (auto pos = positions[fun->pos]) {
|
if (auto pos = positions[fun->pos]) {
|
||||||
obj["file"] = (std::string_view) pos.file;
|
if (auto path = std::get_if<Path>(&pos.origin))
|
||||||
|
obj["file"] = *path;
|
||||||
obj["line"] = pos.line;
|
obj["line"] = pos.line;
|
||||||
obj["column"] = pos.column;
|
obj["column"] = pos.column;
|
||||||
}
|
}
|
||||||
|
@ -2508,7 +2512,8 @@ void EvalState::printStats()
|
||||||
for (auto & i : attrSelects) {
|
for (auto & i : attrSelects) {
|
||||||
json obj = json::object();
|
json obj = json::object();
|
||||||
if (auto pos = positions[i.first]) {
|
if (auto pos = positions[i.first]) {
|
||||||
obj["file"] = (const std::string &) pos.file;
|
if (auto path = std::get_if<Path>(&pos.origin))
|
||||||
|
obj["file"] = *path;
|
||||||
obj["line"] = pos.line;
|
obj["line"] = pos.line;
|
||||||
obj["column"] = pos.column;
|
obj["column"] = pos.column;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ struct RegexCache;
|
||||||
std::shared_ptr<RegexCache> makeRegexCache();
|
std::shared_ptr<RegexCache> makeRegexCache();
|
||||||
|
|
||||||
struct DebugTrace {
|
struct DebugTrace {
|
||||||
std::optional<ErrPos> pos;
|
std::shared_ptr<AbstractPos> pos;
|
||||||
const Expr & expr;
|
const Expr & expr;
|
||||||
const Env & env;
|
const Env & env;
|
||||||
hintformat hint;
|
hintformat hint;
|
||||||
|
@ -457,8 +457,12 @@ private:
|
||||||
friend struct ExprAttrs;
|
friend struct ExprAttrs;
|
||||||
friend struct ExprLet;
|
friend struct ExprLet;
|
||||||
|
|
||||||
Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
|
Expr * parse(
|
||||||
const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv);
|
char * text,
|
||||||
|
size_t length,
|
||||||
|
Pos::Origin origin,
|
||||||
|
Path basePath,
|
||||||
|
std::shared_ptr<StaticEnv> & staticEnv);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ void ConfigFile::apply()
|
||||||
auto tlname = get(trustedList, name);
|
auto tlname = get(trustedList, name);
|
||||||
if (auto saved = tlname ? get(*tlname, valueS) : nullptr) {
|
if (auto saved = tlname ? get(*tlname, valueS) : nullptr) {
|
||||||
trusted = *saved;
|
trusted = *saved;
|
||||||
debug("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name,valueS);
|
printInfo("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name, valueS);
|
||||||
} else {
|
} else {
|
||||||
// FIXME: filter ANSI escapes, newlines, \r, etc.
|
// FIXME: filter ANSI escapes, newlines, \r, etc.
|
||||||
if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) == 'y') {
|
if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) == 'y') {
|
||||||
|
|
|
@ -220,7 +220,7 @@ static Flake getFlake(
|
||||||
Value vInfo;
|
Value vInfo;
|
||||||
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
|
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
|
||||||
|
|
||||||
expectType(state, nAttrs, vInfo, state.positions.add({flakeFile, foFile}, 0, 0));
|
expectType(state, nAttrs, vInfo, state.positions.add({flakeFile}, 1, 1));
|
||||||
|
|
||||||
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||||
expectType(state, nString, *description->value, description->pos);
|
expectType(state, nString, *description->value, description->pos);
|
||||||
|
|
|
@ -8,6 +8,58 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
struct PosAdapter : AbstractPos
|
||||||
|
{
|
||||||
|
Pos::Origin origin;
|
||||||
|
|
||||||
|
PosAdapter(Pos::Origin origin)
|
||||||
|
: origin(std::move(origin))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> getSource() const override
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[](const Pos::none_tag &) -> std::optional<std::string> {
|
||||||
|
return std::nullopt;
|
||||||
|
},
|
||||||
|
[](const Pos::Stdin & s) -> std::optional<std::string> {
|
||||||
|
// Get rid of the null terminators added by the parser.
|
||||||
|
return std::string(s.source->c_str());
|
||||||
|
},
|
||||||
|
[](const Pos::String & s) -> std::optional<std::string> {
|
||||||
|
// Get rid of the null terminators added by the parser.
|
||||||
|
return std::string(s.source->c_str());
|
||||||
|
},
|
||||||
|
[](const Path & path) -> std::optional<std::string> {
|
||||||
|
try {
|
||||||
|
return readFile(path);
|
||||||
|
} catch (Error &) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(std::ostream & out) const override
|
||||||
|
{
|
||||||
|
std::visit(overloaded {
|
||||||
|
[&](const Pos::none_tag &) { out << "«none»"; },
|
||||||
|
[&](const Pos::Stdin &) { out << "«stdin»"; },
|
||||||
|
[&](const Pos::String & s) { out << "«string»"; },
|
||||||
|
[&](const Path & path) { out << path; }
|
||||||
|
}, origin);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Pos::operator std::shared_ptr<AbstractPos>() const
|
||||||
|
{
|
||||||
|
auto pos = std::make_shared<PosAdapter>(origin);
|
||||||
|
pos->line = line;
|
||||||
|
pos->column = column;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
/* Displaying abstract syntax trees. */
|
/* Displaying abstract syntax trees. */
|
||||||
|
|
||||||
static void showString(std::ostream & str, std::string_view s)
|
static void showString(std::ostream & str, std::string_view s)
|
||||||
|
@ -248,24 +300,10 @@ void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Pos & pos)
|
std::ostream & operator << (std::ostream & str, const Pos & pos)
|
||||||
{
|
{
|
||||||
if (!pos)
|
if (auto pos2 = (std::shared_ptr<AbstractPos>) pos) {
|
||||||
|
str << *pos2;
|
||||||
|
} else
|
||||||
str << "undefined position";
|
str << "undefined position";
|
||||||
else
|
|
||||||
{
|
|
||||||
auto f = format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%");
|
|
||||||
switch (pos.origin) {
|
|
||||||
case foFile:
|
|
||||||
f % (const std::string &) pos.file;
|
|
||||||
break;
|
|
||||||
case foStdin:
|
|
||||||
case foString:
|
|
||||||
f % "(string)";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw Error("unhandled Pos origin!");
|
|
||||||
}
|
|
||||||
str << (f % pos.line % pos.column).str();
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,22 @@ MakeError(MissingArgumentError, EvalError);
|
||||||
MakeError(RestrictedPathError, Error);
|
MakeError(RestrictedPathError, Error);
|
||||||
|
|
||||||
/* Position objects. */
|
/* Position objects. */
|
||||||
|
|
||||||
struct Pos
|
struct Pos
|
||||||
{
|
{
|
||||||
std::string file;
|
|
||||||
FileOrigin origin;
|
|
||||||
uint32_t line;
|
uint32_t line;
|
||||||
uint32_t column;
|
uint32_t column;
|
||||||
|
|
||||||
|
struct none_tag { };
|
||||||
|
struct Stdin { ref<std::string> source; };
|
||||||
|
struct String { ref<std::string> source; };
|
||||||
|
|
||||||
|
typedef std::variant<none_tag, Stdin, String, Path> Origin;
|
||||||
|
|
||||||
|
Origin origin;
|
||||||
|
|
||||||
explicit operator bool() const { return line > 0; }
|
explicit operator bool() const { return line > 0; }
|
||||||
|
|
||||||
|
operator std::shared_ptr<AbstractPos>() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PosIdx {
|
class PosIdx {
|
||||||
|
@ -47,7 +54,11 @@ public:
|
||||||
|
|
||||||
explicit operator bool() const { return id > 0; }
|
explicit operator bool() const { return id > 0; }
|
||||||
|
|
||||||
bool operator<(const PosIdx other) const { return id < other.id; }
|
bool operator <(const PosIdx other) const { return id < other.id; }
|
||||||
|
|
||||||
|
bool operator ==(const PosIdx other) const { return id == other.id; }
|
||||||
|
|
||||||
|
bool operator !=(const PosIdx other) const { return id != other.id; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PosTable
|
class PosTable
|
||||||
|
@ -61,13 +72,13 @@ public:
|
||||||
// current origins.back() can be reused or not.
|
// current origins.back() can be reused or not.
|
||||||
mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
|
mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
|
||||||
|
|
||||||
explicit Origin(uint32_t idx): idx(idx), file{}, origin{} {}
|
// Used for searching in PosTable::[].
|
||||||
|
explicit Origin(uint32_t idx): idx(idx), origin{Pos::none_tag()} {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::string file;
|
const Pos::Origin origin;
|
||||||
const FileOrigin origin;
|
|
||||||
|
|
||||||
Origin(std::string file, FileOrigin origin): file(std::move(file)), origin(origin) {}
|
Origin(Pos::Origin origin): origin(origin) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Offset {
|
struct Offset {
|
||||||
|
@ -107,7 +118,7 @@ public:
|
||||||
[] (const auto & a, const auto & b) { return a.idx < b.idx; });
|
[] (const auto & a, const auto & b) { return a.idx < b.idx; });
|
||||||
const auto origin = *std::prev(pastOrigin);
|
const auto origin = *std::prev(pastOrigin);
|
||||||
const auto offset = offsets[idx];
|
const auto offset = offsets[idx];
|
||||||
return {origin.file, origin.origin, offset.line, offset.column};
|
return {offset.line, offset.column, origin.origin};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -34,11 +34,6 @@ namespace nix {
|
||||||
Path basePath;
|
Path basePath;
|
||||||
PosTable::Origin origin;
|
PosTable::Origin origin;
|
||||||
std::optional<ErrorInfo> error;
|
std::optional<ErrorInfo> error;
|
||||||
ParseData(EvalState & state, PosTable::Origin origin)
|
|
||||||
: state(state)
|
|
||||||
, symbols(state.symbols)
|
|
||||||
, origin(std::move(origin))
|
|
||||||
{ };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParserFormals {
|
struct ParserFormals {
|
||||||
|
@ -651,24 +646,20 @@ formal
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
Expr * EvalState::parse(
|
||||||
const PathView path, const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
char * text,
|
||||||
|
size_t length,
|
||||||
|
Pos::Origin origin,
|
||||||
|
Path basePath,
|
||||||
|
std::shared_ptr<StaticEnv> & staticEnv)
|
||||||
{
|
{
|
||||||
yyscan_t scanner;
|
yyscan_t scanner;
|
||||||
std::string file;
|
ParseData data {
|
||||||
switch (origin) {
|
.state = *this,
|
||||||
case foFile:
|
.symbols = symbols,
|
||||||
file = path;
|
.basePath = std::move(basePath),
|
||||||
break;
|
.origin = {origin},
|
||||||
case foStdin:
|
};
|
||||||
case foString:
|
|
||||||
file = text;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
ParseData data(*this, {file, origin});
|
|
||||||
data.basePath = basePath;
|
|
||||||
|
|
||||||
yylex_init(&scanner);
|
yylex_init(&scanner);
|
||||||
yy_scan_buffer(text, length, scanner);
|
yy_scan_buffer(text, length, scanner);
|
||||||
|
@ -720,14 +711,15 @@ Expr * EvalState::parseExprFromFile(const Path & path, std::shared_ptr<StaticEnv
|
||||||
auto buffer = readFile(path);
|
auto buffer = readFile(path);
|
||||||
// readFile should have left some extra space for terminators
|
// readFile should have left some extra space for terminators
|
||||||
buffer.append("\0\0", 2);
|
buffer.append("\0\0", 2);
|
||||||
return parse(buffer.data(), buffer.size(), foFile, path, dirOf(path), staticEnv);
|
return parse(buffer.data(), buffer.size(), path, dirOf(path), staticEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
Expr * EvalState::parseExprFromString(std::string s_, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
||||||
{
|
{
|
||||||
s.append("\0\0", 2);
|
auto s = make_ref<std::string>(std::move(s_));
|
||||||
return parse(s.data(), s.size(), foString, "", basePath, staticEnv);
|
s->append("\0\0", 2);
|
||||||
|
return parse(s->data(), s->size(), Pos::String{.source = s}, basePath, staticEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -743,7 +735,8 @@ Expr * EvalState::parseStdin()
|
||||||
auto buffer = drainFD(0);
|
auto buffer = drainFD(0);
|
||||||
// drainFD should have left some extra space for terminators
|
// drainFD should have left some extra space for terminators
|
||||||
buffer.append("\0\0", 2);
|
buffer.append("\0\0", 2);
|
||||||
return parse(buffer.data(), buffer.size(), foStdin, "", absPath("."), staticBaseEnv);
|
auto s = make_ref<std::string>(std::move(buffer));
|
||||||
|
return parse(s->data(), s->size(), Pos::Stdin{.source = s}, absPath("."), staticBaseEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -368,8 +368,7 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
auto output = runProgram(program, true, commandArgs);
|
auto output = runProgram(program, true, commandArgs);
|
||||||
Expr * parsed;
|
Expr * parsed;
|
||||||
try {
|
try {
|
||||||
auto base = state.positions[pos];
|
parsed = state.parseExprFromString(std::move(output), "/");
|
||||||
parsed = state.parseExprFromString(std::move(output), base.file);
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(state.positions[pos], "While parsing the output from '%1%'", program);
|
e.addTrace(state.positions[pos], "While parsing the output from '%1%'", program);
|
||||||
throw;
|
throw;
|
||||||
|
@ -798,7 +797,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
|
||||||
v = *args[1];
|
v = *args[1];
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned());
|
e.addTrace(nullptr, state.coerceToString(pos, *args[0], context).toOwned());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4021,7 +4020,7 @@ void EvalState::createBaseEnv()
|
||||||
// the parser needs two NUL bytes as terminators; one of them
|
// the parser needs two NUL bytes as terminators; one of them
|
||||||
// is implied by being a C string.
|
// is implied by being a C string.
|
||||||
"\0";
|
"\0";
|
||||||
eval(parse(code, sizeof(code), foFile, derivationNixPath, "/", staticBaseEnv), *vDerivation);
|
eval(parse(code, sizeof(code), derivationNixPath, "/", staticBaseEnv), *vDerivation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,7 @@ namespace nix {
|
||||||
|
|
||||||
MATCHER_P(IsAttrsOfSize, n, fmt("Is a set of size [%1%]", n)) {
|
MATCHER_P(IsAttrsOfSize, n, fmt("Is a set of size [%1%]", n)) {
|
||||||
if (arg.type() != nAttrs) {
|
if (arg.type() != nAttrs) {
|
||||||
*result_listener << "Expexted set got " << arg.type();
|
*result_listener << "Expected set got " << arg.type();
|
||||||
return false;
|
return false;
|
||||||
} else if (arg.attrs->size() != (size_t)n) {
|
} else if (arg.attrs->size() != (size_t)n) {
|
||||||
*result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs->size();
|
*result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs->size();
|
||||||
|
|
|
@ -151,20 +151,7 @@ namespace nix {
|
||||||
// The `y` attribute is at position
|
// The `y` attribute is at position
|
||||||
const char* expr = "builtins.unsafeGetAttrPos \"y\" { y = \"x\"; }";
|
const char* expr = "builtins.unsafeGetAttrPos \"y\" { y = \"x\"; }";
|
||||||
auto v = eval(expr);
|
auto v = eval(expr);
|
||||||
ASSERT_THAT(v, IsAttrsOfSize(3));
|
ASSERT_THAT(v, IsNull());
|
||||||
|
|
||||||
auto file = v.attrs->find(createSymbol("file"));
|
|
||||||
ASSERT_NE(file, nullptr);
|
|
||||||
// FIXME: The file when running these tests is the input string?!?
|
|
||||||
ASSERT_THAT(*file->value, IsStringEq(expr));
|
|
||||||
|
|
||||||
auto line = v.attrs->find(createSymbol("line"));
|
|
||||||
ASSERT_NE(line, nullptr);
|
|
||||||
ASSERT_THAT(*line->value, IsIntEq(1));
|
|
||||||
|
|
||||||
auto column = v.attrs->find(createSymbol("column"));
|
|
||||||
ASSERT_NE(column, nullptr);
|
|
||||||
ASSERT_THAT(*column->value, IsIntEq(33));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PrimOpTest, hasAttr) {
|
TEST_F(PrimOpTest, hasAttr) {
|
||||||
|
@ -617,7 +604,7 @@ namespace nix {
|
||||||
|
|
||||||
TEST_F(PrimOpTest, storeDir) {
|
TEST_F(PrimOpTest, storeDir) {
|
||||||
auto v = eval("builtins.storeDir");
|
auto v = eval("builtins.storeDir");
|
||||||
ASSERT_THAT(v, IsStringEq("/nix/store"));
|
ASSERT_THAT(v, IsStringEq(settings.nixStore));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PrimOpTest, nixVersion) {
|
TEST_F(PrimOpTest, nixVersion) {
|
||||||
|
|
|
@ -24,7 +24,8 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
|
|
||||||
static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
|
static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
|
||||||
{
|
{
|
||||||
xmlAttrs["path"] = pos.file;
|
if (auto path = std::get_if<Path>(&pos.origin))
|
||||||
|
xmlAttrs["path"] = *path;
|
||||||
xmlAttrs["line"] = (format("%1%") % pos.line).str();
|
xmlAttrs["line"] = (format("%1%") % pos.line).str();
|
||||||
xmlAttrs["column"] = (format("%1%") % pos.column).str();
|
xmlAttrs["column"] = (format("%1%") % pos.column).str();
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,16 +166,37 @@ public:
|
||||||
return i->second;
|
return i->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Cache> queryCacheRaw(State & state, const std::string & uri)
|
||||||
|
{
|
||||||
|
auto i = state.caches.find(uri);
|
||||||
|
if (i == state.caches.end()) {
|
||||||
|
auto queryCache(state.queryCache.use()(uri)(time(0) - cacheInfoTtl));
|
||||||
|
if (!queryCache.next())
|
||||||
|
return std::nullopt;
|
||||||
|
state.caches.emplace(uri,
|
||||||
|
Cache{(int) queryCache.getInt(0), queryCache.getStr(1), queryCache.getInt(2) != 0, (int) queryCache.getInt(3)});
|
||||||
|
}
|
||||||
|
return getCache(state, uri);
|
||||||
|
}
|
||||||
|
|
||||||
void createCache(const std::string & uri, const Path & storeDir, bool wantMassQuery, int priority) override
|
void createCache(const std::string & uri, const Path & storeDir, bool wantMassQuery, int priority) override
|
||||||
{
|
{
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
|
SQLiteTxn txn(state->db);
|
||||||
|
|
||||||
// FIXME: race
|
// To avoid the race, we have to check if maybe someone hasn't yet created
|
||||||
|
// the cache for this URI in the meantime.
|
||||||
|
auto cache(queryCacheRaw(*state, uri));
|
||||||
|
|
||||||
|
if (cache)
|
||||||
|
return;
|
||||||
|
|
||||||
state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority).exec();
|
state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority).exec();
|
||||||
assert(sqlite3_changes(state->db) == 1);
|
assert(sqlite3_changes(state->db) == 1);
|
||||||
state->caches[uri] = Cache{(int) sqlite3_last_insert_rowid(state->db), storeDir, wantMassQuery, priority};
|
state->caches[uri] = Cache{(int) sqlite3_last_insert_rowid(state->db), storeDir, wantMassQuery, priority};
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,21 +204,12 @@ public:
|
||||||
{
|
{
|
||||||
return retrySQLite<std::optional<CacheInfo>>([&]() -> std::optional<CacheInfo> {
|
return retrySQLite<std::optional<CacheInfo>>([&]() -> std::optional<CacheInfo> {
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
|
auto cache(queryCacheRaw(*state, uri));
|
||||||
auto i = state->caches.find(uri);
|
if (!cache)
|
||||||
if (i == state->caches.end()) {
|
|
||||||
auto queryCache(state->queryCache.use()(uri)(time(0) - cacheInfoTtl));
|
|
||||||
if (!queryCache.next())
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
state->caches.emplace(uri,
|
|
||||||
Cache{(int) queryCache.getInt(0), queryCache.getStr(1), queryCache.getInt(2) != 0, (int) queryCache.getInt(3)});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto & cache(getCache(*state, uri));
|
|
||||||
|
|
||||||
return CacheInfo {
|
return CacheInfo {
|
||||||
.wantMassQuery = cache.wantMassQuery,
|
.wantMassQuery = cache->wantMassQuery,
|
||||||
.priority = cache.priority
|
.priority = cache->priority
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,15 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
SQLiteError::SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf)
|
SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, hintformat && hf)
|
||||||
: Error(""), path(path), errNo(errNo), extendedErrNo(extendedErrNo)
|
: Error(""), path(path), errMsg(errMsg), errNo(errNo), extendedErrNo(extendedErrNo), offset(offset)
|
||||||
{
|
{
|
||||||
err.msg = hintfmt("%s: %s (in '%s')",
|
auto offsetStr = (offset == -1) ? "" : "at offset " + std::to_string(offset) + ": ";
|
||||||
|
err.msg = hintfmt("%s: %s%s, %s (in '%s')",
|
||||||
normaltxt(hf.str()),
|
normaltxt(hf.str()),
|
||||||
|
offsetStr,
|
||||||
sqlite3_errstr(extendedErrNo),
|
sqlite3_errstr(extendedErrNo),
|
||||||
|
errMsg,
|
||||||
path ? path : "(in-memory)");
|
path ? path : "(in-memory)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,11 +24,13 @@ SQLiteError::SQLiteError(const char *path, int errNo, int extendedErrNo, hintfor
|
||||||
{
|
{
|
||||||
int err = sqlite3_errcode(db);
|
int err = sqlite3_errcode(db);
|
||||||
int exterr = sqlite3_extended_errcode(db);
|
int exterr = sqlite3_extended_errcode(db);
|
||||||
|
int offset = sqlite3_error_offset(db);
|
||||||
|
|
||||||
auto path = sqlite3_db_filename(db, nullptr);
|
auto path = sqlite3_db_filename(db, nullptr);
|
||||||
|
auto errMsg = sqlite3_errmsg(db);
|
||||||
|
|
||||||
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
|
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
|
||||||
auto exp = SQLiteBusy(path, err, exterr, std::move(hf));
|
auto exp = SQLiteBusy(path, errMsg, err, exterr, offset, std::move(hf));
|
||||||
exp.err.msg = hintfmt(
|
exp.err.msg = hintfmt(
|
||||||
err == SQLITE_PROTOCOL
|
err == SQLITE_PROTOCOL
|
||||||
? "SQLite database '%s' is busy (SQLITE_PROTOCOL)"
|
? "SQLite database '%s' is busy (SQLITE_PROTOCOL)"
|
||||||
|
@ -33,7 +38,7 @@ SQLiteError::SQLiteError(const char *path, int errNo, int extendedErrNo, hintfor
|
||||||
path ? path : "(in-memory)");
|
path ? path : "(in-memory)");
|
||||||
throw exp;
|
throw exp;
|
||||||
} else
|
} else
|
||||||
throw SQLiteError(path, err, exterr, std::move(hf));
|
throw SQLiteError(path, errMsg, err, exterr, offset, std::move(hf));
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLite::SQLite(const Path & path, bool create)
|
SQLite::SQLite(const Path & path, bool create)
|
||||||
|
|
|
@ -98,21 +98,22 @@ struct SQLiteTxn
|
||||||
|
|
||||||
struct SQLiteError : Error
|
struct SQLiteError : Error
|
||||||
{
|
{
|
||||||
const char *path;
|
std::string path;
|
||||||
int errNo, extendedErrNo;
|
std::string errMsg;
|
||||||
|
int errNo, extendedErrNo, offset;
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
[[noreturn]] static void throw_(sqlite3 * db, const std::string & fs, const Args & ... args) {
|
[[noreturn]] static void throw_(sqlite3 * db, const std::string & fs, const Args & ... args) {
|
||||||
throw_(db, hintfmt(fs, args...));
|
throw_(db, hintfmt(fs, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf);
|
SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, hintformat && hf);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
SQLiteError(const char *path, int errNo, int extendedErrNo, const std::string & fs, const Args & ... args)
|
SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, const std::string & fs, const Args & ... args)
|
||||||
: SQLiteError(path, errNo, extendedErrNo, hintfmt(fs, args...))
|
: SQLiteError(path, errNo, extendedErrNo, offset, hintfmt(fs, args...))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
[[noreturn]] static void throw_(sqlite3 * db, hintformat && hf);
|
[[noreturn]] static void throw_(sqlite3 * db, hintformat && hf);
|
||||||
|
|
103
src/libutil/canon-path.cc
Normal file
103
src/libutil/canon-path.cc
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#include "canon-path.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
CanonPath CanonPath::root = CanonPath("/");
|
||||||
|
|
||||||
|
CanonPath::CanonPath(std::string_view raw)
|
||||||
|
: path(absPath((Path) raw, "/"))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
CanonPath::CanonPath(std::string_view raw, const CanonPath & root)
|
||||||
|
: path(absPath((Path) raw, root.abs()))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
std::optional<CanonPath> CanonPath::parent() const
|
||||||
|
{
|
||||||
|
if (isRoot()) return std::nullopt;
|
||||||
|
return CanonPath(unchecked_t(), path.substr(0, std::max((size_t) 1, path.rfind('/'))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanonPath::pop()
|
||||||
|
{
|
||||||
|
assert(!isRoot());
|
||||||
|
path.resize(std::max((size_t) 1, path.rfind('/')));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanonPath::isWithin(const CanonPath & parent) const
|
||||||
|
{
|
||||||
|
return !(
|
||||||
|
path.size() < parent.path.size()
|
||||||
|
|| path.substr(0, parent.path.size()) != parent.path
|
||||||
|
|| (parent.path.size() > 1 && path.size() > parent.path.size()
|
||||||
|
&& path[parent.path.size()] != '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
CanonPath CanonPath::removePrefix(const CanonPath & prefix) const
|
||||||
|
{
|
||||||
|
assert(isWithin(prefix));
|
||||||
|
if (prefix.isRoot()) return *this;
|
||||||
|
if (path.size() == prefix.path.size()) return root;
|
||||||
|
return CanonPath(unchecked_t(), path.substr(prefix.path.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanonPath::extend(const CanonPath & x)
|
||||||
|
{
|
||||||
|
if (x.isRoot()) return;
|
||||||
|
if (isRoot())
|
||||||
|
path += x.rel();
|
||||||
|
else
|
||||||
|
path += x.abs();
|
||||||
|
}
|
||||||
|
|
||||||
|
CanonPath CanonPath::operator + (const CanonPath & x) const
|
||||||
|
{
|
||||||
|
auto res = *this;
|
||||||
|
res.extend(x);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanonPath::push(std::string_view c)
|
||||||
|
{
|
||||||
|
assert(c.find('/') == c.npos);
|
||||||
|
assert(c != "." && c != "..");
|
||||||
|
if (!isRoot()) path += '/';
|
||||||
|
path += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanonPath CanonPath::operator + (std::string_view c) const
|
||||||
|
{
|
||||||
|
auto res = *this;
|
||||||
|
res.push(c);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanonPath::isAllowed(const std::set<CanonPath> & allowed) const
|
||||||
|
{
|
||||||
|
/* Check if `this` is an exact match or the parent of an
|
||||||
|
allowed path. */
|
||||||
|
auto lb = allowed.lower_bound(*this);
|
||||||
|
if (lb != allowed.end()) {
|
||||||
|
if (lb->isWithin(*this))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if a parent of `this` is allowed. */
|
||||||
|
auto path = *this;
|
||||||
|
while (!path.isRoot()) {
|
||||||
|
path.pop();
|
||||||
|
if (allowed.count(path))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream & operator << (std::ostream & stream, const CanonPath & path)
|
||||||
|
{
|
||||||
|
stream << path.abs();
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
173
src/libutil/canon-path.hh
Normal file
173
src/libutil/canon-path.hh
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* A canonical representation of a path. It ensures the following:
|
||||||
|
|
||||||
|
- It always starts with a slash.
|
||||||
|
|
||||||
|
- It never ends with a slash, except if the path is "/".
|
||||||
|
|
||||||
|
- A slash is never followed by a slash (i.e. no empty components).
|
||||||
|
|
||||||
|
- There are no components equal to '.' or '..'.
|
||||||
|
|
||||||
|
Note that the path does not need to correspond to an actually
|
||||||
|
existing path, and there is no guarantee that symlinks are
|
||||||
|
resolved.
|
||||||
|
*/
|
||||||
|
class CanonPath
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/* Construct a canon path from a non-canonical path. Any '.', '..'
|
||||||
|
or empty components are removed. */
|
||||||
|
CanonPath(std::string_view raw);
|
||||||
|
|
||||||
|
explicit CanonPath(const char * raw)
|
||||||
|
: CanonPath(std::string_view(raw))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
struct unchecked_t { };
|
||||||
|
|
||||||
|
CanonPath(unchecked_t _, std::string path)
|
||||||
|
: path(std::move(path))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
static CanonPath root;
|
||||||
|
|
||||||
|
/* If `raw` starts with a slash, return
|
||||||
|
`CanonPath(raw)`. Otherwise return a `CanonPath` representing
|
||||||
|
`root + "/" + raw`. */
|
||||||
|
CanonPath(std::string_view raw, const CanonPath & root);
|
||||||
|
|
||||||
|
bool isRoot() const
|
||||||
|
{ return path.size() <= 1; }
|
||||||
|
|
||||||
|
explicit operator std::string_view() const
|
||||||
|
{ return path; }
|
||||||
|
|
||||||
|
const std::string & abs() const
|
||||||
|
{ return path; }
|
||||||
|
|
||||||
|
/* Like abs(), but return an empty string if this path is
|
||||||
|
'/'. Thus the returned string never ends in a slash. */
|
||||||
|
const std::string & absOrEmpty() const
|
||||||
|
{
|
||||||
|
const static std::string epsilon;
|
||||||
|
return isRoot() ? epsilon : path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * c_str() const
|
||||||
|
{ return path.c_str(); }
|
||||||
|
|
||||||
|
std::string_view rel() const
|
||||||
|
{ return ((std::string_view) path).substr(1); }
|
||||||
|
|
||||||
|
struct Iterator
|
||||||
|
{
|
||||||
|
std::string_view remaining;
|
||||||
|
size_t slash;
|
||||||
|
|
||||||
|
Iterator(std::string_view remaining)
|
||||||
|
: remaining(remaining)
|
||||||
|
, slash(remaining.find('/'))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool operator != (const Iterator & x) const
|
||||||
|
{ return remaining.data() != x.remaining.data(); }
|
||||||
|
|
||||||
|
const std::string_view operator * () const
|
||||||
|
{ return remaining.substr(0, slash); }
|
||||||
|
|
||||||
|
void operator ++ ()
|
||||||
|
{
|
||||||
|
if (slash == remaining.npos)
|
||||||
|
remaining = remaining.substr(remaining.size());
|
||||||
|
else {
|
||||||
|
remaining = remaining.substr(slash + 1);
|
||||||
|
slash = remaining.find('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Iterator begin() const { return Iterator(rel()); }
|
||||||
|
Iterator end() const { return Iterator(rel().substr(path.size() - 1)); }
|
||||||
|
|
||||||
|
std::optional<CanonPath> parent() const;
|
||||||
|
|
||||||
|
/* Remove the last component. Panics if this path is the root. */
|
||||||
|
void pop();
|
||||||
|
|
||||||
|
std::optional<std::string_view> dirOf() const
|
||||||
|
{
|
||||||
|
if (isRoot()) return std::nullopt;
|
||||||
|
return ((std::string_view) path).substr(0, path.rfind('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string_view> baseName() const
|
||||||
|
{
|
||||||
|
if (isRoot()) return std::nullopt;
|
||||||
|
return ((std::string_view) path).substr(path.rfind('/') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const CanonPath & x) const
|
||||||
|
{ return path == x.path; }
|
||||||
|
|
||||||
|
bool operator != (const CanonPath & x) const
|
||||||
|
{ return path != x.path; }
|
||||||
|
|
||||||
|
/* Compare paths lexicographically except that path separators
|
||||||
|
are sorted before any other character. That is, in the sorted order
|
||||||
|
a directory is always followed directly by its children. For
|
||||||
|
instance, 'foo' < 'foo/bar' < 'foo!'. */
|
||||||
|
bool operator < (const CanonPath & x) const
|
||||||
|
{
|
||||||
|
auto i = path.begin();
|
||||||
|
auto j = x.path.begin();
|
||||||
|
for ( ; i != path.end() && j != x.path.end(); ++i, ++j) {
|
||||||
|
auto c_i = *i;
|
||||||
|
if (c_i == '/') c_i = 0;
|
||||||
|
auto c_j = *j;
|
||||||
|
if (c_j == '/') c_j = 0;
|
||||||
|
if (c_i < c_j) return true;
|
||||||
|
if (c_i > c_j) return false;
|
||||||
|
}
|
||||||
|
return i == path.end() && j != x.path.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if `this` is equal to `parent` or a child of
|
||||||
|
`parent`. */
|
||||||
|
bool isWithin(const CanonPath & parent) const;
|
||||||
|
|
||||||
|
CanonPath removePrefix(const CanonPath & prefix) const;
|
||||||
|
|
||||||
|
/* Append another path to this one. */
|
||||||
|
void extend(const CanonPath & x);
|
||||||
|
|
||||||
|
/* Concatenate two paths. */
|
||||||
|
CanonPath operator + (const CanonPath & x) const;
|
||||||
|
|
||||||
|
/* Add a path component to this one. It must not contain any slashes. */
|
||||||
|
void push(std::string_view c);
|
||||||
|
|
||||||
|
CanonPath operator + (std::string_view c) const;
|
||||||
|
|
||||||
|
/* Check whether access to this path is allowed, which is the case
|
||||||
|
if 1) `this` is within any of the `allowed` paths; or 2) any of
|
||||||
|
the `allowed` paths are within `this`. (The latter condition
|
||||||
|
ensures access to the parents of allowed paths.) */
|
||||||
|
bool isAllowed(const std::set<CanonPath> & allowed) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream & operator << (std::ostream & stream, const CanonPath & path);
|
||||||
|
|
||||||
|
}
|
|
@ -9,9 +9,9 @@ namespace nix {
|
||||||
|
|
||||||
const std::string nativeSystem = SYSTEM;
|
const std::string nativeSystem = SYSTEM;
|
||||||
|
|
||||||
void BaseError::addTrace(std::optional<ErrPos> e, hintformat hint)
|
void BaseError::addTrace(std::shared_ptr<AbstractPos> && e, hintformat hint)
|
||||||
{
|
{
|
||||||
err.traces.push_front(Trace { .pos = e, .hint = hint });
|
err.traces.push_front(Trace { .pos = std::move(e), .hint = hint });
|
||||||
}
|
}
|
||||||
|
|
||||||
// c++ std::exception descendants must have a 'const char* what()' function.
|
// c++ std::exception descendants must have a 'const char* what()' function.
|
||||||
|
@ -30,91 +30,46 @@ const std::string & BaseError::calcWhat() const
|
||||||
|
|
||||||
std::optional<std::string> ErrorInfo::programName = std::nullopt;
|
std::optional<std::string> ErrorInfo::programName = std::nullopt;
|
||||||
|
|
||||||
std::ostream & operator<<(std::ostream & os, const hintformat & hf)
|
std::ostream & operator <<(std::ostream & os, const hintformat & hf)
|
||||||
{
|
{
|
||||||
return os << hf.str();
|
return os << hf.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string showErrPos(const ErrPos & errPos)
|
std::ostream & operator <<(std::ostream & str, const AbstractPos & pos)
|
||||||
{
|
{
|
||||||
if (errPos.line > 0) {
|
pos.print(str);
|
||||||
if (errPos.column > 0) {
|
str << ":" << pos.line;
|
||||||
return fmt("%d:%d", errPos.line, errPos.column);
|
if (pos.column > 0)
|
||||||
} else {
|
str << ":" << pos.column;
|
||||||
return fmt("%d", errPos.line);
|
return str;
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos)
|
std::optional<LinesOfCode> AbstractPos::getCodeLines() const
|
||||||
{
|
{
|
||||||
if (errPos.line <= 0)
|
if (line == 0)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if (errPos.origin == foFile) {
|
if (auto source = getSource()) {
|
||||||
LinesOfCode loc;
|
|
||||||
try {
|
|
||||||
// FIXME: when running as the daemon, make sure we don't
|
|
||||||
// open a file to which the client doesn't have access.
|
|
||||||
AutoCloseFD fd = open(errPos.file.c_str(), O_RDONLY | O_CLOEXEC);
|
|
||||||
if (!fd) return {};
|
|
||||||
|
|
||||||
|
std::istringstream iss(*source);
|
||||||
// count the newlines.
|
// count the newlines.
|
||||||
int count = 0;
|
int count = 0;
|
||||||
std::string line;
|
std::string curLine;
|
||||||
int pl = errPos.line - 1;
|
int pl = line - 1;
|
||||||
do
|
|
||||||
{
|
|
||||||
line = readLine(fd.get());
|
|
||||||
++count;
|
|
||||||
if (count < pl)
|
|
||||||
;
|
|
||||||
else if (count == pl)
|
|
||||||
loc.prevLineOfCode = line;
|
|
||||||
else if (count == pl + 1)
|
|
||||||
loc.errLineOfCode = line;
|
|
||||||
else if (count == pl + 2) {
|
|
||||||
loc.nextLineOfCode = line;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
return loc;
|
|
||||||
}
|
|
||||||
catch (EndOfFile & eof) {
|
|
||||||
if (loc.errLineOfCode.has_value())
|
|
||||||
return loc;
|
|
||||||
else
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
catch (std::exception & e) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::istringstream iss(errPos.file);
|
|
||||||
// count the newlines.
|
|
||||||
int count = 0;
|
|
||||||
std::string line;
|
|
||||||
int pl = errPos.line - 1;
|
|
||||||
|
|
||||||
LinesOfCode loc;
|
LinesOfCode loc;
|
||||||
|
|
||||||
do
|
do {
|
||||||
{
|
std::getline(iss, curLine);
|
||||||
std::getline(iss, line);
|
|
||||||
++count;
|
++count;
|
||||||
if (count < pl)
|
if (count < pl)
|
||||||
{
|
|
||||||
;
|
;
|
||||||
}
|
|
||||||
else if (count == pl) {
|
else if (count == pl) {
|
||||||
loc.prevLineOfCode = line;
|
loc.prevLineOfCode = curLine;
|
||||||
} else if (count == pl + 1) {
|
} else if (count == pl + 1) {
|
||||||
loc.errLineOfCode = line;
|
loc.errLineOfCode = curLine;
|
||||||
} else if (count == pl + 2) {
|
} else if (count == pl + 2) {
|
||||||
loc.nextLineOfCode = line;
|
loc.nextLineOfCode = curLine;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,12 +79,14 @@ std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos)
|
||||||
|
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// print lines of code to the ostream, indicating the error column.
|
// print lines of code to the ostream, indicating the error column.
|
||||||
void printCodeLines(std::ostream & out,
|
void printCodeLines(std::ostream & out,
|
||||||
const std::string & prefix,
|
const std::string & prefix,
|
||||||
const ErrPos & errPos,
|
const AbstractPos & errPos,
|
||||||
const LinesOfCode & loc)
|
const LinesOfCode & loc)
|
||||||
{
|
{
|
||||||
// previous line of code.
|
// previous line of code.
|
||||||
|
@ -176,28 +133,6 @@ void printCodeLines(std::ostream & out,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void printAtPos(const ErrPos & pos, std::ostream & out)
|
|
||||||
{
|
|
||||||
if (pos) {
|
|
||||||
switch (pos.origin) {
|
|
||||||
case foFile: {
|
|
||||||
out << fmt(ANSI_BLUE "at " ANSI_WARNING "%s:%s" ANSI_NORMAL ":", pos.file, showErrPos(pos));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case foString: {
|
|
||||||
out << fmt(ANSI_BLUE "at " ANSI_WARNING "«string»:%s" ANSI_NORMAL ":", showErrPos(pos));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case foStdin: {
|
|
||||||
out << fmt(ANSI_BLUE "at " ANSI_WARNING "«stdin»:%s" ANSI_NORMAL ":", showErrPos(pos));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw Error("invalid FileOrigin in errPos");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string indent(std::string_view indentFirst, std::string_view indentRest, std::string_view s)
|
static std::string indent(std::string_view indentFirst, std::string_view indentRest, std::string_view s)
|
||||||
{
|
{
|
||||||
std::string res;
|
std::string res;
|
||||||
|
@ -263,22 +198,22 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
|
||||||
|
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
auto noSource = ANSI_ITALIC " (source not available)" ANSI_NORMAL "\n";
|
||||||
|
|
||||||
// traces
|
// traces
|
||||||
if (showTrace && !einfo.traces.empty()) {
|
if (showTrace && !einfo.traces.empty()) {
|
||||||
for (const auto & trace : einfo.traces) {
|
for (const auto & trace : einfo.traces) {
|
||||||
oss << "\n" << "… " << trace.hint.str() << "\n";
|
oss << "\n" << "… " << trace.hint.str() << "\n";
|
||||||
|
|
||||||
if (trace.pos.has_value() && (*trace.pos)) {
|
if (trace.pos) {
|
||||||
auto pos = trace.pos.value();
|
oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *trace.pos << ANSI_NORMAL << ":";
|
||||||
oss << "\n";
|
|
||||||
printAtPos(pos, oss);
|
|
||||||
|
|
||||||
auto loc = getCodeLines(pos);
|
if (auto loc = trace.pos->getCodeLines()) {
|
||||||
if (loc.has_value()) {
|
|
||||||
oss << "\n";
|
oss << "\n";
|
||||||
printCodeLines(oss, "", pos, *loc);
|
printCodeLines(oss, "", *trace.pos, *loc);
|
||||||
oss << "\n";
|
oss << "\n";
|
||||||
}
|
} else
|
||||||
|
oss << noSource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
oss << "\n" << prefix;
|
oss << "\n" << prefix;
|
||||||
|
@ -286,22 +221,19 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
|
||||||
|
|
||||||
oss << einfo.msg << "\n";
|
oss << einfo.msg << "\n";
|
||||||
|
|
||||||
if (einfo.errPos.has_value() && *einfo.errPos) {
|
if (einfo.errPos) {
|
||||||
oss << "\n";
|
oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *einfo.errPos << ANSI_NORMAL << ":";
|
||||||
printAtPos(*einfo.errPos, oss);
|
|
||||||
|
|
||||||
auto loc = getCodeLines(*einfo.errPos);
|
if (auto loc = einfo.errPos->getCodeLines()) {
|
||||||
|
|
||||||
// lines of code.
|
|
||||||
if (loc.has_value()) {
|
|
||||||
oss << "\n";
|
oss << "\n";
|
||||||
printCodeLines(oss, "", *einfo.errPos, *loc);
|
printCodeLines(oss, "", *einfo.errPos, *loc);
|
||||||
oss << "\n";
|
oss << "\n";
|
||||||
}
|
} else
|
||||||
|
oss << noSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto suggestions = einfo.suggestions.trim();
|
auto suggestions = einfo.suggestions.trim();
|
||||||
if (! suggestions.suggestions.empty()){
|
if (!suggestions.suggestions.empty()) {
|
||||||
oss << "Did you mean " <<
|
oss << "Did you mean " <<
|
||||||
suggestions.trim() <<
|
suggestions.trim() <<
|
||||||
"?" << std::endl;
|
"?" << std::endl;
|
||||||
|
|
|
@ -54,13 +54,6 @@ typedef enum {
|
||||||
lvlVomit
|
lvlVomit
|
||||||
} Verbosity;
|
} Verbosity;
|
||||||
|
|
||||||
/* adjust Pos::origin bit width when adding stuff here */
|
|
||||||
typedef enum {
|
|
||||||
foFile,
|
|
||||||
foStdin,
|
|
||||||
foString
|
|
||||||
} FileOrigin;
|
|
||||||
|
|
||||||
// the lines of code surrounding an error.
|
// the lines of code surrounding an error.
|
||||||
struct LinesOfCode {
|
struct LinesOfCode {
|
||||||
std::optional<std::string> prevLineOfCode;
|
std::optional<std::string> prevLineOfCode;
|
||||||
|
@ -68,54 +61,37 @@ struct LinesOfCode {
|
||||||
std::optional<std::string> nextLineOfCode;
|
std::optional<std::string> nextLineOfCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ErrPos indicates the location of an error in a nix file.
|
/* An abstract type that represents a location in a source file. */
|
||||||
struct ErrPos {
|
struct AbstractPos
|
||||||
int line = 0;
|
{
|
||||||
int column = 0;
|
uint32_t line = 0;
|
||||||
std::string file;
|
uint32_t column = 0;
|
||||||
FileOrigin origin;
|
|
||||||
|
|
||||||
operator bool() const
|
/* Return the contents of the source file. */
|
||||||
{
|
virtual std::optional<std::string> getSource() const
|
||||||
return line != 0;
|
{ return std::nullopt; };
|
||||||
}
|
|
||||||
|
|
||||||
// convert from the Pos struct, found in libexpr.
|
virtual void print(std::ostream & out) const = 0;
|
||||||
template <class P>
|
|
||||||
ErrPos & operator=(const P & pos)
|
|
||||||
{
|
|
||||||
origin = pos.origin;
|
|
||||||
line = pos.line;
|
|
||||||
column = pos.column;
|
|
||||||
file = pos.file;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
std::optional<LinesOfCode> getCodeLines() const;
|
||||||
ErrPos(const P & p)
|
|
||||||
{
|
|
||||||
*this = p;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos);
|
std::ostream & operator << (std::ostream & str, const AbstractPos & pos);
|
||||||
|
|
||||||
void printCodeLines(std::ostream & out,
|
void printCodeLines(std::ostream & out,
|
||||||
const std::string & prefix,
|
const std::string & prefix,
|
||||||
const ErrPos & errPos,
|
const AbstractPos & errPos,
|
||||||
const LinesOfCode & loc);
|
const LinesOfCode & loc);
|
||||||
|
|
||||||
void printAtPos(const ErrPos & pos, std::ostream & out);
|
|
||||||
|
|
||||||
struct Trace {
|
struct Trace {
|
||||||
std::optional<ErrPos> pos;
|
std::shared_ptr<AbstractPos> pos;
|
||||||
hintformat hint;
|
hintformat hint;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ErrorInfo {
|
struct ErrorInfo {
|
||||||
Verbosity level;
|
Verbosity level;
|
||||||
hintformat msg;
|
hintformat msg;
|
||||||
std::optional<ErrPos> errPos;
|
std::shared_ptr<AbstractPos> errPos;
|
||||||
std::list<Trace> traces;
|
std::list<Trace> traces;
|
||||||
|
|
||||||
Suggestions suggestions;
|
Suggestions suggestions;
|
||||||
|
@ -177,12 +153,12 @@ public:
|
||||||
const ErrorInfo & info() const { calcWhat(); return err; }
|
const ErrorInfo & info() const { calcWhat(); return err; }
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void addTrace(std::optional<ErrPos> e, const std::string & fs, const Args & ... args)
|
void addTrace(std::shared_ptr<AbstractPos> && e, const std::string & fs, const Args & ... args)
|
||||||
{
|
{
|
||||||
addTrace(e, hintfmt(fs, args...));
|
addTrace(std::move(e), hintfmt(fs, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
void addTrace(std::optional<ErrPos> e, hintformat hint);
|
void addTrace(std::shared_ptr<AbstractPos> && e, hintformat hint);
|
||||||
|
|
||||||
bool hasTrace() const { return !err.traces.empty(); }
|
bool hasTrace() const { return !err.traces.empty(); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -131,6 +131,21 @@ Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type,
|
||||||
logger.startActivity(id, lvl, type, s, fields, parent);
|
logger.startActivity(id, lvl, type, s, fields, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void to_json(nlohmann::json & json, std::shared_ptr<AbstractPos> pos)
|
||||||
|
{
|
||||||
|
if (pos) {
|
||||||
|
json["line"] = pos->line;
|
||||||
|
json["column"] = pos->column;
|
||||||
|
std::ostringstream str;
|
||||||
|
pos->print(str);
|
||||||
|
json["file"] = str.str();
|
||||||
|
} else {
|
||||||
|
json["line"] = nullptr;
|
||||||
|
json["column"] = nullptr;
|
||||||
|
json["file"] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct JSONLogger : Logger {
|
struct JSONLogger : Logger {
|
||||||
Logger & prevLogger;
|
Logger & prevLogger;
|
||||||
|
|
||||||
|
@ -177,27 +192,14 @@ struct JSONLogger : Logger {
|
||||||
json["level"] = ei.level;
|
json["level"] = ei.level;
|
||||||
json["msg"] = oss.str();
|
json["msg"] = oss.str();
|
||||||
json["raw_msg"] = ei.msg.str();
|
json["raw_msg"] = ei.msg.str();
|
||||||
|
to_json(json, ei.errPos);
|
||||||
if (ei.errPos.has_value() && (*ei.errPos)) {
|
|
||||||
json["line"] = ei.errPos->line;
|
|
||||||
json["column"] = ei.errPos->column;
|
|
||||||
json["file"] = ei.errPos->file;
|
|
||||||
} else {
|
|
||||||
json["line"] = nullptr;
|
|
||||||
json["column"] = nullptr;
|
|
||||||
json["file"] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loggerSettings.showTrace.get() && !ei.traces.empty()) {
|
if (loggerSettings.showTrace.get() && !ei.traces.empty()) {
|
||||||
nlohmann::json traces = nlohmann::json::array();
|
nlohmann::json traces = nlohmann::json::array();
|
||||||
for (auto iter = ei.traces.rbegin(); iter != ei.traces.rend(); ++iter) {
|
for (auto iter = ei.traces.rbegin(); iter != ei.traces.rend(); ++iter) {
|
||||||
nlohmann::json stackFrame;
|
nlohmann::json stackFrame;
|
||||||
stackFrame["raw_msg"] = iter->hint.str();
|
stackFrame["raw_msg"] = iter->hint.str();
|
||||||
if (iter->pos.has_value() && (*iter->pos)) {
|
to_json(stackFrame, iter->pos);
|
||||||
stackFrame["line"] = iter->pos->line;
|
|
||||||
stackFrame["column"] = iter->pos->column;
|
|
||||||
stackFrame["file"] = iter->pos->file;
|
|
||||||
}
|
|
||||||
traces.push_back(stackFrame);
|
traces.push_back(stackFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
155
src/libutil/tests/canon-path.cc
Normal file
155
src/libutil/tests/canon-path.cc
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
#include "canon-path.hh"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
TEST(CanonPath, basic) {
|
||||||
|
{
|
||||||
|
CanonPath p("/");
|
||||||
|
ASSERT_EQ(p.abs(), "/");
|
||||||
|
ASSERT_EQ(p.rel(), "");
|
||||||
|
ASSERT_EQ(p.baseName(), std::nullopt);
|
||||||
|
ASSERT_EQ(p.dirOf(), std::nullopt);
|
||||||
|
ASSERT_FALSE(p.parent());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("/foo//");
|
||||||
|
ASSERT_EQ(p.abs(), "/foo");
|
||||||
|
ASSERT_EQ(p.rel(), "foo");
|
||||||
|
ASSERT_EQ(*p.baseName(), "foo");
|
||||||
|
ASSERT_EQ(*p.dirOf(), ""); // FIXME: do we want this?
|
||||||
|
ASSERT_EQ(p.parent()->abs(), "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("foo/bar");
|
||||||
|
ASSERT_EQ(p.abs(), "/foo/bar");
|
||||||
|
ASSERT_EQ(p.rel(), "foo/bar");
|
||||||
|
ASSERT_EQ(*p.baseName(), "bar");
|
||||||
|
ASSERT_EQ(*p.dirOf(), "/foo");
|
||||||
|
ASSERT_EQ(p.parent()->abs(), "/foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("foo//bar/");
|
||||||
|
ASSERT_EQ(p.abs(), "/foo/bar");
|
||||||
|
ASSERT_EQ(p.rel(), "foo/bar");
|
||||||
|
ASSERT_EQ(*p.baseName(), "bar");
|
||||||
|
ASSERT_EQ(*p.dirOf(), "/foo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, pop) {
|
||||||
|
CanonPath p("foo/bar/x");
|
||||||
|
ASSERT_EQ(p.abs(), "/foo/bar/x");
|
||||||
|
p.pop();
|
||||||
|
ASSERT_EQ(p.abs(), "/foo/bar");
|
||||||
|
p.pop();
|
||||||
|
ASSERT_EQ(p.abs(), "/foo");
|
||||||
|
p.pop();
|
||||||
|
ASSERT_EQ(p.abs(), "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, removePrefix) {
|
||||||
|
CanonPath p1("foo/bar");
|
||||||
|
CanonPath p2("foo/bar/a/b/c");
|
||||||
|
ASSERT_EQ(p2.removePrefix(p1).abs(), "/a/b/c");
|
||||||
|
ASSERT_EQ(p1.removePrefix(p1).abs(), "/");
|
||||||
|
ASSERT_EQ(p1.removePrefix(CanonPath("/")).abs(), "/foo/bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, iter) {
|
||||||
|
{
|
||||||
|
CanonPath p("a//foo/bar//");
|
||||||
|
std::vector<std::string_view> ss;
|
||||||
|
for (auto & c : p) ss.push_back(c);
|
||||||
|
ASSERT_EQ(ss, std::vector<std::string_view>({"a", "foo", "bar"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("/");
|
||||||
|
std::vector<std::string_view> ss;
|
||||||
|
for (auto & c : p) ss.push_back(c);
|
||||||
|
ASSERT_EQ(ss, std::vector<std::string_view>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, concat) {
|
||||||
|
{
|
||||||
|
CanonPath p1("a//foo/bar//");
|
||||||
|
CanonPath p2("xyzzy/bla");
|
||||||
|
ASSERT_EQ((p1 + p2).abs(), "/a/foo/bar/xyzzy/bla");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p1("/");
|
||||||
|
CanonPath p2("/a/b");
|
||||||
|
ASSERT_EQ((p1 + p2).abs(), "/a/b");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p1("/a/b");
|
||||||
|
CanonPath p2("/");
|
||||||
|
ASSERT_EQ((p1 + p2).abs(), "/a/b");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("/foo/bar");
|
||||||
|
ASSERT_EQ((p + "x").abs(), "/foo/bar/x");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("/");
|
||||||
|
ASSERT_EQ((p + "foo" + "bar").abs(), "/foo/bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, within) {
|
||||||
|
{
|
||||||
|
ASSERT_TRUE(CanonPath("foo").isWithin(CanonPath("foo")));
|
||||||
|
ASSERT_FALSE(CanonPath("foo").isWithin(CanonPath("bar")));
|
||||||
|
ASSERT_FALSE(CanonPath("foo").isWithin(CanonPath("fo")));
|
||||||
|
ASSERT_TRUE(CanonPath("foo/bar").isWithin(CanonPath("foo")));
|
||||||
|
ASSERT_FALSE(CanonPath("foo").isWithin(CanonPath("foo/bar")));
|
||||||
|
ASSERT_TRUE(CanonPath("/foo/bar/default.nix").isWithin(CanonPath("/")));
|
||||||
|
ASSERT_TRUE(CanonPath("/").isWithin(CanonPath("/")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, sort) {
|
||||||
|
ASSERT_FALSE(CanonPath("foo") < CanonPath("foo"));
|
||||||
|
ASSERT_TRUE (CanonPath("foo") < CanonPath("foo/bar"));
|
||||||
|
ASSERT_TRUE (CanonPath("foo/bar") < CanonPath("foo!"));
|
||||||
|
ASSERT_FALSE(CanonPath("foo!") < CanonPath("foo"));
|
||||||
|
ASSERT_TRUE (CanonPath("foo") < CanonPath("foo!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, allowed) {
|
||||||
|
{
|
||||||
|
std::set<CanonPath> allowed {
|
||||||
|
CanonPath("foo/bar"),
|
||||||
|
CanonPath("foo!"),
|
||||||
|
CanonPath("xyzzy"),
|
||||||
|
CanonPath("a/b/c"),
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_TRUE (CanonPath("foo/bar").isAllowed(allowed));
|
||||||
|
ASSERT_TRUE (CanonPath("foo/bar/bla").isAllowed(allowed));
|
||||||
|
ASSERT_TRUE (CanonPath("foo").isAllowed(allowed));
|
||||||
|
ASSERT_FALSE(CanonPath("bar").isAllowed(allowed));
|
||||||
|
ASSERT_FALSE(CanonPath("bar/a").isAllowed(allowed));
|
||||||
|
ASSERT_TRUE (CanonPath("a").isAllowed(allowed));
|
||||||
|
ASSERT_TRUE (CanonPath("a/b").isAllowed(allowed));
|
||||||
|
ASSERT_TRUE (CanonPath("a/b/c").isAllowed(allowed));
|
||||||
|
ASSERT_TRUE (CanonPath("a/b/c/d").isAllowed(allowed));
|
||||||
|
ASSERT_TRUE (CanonPath("a/b/c/d/e").isAllowed(allowed));
|
||||||
|
ASSERT_FALSE(CanonPath("a/b/a").isAllowed(allowed));
|
||||||
|
ASSERT_FALSE(CanonPath("a/b/d").isAllowed(allowed));
|
||||||
|
ASSERT_FALSE(CanonPath("aaa").isAllowed(allowed));
|
||||||
|
ASSERT_FALSE(CanonPath("zzz").isAllowed(allowed));
|
||||||
|
ASSERT_TRUE (CanonPath("/").isAllowed(allowed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -647,7 +647,7 @@ static void upgradeDerivations(Globals & globals,
|
||||||
} else newElems.push_back(i);
|
} else newElems.push_back(i);
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(std::nullopt, "while trying to find an upgrade for '%s'", i.queryName());
|
e.addTrace(nullptr, "while trying to find an upgrade for '%s'", i.queryName());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -958,7 +958,7 @@ static void queryJSON(Globals & globals, std::vector<DrvInfo> & elems, bool prin
|
||||||
} catch (AssertionError & e) {
|
} catch (AssertionError & e) {
|
||||||
printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName());
|
printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName());
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(std::nullopt, "while querying the derivation named '%1%'", i.queryName());
|
e.addTrace(nullptr, "while querying the derivation named '%1%'", i.queryName());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1262,7 +1262,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
} catch (AssertionError & e) {
|
} catch (AssertionError & e) {
|
||||||
printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName());
|
printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName());
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(std::nullopt, "while querying the derivation named '%1%'", i.queryName());
|
e.addTrace(nullptr, "while querying the derivation named '%1%'", i.queryName());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,10 +192,12 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore
|
||||||
drv.env.erase("allowedRequisites");
|
drv.env.erase("allowedRequisites");
|
||||||
drv.env.erase("disallowedReferences");
|
drv.env.erase("disallowedReferences");
|
||||||
drv.env.erase("disallowedRequisites");
|
drv.env.erase("disallowedRequisites");
|
||||||
|
drv.env.erase("name");
|
||||||
|
|
||||||
/* Rehash and write the derivation. FIXME: would be nice to use
|
/* Rehash and write the derivation. FIXME: would be nice to use
|
||||||
'buildDerivation', but that's privileged. */
|
'buildDerivation', but that's privileged. */
|
||||||
drv.name += "-env";
|
drv.name += "-env";
|
||||||
|
drv.env.emplace("name", drv.name);
|
||||||
drv.inputSrcs.insert(std::move(getEnvShPath));
|
drv.inputSrcs.insert(std::move(getEnvShPath));
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||||
for (auto & output : drv.outputs) {
|
for (auto & output : drv.outputs) {
|
||||||
|
|
|
@ -14,6 +14,15 @@ subRepo=$TEST_ROOT/gitSubmodulesSub
|
||||||
|
|
||||||
rm -rf ${rootRepo} ${subRepo} $TEST_HOME/.cache/nix
|
rm -rf ${rootRepo} ${subRepo} $TEST_HOME/.cache/nix
|
||||||
|
|
||||||
|
# Submodules can't be fetched locally by default, which can cause
|
||||||
|
# information leakage vulnerabilities, but for these tests our
|
||||||
|
# submodule is intentionally local and it's all trusted, so we
|
||||||
|
# disable this restriction. Setting it per repo is not sufficient, as
|
||||||
|
# the repo-local config does not apply to the commands run from
|
||||||
|
# outside the repos by Nix.
|
||||||
|
export XDG_CONFIG_HOME=$TEST_HOME/.config
|
||||||
|
git config --global protocol.file.allow always
|
||||||
|
|
||||||
initGitRepo() {
|
initGitRepo() {
|
||||||
git init $1
|
git init $1
|
||||||
git -C $1 config user.email "foobar@example.com"
|
git -C $1 config user.email "foobar@example.com"
|
||||||
|
|
|
@ -32,40 +32,40 @@ expect_trace() {
|
||||||
|
|
||||||
# failure inside a tryEval
|
# failure inside a tryEval
|
||||||
expect_trace 'builtins.tryEval (throw "example")' "
|
expect_trace 'builtins.tryEval (throw "example")' "
|
||||||
function-trace entered (string):1:1 at
|
function-trace entered «string»:1:1 at
|
||||||
function-trace entered (string):1:19 at
|
function-trace entered «string»:1:19 at
|
||||||
function-trace exited (string):1:19 at
|
function-trace exited «string»:1:19 at
|
||||||
function-trace exited (string):1:1 at
|
function-trace exited «string»:1:1 at
|
||||||
"
|
"
|
||||||
|
|
||||||
# Missing argument to a formal function
|
# Missing argument to a formal function
|
||||||
expect_trace '({ x }: x) { }' "
|
expect_trace '({ x }: x) { }' "
|
||||||
function-trace entered (string):1:1 at
|
function-trace entered «string»:1:1 at
|
||||||
function-trace exited (string):1:1 at
|
function-trace exited «string»:1:1 at
|
||||||
"
|
"
|
||||||
|
|
||||||
# Too many arguments to a formal function
|
# Too many arguments to a formal function
|
||||||
expect_trace '({ x }: x) { x = "x"; y = "y"; }' "
|
expect_trace '({ x }: x) { x = "x"; y = "y"; }' "
|
||||||
function-trace entered (string):1:1 at
|
function-trace entered «string»:1:1 at
|
||||||
function-trace exited (string):1:1 at
|
function-trace exited «string»:1:1 at
|
||||||
"
|
"
|
||||||
|
|
||||||
# Not enough arguments to a lambda
|
# Not enough arguments to a lambda
|
||||||
expect_trace '(x: y: x + y) 1' "
|
expect_trace '(x: y: x + y) 1' "
|
||||||
function-trace entered (string):1:1 at
|
function-trace entered «string»:1:1 at
|
||||||
function-trace exited (string):1:1 at
|
function-trace exited «string»:1:1 at
|
||||||
"
|
"
|
||||||
|
|
||||||
# Too many arguments to a lambda
|
# Too many arguments to a lambda
|
||||||
expect_trace '(x: x) 1 2' "
|
expect_trace '(x: x) 1 2' "
|
||||||
function-trace entered (string):1:1 at
|
function-trace entered «string»:1:1 at
|
||||||
function-trace exited (string):1:1 at
|
function-trace exited «string»:1:1 at
|
||||||
"
|
"
|
||||||
|
|
||||||
# Not a function
|
# Not a function
|
||||||
expect_trace '1 2' "
|
expect_trace '1 2' "
|
||||||
function-trace entered (string):1:1 at
|
function-trace entered «string»:1:1 at
|
||||||
function-trace exited (string):1:1 at
|
function-trace exited «string»:1:1 at
|
||||||
"
|
"
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
Loading…
Reference in a new issue