libexpr: don't debugThrow from async code
running a debugger is never-async since it holds an EvalState reference. fixes #761 Change-Id: Ie1b4df6f81bb5614f11ea183eb9d1058fde73121
This commit is contained in:
@@ -255,7 +255,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
|
||||
evaluator->paths.resolveExprPath(aio.blockOn(lookupFileArg(
|
||||
*evaluator,
|
||||
inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i
|
||||
)))
|
||||
)).unwrap())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1558,7 +1558,7 @@ static int main_nix_env(AsyncIoRoot & aio, std::string programName, Strings argv
|
||||
|
||||
globals.instSource.nixExprPath = std::make_shared<SourcePath>(
|
||||
file != ""
|
||||
? aio.blockOn(lookupFileArg(*globals.state, file))
|
||||
? aio.blockOn(lookupFileArg(*globals.state, file)).unwrap()
|
||||
: CanonPath(nixExprPath));
|
||||
|
||||
globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state);
|
||||
|
||||
@@ -167,7 +167,7 @@ static int main_nix_instantiate(AsyncIoRoot & aio, std::string programName, Stri
|
||||
|
||||
if (findFile) {
|
||||
for (auto & i : files) {
|
||||
auto p = aio.blockOn(evaluator->paths.findFile(i));
|
||||
auto p = aio.blockOn(evaluator->paths.findFile(i)).unwrap();
|
||||
std::cout << p.canonical().abs() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
@@ -181,11 +181,10 @@ static int main_nix_instantiate(AsyncIoRoot & aio, std::string programName, Stri
|
||||
files.push_back("./default.nix");
|
||||
|
||||
for (auto & i : files) {
|
||||
Expr & e = fromArgs
|
||||
? evaluator->parseExprFromString(i, CanonPath::fromCwd())
|
||||
: evaluator->parseExprFromFile(
|
||||
evaluator->paths.resolveExprPath(aio.blockOn(lookupFileArg(*evaluator, i)))
|
||||
);
|
||||
Expr & e = fromArgs ? evaluator->parseExprFromString(i, CanonPath::fromCwd())
|
||||
: evaluator->parseExprFromFile(evaluator->paths.resolveExprPath(
|
||||
aio.blockOn(lookupFileArg(*evaluator, i)).unwrap()
|
||||
));
|
||||
processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
|
||||
evalOnly, outputKind, xmlOutputSourceLocation, e);
|
||||
}
|
||||
|
||||
@@ -188,7 +188,8 @@ Bindings * MixEvalArgs::getAutoArgs(Evaluator & state)
|
||||
return res.finish();
|
||||
}
|
||||
|
||||
kj::Promise<Result<SourcePath>> lookupFileArg(Evaluator & state, std::string_view fileArg)
|
||||
kj::Promise<Result<EvalPaths::PathResult<SourcePath, ThrownError>>>
|
||||
lookupFileArg(Evaluator & state, std::string_view fileArg)
|
||||
try {
|
||||
if (EvalSettings::isPseudoUrl(fileArg)) {
|
||||
auto const url = EvalSettings::resolvePseudoUrl(fileArg);
|
||||
@@ -199,19 +200,20 @@ try {
|
||||
/* locked */ false
|
||||
));
|
||||
StorePath const storePath = downloaded.tree.storePath;
|
||||
co_return CanonPath(state.store->toRealPath(storePath));
|
||||
co_return SourcePath(CanonPath(state.store->toRealPath(storePath)));
|
||||
} else if (fileArg.starts_with("flake:")) {
|
||||
experimentalFeatureSettings.require(Xp::Flakes);
|
||||
static constexpr size_t FLAKE_LEN = std::string_view("flake:").size();
|
||||
auto flakeRef = parseFlakeRef(std::string(fileArg.substr(FLAKE_LEN)), {}, true, false);
|
||||
auto storePath = TRY_AWAIT(TRY_AWAIT(flakeRef.resolve(state.store)).fetchTree(state.store))
|
||||
.first.storePath;
|
||||
co_return CanonPath(state.store->toRealPath(storePath));
|
||||
} else if (fileArg.size() > 2 && fileArg.at(0) == '<' && fileArg.at(fileArg.size() - 1) == '>') {
|
||||
co_return SourcePath(CanonPath(state.store->toRealPath(storePath)));
|
||||
} else if (fileArg.size() > 2 && fileArg.at(0) == '<' && fileArg.at(fileArg.size() - 1) == '>')
|
||||
{
|
||||
Path p(fileArg.substr(1, fileArg.size() - 2));
|
||||
co_return TRY_AWAIT(state.paths.findFile(p));
|
||||
} else {
|
||||
co_return CanonPath::fromCwd(fileArg);
|
||||
co_return SourcePath(CanonPath::fromCwd(fileArg));
|
||||
}
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "lix/libexpr/eval-error.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libutil/args.hh"
|
||||
#include "lix/libmain/common-args.hh"
|
||||
@@ -49,6 +50,7 @@ private:
|
||||
*
|
||||
* @exception nix::ThrownError for failed search path lookup. Probably others.
|
||||
*/
|
||||
kj::Promise<Result<SourcePath>> lookupFileArg(Evaluator & state, std::string_view fileArg);
|
||||
kj::Promise<Result<EvalPaths::PathResult<SourcePath, ThrownError>>>
|
||||
lookupFileArg(Evaluator & state, std::string_view fileArg);
|
||||
|
||||
}
|
||||
|
||||
@@ -215,9 +215,9 @@ void SourceExprCommand::completeInstallable(EvalState & state, AddCompletions &
|
||||
|
||||
auto evaluator = getEvaluator();
|
||||
|
||||
Expr & e = evaluator->parseExprFromFile(
|
||||
state.ctx.paths.resolveExprPath(state.aio.blockOn(lookupFileArg(*evaluator, *file)))
|
||||
);
|
||||
Expr & e = evaluator->parseExprFromFile(state.ctx.paths.resolveExprPath(
|
||||
state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap()
|
||||
));
|
||||
|
||||
Value root;
|
||||
state.eval(e, root);
|
||||
@@ -457,7 +457,7 @@ Installables SourceExprCommand::parseInstallables(
|
||||
state.eval(e, *vFile);
|
||||
}
|
||||
else if (file)
|
||||
state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)), *vFile);
|
||||
state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap(), *vFile);
|
||||
else {
|
||||
auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd());
|
||||
state.eval(e, *vFile);
|
||||
|
||||
+2
-1
@@ -34,6 +34,7 @@
|
||||
#include "lix/libutil/signals.hh"
|
||||
#include "lix/libexpr/print.hh"
|
||||
#include "lix/libexpr/gc-small-vector.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "lix/libutil/users.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
@@ -899,7 +900,7 @@ void NixRepl::loadFile(const Path & path)
|
||||
loadedFiles.remove(path);
|
||||
loadedFiles.push_back(path);
|
||||
Value v, v2;
|
||||
state.evalFile(state.aio.blockOn(lookupFileArg(evaluator, path)), v);
|
||||
state.evalFile(state.aio.blockOn(lookupFileArg(evaluator, path)).unwrap(always_progresses), v);
|
||||
state.autoCallFunction(*autoArgs, v, v2);
|
||||
addAttrsToScope(v2);
|
||||
}
|
||||
|
||||
+11
-9
@@ -2322,7 +2322,7 @@ BackedStringView EvalState::coerceToString(
|
||||
v._path
|
||||
: copyToStore
|
||||
? ctx.store->printStorePath(
|
||||
aio.blockOn(ctx.paths.copyPathToStore(context, v.path(), ctx.repair)))
|
||||
aio.blockOn(ctx.paths.copyPathToStore(context, v.path(), ctx.repair)).unwrap())
|
||||
: v.path().to_string();
|
||||
}
|
||||
|
||||
@@ -2391,10 +2391,11 @@ BackedStringView EvalState::coerceToString(
|
||||
}
|
||||
|
||||
|
||||
kj::Promise<Result<StorePath>> EvalPaths::copyPathToStore(NixStringContext & context, const SourcePath & path, RepairFlag repair)
|
||||
kj::Promise<Result<EvalPaths::PathResult<StorePath, EvalError>>>
|
||||
EvalPaths::copyPathToStore(NixStringContext & context, const SourcePath & path, RepairFlag repair)
|
||||
try {
|
||||
if (nix::isDerivation(path.canonical().abs()))
|
||||
errors.make<EvalError>("file names are not allowed to end in '%1%'", drvExtension).debugThrow();
|
||||
co_return errors.make<EvalError>("file names are not allowed to end in '%1%'", drvExtension);
|
||||
|
||||
auto i = srcToStore.find(path);
|
||||
|
||||
@@ -2792,13 +2793,14 @@ Expr & Evaluator::parseStdin()
|
||||
}
|
||||
|
||||
|
||||
kj::Promise<Result<SourcePath>> EvalPaths::findFile(const std::string_view path)
|
||||
kj::Promise<Result<EvalPaths::PathResult<SourcePath, ThrownError>>>
|
||||
EvalPaths::findFile(const std::string_view path)
|
||||
{
|
||||
return findFile(searchPath_, path);
|
||||
}
|
||||
|
||||
|
||||
kj::Promise<Result<SourcePath>>
|
||||
kj::Promise<Result<EvalPaths::PathResult<SourcePath, ThrownError>>>
|
||||
EvalPaths::findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos)
|
||||
try {
|
||||
for (auto & i : searchPath.elements) {
|
||||
@@ -2812,18 +2814,18 @@ try {
|
||||
auto r = *rOpt;
|
||||
|
||||
Path res = suffix == "" ? r : concatStrings(r, "/", suffix);
|
||||
if (pathExists(res)) co_return CanonPath(canonPath(res));
|
||||
if (pathExists(res)) co_return SourcePath(CanonPath(canonPath(res)));
|
||||
}
|
||||
|
||||
if (path.starts_with("nix/"))
|
||||
co_return CanonPath(concatStrings(corepkgsPrefix, path.substr(4)));
|
||||
co_return SourcePath(CanonPath(concatStrings(corepkgsPrefix, path.substr(4))));
|
||||
|
||||
errors.make<ThrownError>(
|
||||
co_return errors.make<ThrownError>(
|
||||
evalSettings.pureEval
|
||||
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
||||
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
||||
path
|
||||
).atPos(pos).debugThrow();
|
||||
).atPos(pos);
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
+29
-3
@@ -451,11 +451,37 @@ public:
|
||||
*/
|
||||
Path toRealPath(const Path & path, const NixStringContext & context);
|
||||
|
||||
/**
|
||||
* findFile wants to throw a debuggable error when the requested file
|
||||
* is not found, but it can't invoke the debugger itself because it's
|
||||
* async code. This wraps the result-or-error to allow it regardless.
|
||||
* This happens for copyPathToStore as well, with another error type.
|
||||
*/
|
||||
template<typename T, typename E>
|
||||
struct PathResult : private std::variant<T, EvalErrorBuilder<E>>
|
||||
{
|
||||
PathResult(T p) : std::variant<T, EvalErrorBuilder<E>>(std::move(p)) {}
|
||||
PathResult(EvalErrorBuilder<E> e) : std::variant<T, EvalErrorBuilder<E>>(std::move(e)) {}
|
||||
|
||||
T unwrap(NeverAsync = {}) &&
|
||||
{
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[](T & p) -> T { return std::move(p); },
|
||||
[](EvalErrorBuilder<E> & e) -> T {
|
||||
std::move(e).debugThrow();
|
||||
}
|
||||
},
|
||||
static_cast<std::variant<T, EvalErrorBuilder<E>> &>(*this)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Look up a file in the search path.
|
||||
*/
|
||||
kj::Promise<Result<SourcePath>> findFile(const std::string_view path);
|
||||
kj::Promise<Result<SourcePath>>
|
||||
kj::Promise<Result<PathResult<SourcePath, ThrownError>>> findFile(const std::string_view path);
|
||||
kj::Promise<Result<PathResult<SourcePath, ThrownError>>>
|
||||
findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
|
||||
|
||||
/**
|
||||
@@ -468,7 +494,7 @@ public:
|
||||
kj::Promise<Result<std::optional<std::string>>>
|
||||
resolveSearchPathPath(const SearchPath::Path & path);
|
||||
|
||||
kj::Promise<Result<StorePath>> copyPathToStore(
|
||||
kj::Promise<Result<PathResult<StorePath, EvalError>>> copyPathToStore(
|
||||
NixStringContext & context, const SourcePath & path, RepairFlag repair = NoRepair
|
||||
);
|
||||
|
||||
|
||||
@@ -1339,7 +1339,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V
|
||||
auto path = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.findFile");
|
||||
|
||||
v.mkPath(state.ctx.paths.checkSourcePath(
|
||||
state.aio.blockOn(state.ctx.paths.findFile(searchPath, path, pos))
|
||||
state.aio.blockOn(state.ctx.paths.findFile(searchPath, path, pos)).unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ JSON printValueAsJSON(EvalState & state, bool strict,
|
||||
if (copyToStore)
|
||||
out = state.ctx.store->printStorePath(state.aio.blockOn(
|
||||
state.ctx.paths.copyPathToStore(context, v.path(), state.ctx.repair)
|
||||
));
|
||||
).unwrap());
|
||||
else {
|
||||
out = v.path().to_string();
|
||||
}
|
||||
|
||||
+1
-1
@@ -204,7 +204,7 @@ static int main_nix_prefetch_url(AsyncIoRoot & aio, std::string programName, Str
|
||||
Value vRoot;
|
||||
state->evalFile(
|
||||
evaluator->paths.resolveExprPath(
|
||||
aio.blockOn(lookupFileArg(*evaluator, args.empty() ? "." : args[0]))),
|
||||
aio.blockOn(lookupFileArg(*evaluator, args.empty() ? "." : args[0])).unwrap()),
|
||||
vRoot);
|
||||
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
|
||||
state->forceAttrs(v, noPos, "while evaluating the source attribute to prefetch");
|
||||
|
||||
@@ -51,7 +51,10 @@ static nix::Value *releaseExprTopLevelValue(nix::EvalState &state,
|
||||
nix::CanonPath::fromCwd());
|
||||
state.eval(e, vTop);
|
||||
} else {
|
||||
state.evalFile(state.aio.blockOn(nix::lookupFileArg(state.ctx, args.releaseExpr)), vTop);
|
||||
state.evalFile(
|
||||
state.aio.blockOn(nix::lookupFileArg(state.ctx, args.releaseExpr))
|
||||
.unwrap(),
|
||||
vTop);
|
||||
}
|
||||
|
||||
auto vRoot = state.ctx.mem.allocValue();
|
||||
|
||||
@@ -35,14 +35,16 @@ TEST(Arguments, lookupFileArg) {
|
||||
auto store = aio.blockOn(openStore("dummy://"));
|
||||
auto state = std::make_shared<Evaluator>(aio, searchPath, store, store);
|
||||
|
||||
SourcePath const foundUnitData = aio.blockOn(lookupFileArg(*state, "<example>"));
|
||||
SourcePath const foundUnitData =
|
||||
aio.blockOn(lookupFileArg(*state, "<example>")).unwrap(always_progresses);
|
||||
EXPECT_EQ(foundUnitData.canonical(), canonDataPath);
|
||||
|
||||
// lookupFileArg should not resolve <search paths> if anything else is before or after it.
|
||||
SourcePath const yepEvenSpaces = aio.blockOn(lookupFileArg(*state, " <example>"));
|
||||
SourcePath const yepEvenSpaces =
|
||||
aio.blockOn(lookupFileArg(*state, " <example>")).unwrap(always_progresses);
|
||||
EXPECT_EQ(yepEvenSpaces.canonical(), CanonPath::fromCwd(" <example>"));
|
||||
EXPECT_EQ(
|
||||
aio.blockOn(lookupFileArg(*state, "<example>/nixos")).canonical(),
|
||||
aio.blockOn(lookupFileArg(*state, "<example>/nixos")).unwrap(always_progresses).canonical(),
|
||||
CanonPath::fromCwd("<example>/nixos")
|
||||
);
|
||||
|
||||
@@ -53,7 +55,8 @@ TEST(Arguments, lookupFileArg) {
|
||||
EXPECT_NE(msg.find(CHANNEL_URL), msg.npos);
|
||||
}
|
||||
|
||||
SourcePath const normalFile = aio.blockOn(lookupFileArg(*state, unitDataPath));
|
||||
SourcePath const normalFile =
|
||||
aio.blockOn(lookupFileArg(*state, unitDataPath)).unwrap(always_progresses);
|
||||
EXPECT_EQ(normalFile.canonical(), canonDataPath);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user