aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/eval-cache.cc9
-rw-r--r--src/libexpr/eval.cc24
-rw-r--r--src/libexpr/eval.hh10
-rw-r--r--src/libexpr/fetchurl.nix8
-rw-r--r--src/libexpr/flake/config.cc2
-rw-r--r--src/libexpr/flake/flake.cc38
-rw-r--r--src/libexpr/flake/flakeref.hh2
-rw-r--r--src/libexpr/flake/lockfile.cc2
-rw-r--r--src/libexpr/parser.y6
-rw-r--r--src/libexpr/primops.cc42
-rw-r--r--src/libexpr/primops/fetchTree.cc4
-rw-r--r--src/libexpr/tests/primops.cc24
-rw-r--r--src/libexpr/value-to-json.cc21
-rw-r--r--src/libexpr/value-to-json.hh4
-rw-r--r--src/libexpr/value.hh2
15 files changed, 138 insertions, 60 deletions
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index d77b25898..b259eec63 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -282,7 +282,7 @@ struct AttrDb
auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second]));
if (!queryAttribute.next()) return {};
- auto rowId = (AttrType) queryAttribute.getInt(0);
+ auto rowId = (AttrId) queryAttribute.getInt(0);
auto type = (AttrType) queryAttribute.getInt(1);
switch (type) {
@@ -486,7 +486,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
return nullptr;
else if (std::get_if<failed_t>(&attr->second)) {
if (forceErrors)
- debug("reevaluating failed cached attribute '%s'");
+ debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name));
else
throw CachedEvalError("cached failure of attribute '%s'", getAttrPathStr(name));
} else
@@ -507,11 +507,6 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
return nullptr;
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
- for (auto & attr : *v.attrs) {
- if (root->db)
- root->db->setPlaceholder({cachedValue->first, attr.name});
- }
-
auto attr = v.attrs->get(name);
if (!attr) {
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 40462afdf..e3716f217 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -464,9 +464,10 @@ EvalState::EvalState(
, emptyBindings(0)
, store(store)
, buildStore(buildStore ? buildStore : store)
- , debugRepl(0)
+ , debugRepl(nullptr)
, debugStop(false)
, debugQuit(false)
+ , trylevel(0)
, regexCache(makeRegexCache())
#if HAVE_BOEHMGC
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
@@ -832,7 +833,14 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
: nullptr;
if (error)
- printError("%s\n\n" ANSI_BOLD "Starting REPL to allow you to inspect the current state of the evaluator.\n" ANSI_NORMAL, error->what());
+ {
+ printError("%s\n\n", error->what());
+
+ if (trylevel > 0 && error->info().level != lvlInfo)
+ printError("This exception occurred in a 'tryEval' call. Use " ANSI_GREEN "--ignore-try" ANSI_NORMAL " to skip these.\n");
+
+ printError(ANSI_BOLD "Starting REPL to allow you to inspect the current state of the evaluator.\n" ANSI_NORMAL);
+ }
auto se = getStaticEnv(expr);
if (se) {
@@ -2493,18 +2501,18 @@ void EvalState::printStats()
}
{
auto list = topObj.list("functions");
- for (auto & i : functionCalls) {
+ for (auto & [fun, count] : functionCalls) {
auto obj = list.object();
- if (i.first->name)
- obj.attr("name", (const std::string &) i.first->name);
+ if (fun->name)
+ obj.attr("name", (std::string_view) symbols[fun->name]);
else
obj.attr("name", nullptr);
- if (auto pos = positions[i.first->pos]) {
- obj.attr("file", (const std::string &) pos.file);
+ if (auto pos = positions[fun->pos]) {
+ obj.attr("file", (std::string_view) pos.file);
obj.attr("line", pos.line);
obj.attr("column", pos.column);
}
- obj.attr("count", i.second);
+ obj.attr("count", count);
}
}
{
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 4eaa3c9b0..f07f15d43 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -130,6 +130,7 @@ public:
void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
bool debugStop;
bool debugQuit;
+ int trylevel;
std::list<DebugTrace> debugTraces;
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
@@ -646,6 +647,15 @@ struct EvalSettings : Config
Setting<bool> useEvalCache{this, true, "eval-cache",
"Whether to use the flake evaluation cache."};
+
+ Setting<bool> ignoreExceptionsDuringTry{this, false, "ignore-try",
+ R"(
+ If set to true, ignore exceptions inside 'tryEval' calls when evaluating nix expressions in
+ debug mode (using the --debugger flag). By default the debugger will pause on all exceptions.
+ )"};
+
+ Setting<bool> traceVerbose{this, false, "trace-verbose",
+ "Whether `builtins.traceVerbose` should trace its first argument when evaluated."};
};
extern EvalSettings evalSettings;
diff --git a/src/libexpr/fetchurl.nix b/src/libexpr/fetchurl.nix
index 02531103b..9d1b61d7f 100644
--- a/src/libexpr/fetchurl.nix
+++ b/src/libexpr/fetchurl.nix
@@ -12,13 +12,13 @@
, executable ? false
, unpack ? false
, name ? baseNameOf (toString url)
+, impure ? false
}:
-derivation {
+derivation ({
builder = "builtin:fetchurl";
# New-style output content requirements.
- inherit outputHashAlgo outputHash;
outputHashMode = if unpack || executable then "recursive" else "flat";
inherit name url executable unpack;
@@ -38,4 +38,6 @@ derivation {
# To make "nix-prefetch-url" work.
urls = [ url ];
-}
+} // (if impure
+ then { __impure = true; }
+ else { inherit outputHashAlgo outputHash; }))
diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc
index 3e9d264b4..6df95f1f0 100644
--- a/src/libexpr/flake/config.cc
+++ b/src/libexpr/flake/config.cc
@@ -68,7 +68,7 @@ void ConfigFile::apply()
}
}
if (!trusted) {
- warn("ignoring untrusted flake configuration setting '%s'", name);
+ warn("ignoring untrusted flake configuration setting '%s'.\nPass '%s' to trust it", name, "--accept-flake-config");
continue;
}
}
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 35c841897..119c556ac 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -341,7 +341,6 @@ LockedFlake lockFlake(
debug("old lock file: %s", oldLockFile);
- // FIXME: check whether all overrides are used.
std::map<InputPath, FlakeInput> overrides;
std::set<InputPath> overridesUsed, updatesUsed;
@@ -384,6 +383,18 @@ LockedFlake lockFlake(
}
}
+ /* Check whether this input has overrides for a
+ non-existent input. */
+ for (auto [inputPath, inputOverride] : overrides) {
+ auto inputPath2(inputPath);
+ auto follow = inputPath2.back();
+ inputPath2.pop_back();
+ if (inputPath2 == inputPathPrefix && !flakeInputs.count(follow))
+ warn(
+ "input '%s' has an override for a non-existent input '%s'",
+ printInputPath(inputPathPrefix), follow);
+ }
+
/* Go over the flake inputs, resolve/fetch them if
necessary (i.e. if they're new or the flakeref changed
from what's in the lock file). */
@@ -472,12 +483,12 @@ LockedFlake lockFlake(
} else if (auto follows = std::get_if<1>(&i.second)) {
if (! trustLock) {
// It is possible that the flake has changed,
- // so we must confirm all the follows that are in the lockfile are also in the flake.
+ // so we must confirm all the follows that are in the lock file are also in the flake.
auto overridePath(inputPath);
overridePath.push_back(i.first);
auto o = overrides.find(overridePath);
// If the override disappeared, we have to refetch the flake,
- // since some of the inputs may not be present in the lockfile.
+ // since some of the inputs may not be present in the lock file.
if (o == overrides.end()) {
mustRefetch = true;
// There's no point populating the rest of the fake inputs,
@@ -513,6 +524,15 @@ LockedFlake lockFlake(
if (!lockFlags.allowMutable && !input.ref->input.isLocked())
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
+ /* Note: in case of an --override-input, we use
+ the *original* ref (input2.ref) for the
+ "original" field, rather than the
+ override. This ensures that the override isn't
+ nuked the next time we update the lock
+ file. That is, overrides are sticky unless you
+ use --no-write-lock-file. */
+ auto ref = input2.ref ? *input2.ref : *input.ref;
+
if (input.isFlake) {
Path localPath = parentPath;
FlakeRef localRef = *input.ref;
@@ -524,15 +544,7 @@ LockedFlake lockFlake(
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
- /* Note: in case of an --override-input, we use
- the *original* ref (input2.ref) for the
- "original" field, rather than the
- override. This ensures that the override isn't
- nuked the next time we update the lock
- file. That is, overrides are sticky unless you
- use --no-write-lock-file. */
- auto childNode = std::make_shared<LockedNode>(
- inputFlake.lockedRef, input2.ref ? *input2.ref : *input.ref);
+ auto childNode = std::make_shared<LockedNode>(inputFlake.lockedRef, ref);
node->inputs.insert_or_assign(id, childNode);
@@ -560,7 +572,7 @@ LockedFlake lockFlake(
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
state, *input.ref, useRegistries, flakeCache);
node->inputs.insert_or_assign(id,
- std::make_shared<LockedNode>(lockedRef, *input.ref, false));
+ std::make_shared<LockedNode>(lockedRef, ref, false));
}
}
diff --git a/src/libexpr/flake/flakeref.hh b/src/libexpr/flake/flakeref.hh
index a9182f4bf..fe4f67193 100644
--- a/src/libexpr/flake/flakeref.hh
+++ b/src/libexpr/flake/flakeref.hh
@@ -28,7 +28,7 @@ typedef std::string FlakeId;
* object that fetcher generates (usually via
* FlakeRef::fromAttrs(attrs) or parseFlakeRef(url) calls).
*
- * The actual fetch not have been performed yet (i.e. a FlakeRef may
+ * The actual fetch may not have been performed yet (i.e. a FlakeRef may
* be lazy), but the fetcher can be invoked at any time via the
* FlakeRef to ensure the store is populated with this input.
*/
diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc
index 60b52d578..629d2e669 100644
--- a/src/libexpr/flake/lockfile.cc
+++ b/src/libexpr/flake/lockfile.cc
@@ -36,7 +36,7 @@ LockedNode::LockedNode(const nlohmann::json & json)
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
{
if (!lockedRef.input.isLocked())
- throw Error("lockfile contains mutable lock '%s'",
+ throw Error("lock file contains mutable lock '%s'",
fetchers::attrsToJSON(lockedRef.input.toAttrs()));
}
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 8cbc2da4d..7c9b5a2db 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -520,6 +520,12 @@ path_start
$$ = new ExprPath(path);
}
| HPATH {
+ if (evalSettings.pureEval) {
+ throw Error(
+ "the path '%s' can not be resolved in pure mode",
+ std::string_view($1.p, $1.l)
+ );
+ }
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
$$ = new ExprPath(path);
}
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index eea274301..28b998474 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -851,6 +851,18 @@ static RegisterPrimOp primop_floor({
static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
auto attrs = state.buildBindings(2);
+
+ /* increment state.trylevel, and decrement it when this function returns. */
+ MaintainCount trylevel(state.trylevel);
+
+ void (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
+ if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry)
+ {
+ /* to prevent starting the repl from exceptions withing a tryEval, null it. */
+ savedDebugRepl = state.debugRepl;
+ state.debugRepl = nullptr;
+ }
+
try {
state.forceValue(*args[0], pos);
attrs.insert(state.sValue, args[0]);
@@ -859,6 +871,11 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va
attrs.alloc(state.sValue).mkBool(false);
attrs.alloc("success").mkBool(false);
}
+
+ // restore the debugRepl pointer if we saved it earlier.
+ if (savedDebugRepl)
+ state.debugRepl = savedDebugRepl;
+
v.mkAttrs(attrs);
}
@@ -970,6 +987,15 @@ static RegisterPrimOp primop_trace({
});
+/* Takes two arguments and evaluates to the second one. Used as the
+ * builtins.traceVerbose implementation when --trace-verbose is not enabled
+ */
+static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Value & v)
+{
+ state.forceValue(*args[1], pos);
+ v = *args[1];
+}
+
/*************************************************************
* Derivations
*************************************************************/
@@ -2428,8 +2454,8 @@ static RegisterPrimOp primop_intersectAttrs({
.name = "__intersectAttrs",
.args = {"e1", "e2"},
.doc = R"(
- Return a set consisting of the attributes in the set *e2* that also
- exist in the set *e1*.
+ Return a set consisting of the attributes in the set *e2* which have the
+ same name as some attribute in *e1*.
)",
.fun = prim_intersectAttrs,
});
@@ -3926,6 +3952,18 @@ void EvalState::createBaseEnv()
addPrimOp("__exec", 1, prim_exec);
}
+ addPrimOp({
+ .fun = evalSettings.traceVerbose ? prim_trace : prim_second,
+ .arity = 2,
+ .name = "__traceVerbose",
+ .args = { "e1", "e2" },
+ .doc = R"(
+ Evaluate *e1* and print its abstract syntax representation on standard
+ error if `--trace-verbose` is enabled. Then return *e2*. This function
+ is useful for debugging.
+ )",
+ });
+
/* Add a value containing the current Nix expression search path. */
mkList(v, searchPath.size());
int n = 0;
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index e5eeea520..84e7f5c02 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -364,6 +364,10 @@ static RegisterPrimOp primop_fetchGit({
A Boolean parameter that specifies whether submodules should be
checked out. Defaults to `false`.
+ - shallow\
+ A Boolean parameter that specifies whether fetching a shallow clone
+ is allowed. Defaults to `false`.
+
- allRefs\
Whether to fetch all refs of the repository. With this argument being
true, it's possible to load a `rev` from *any* `ref` (by default only
diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc
index f65b6593d..16cf66d2c 100644
--- a/src/libexpr/tests/primops.cc
+++ b/src/libexpr/tests/primops.cc
@@ -540,22 +540,22 @@ namespace nix {
ASSERT_THAT(v, IsStringEq(output));
}
-#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " #input), std::string_view(output)))
+#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " input), std::string_view(output)))
INSTANTIATE_TEST_SUITE_P(
toString,
ToStringPrimOpTest,
testing::Values(
- CASE("foo", "foo"),
- CASE(1, "1"),
- CASE([1 2 3], "1 2 3"),
- CASE(.123, "0.123000"),
- CASE(true, "1"),
- CASE(false, ""),
- CASE(null, ""),
- CASE({ v = "bar"; __toString = self: self.v; }, "bar"),
- CASE({ v = "bar"; __toString = self: self.v; outPath = "foo"; }, "bar"),
- CASE({ outPath = "foo"; }, "foo"),
- CASE(./test, "/test")
+ CASE(R"("foo")", "foo"),
+ CASE(R"(1)", "1"),
+ CASE(R"([1 2 3])", "1 2 3"),
+ CASE(R"(.123)", "0.123000"),
+ CASE(R"(true)", "1"),
+ CASE(R"(false)", ""),
+ CASE(R"(null)", ""),
+ CASE(R"({ v = "bar"; __toString = self: self.v; })", "bar"),
+ CASE(R"({ v = "bar"; __toString = self: self.v; outPath = "foo"; })", "bar"),
+ CASE(R"({ outPath = "foo"; })", "foo"),
+ CASE(R"(./test)", "/test")
)
);
#undef CASE
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index 03504db61..4d63d8b49 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -10,7 +10,7 @@
namespace nix {
void printValueAsJSON(EvalState & state, bool strict,
- Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context)
+ Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context, bool copyToStore)
{
checkInterrupt();
@@ -32,7 +32,10 @@ void printValueAsJSON(EvalState & state, bool strict,
break;
case nPath:
- out.write(state.copyPathToStore(context, v.path));
+ if (copyToStore)
+ out.write(state.copyPathToStore(context, v.path));
+ else
+ out.write(v.path);
break;
case nNull:
@@ -54,10 +57,10 @@ void printValueAsJSON(EvalState & state, bool strict,
for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j)));
auto placeholder(obj.placeholder(j));
- printValueAsJSON(state, strict, *a.value, a.pos, placeholder, context);
+ printValueAsJSON(state, strict, *a.value, a.pos, placeholder, context, copyToStore);
}
} else
- printValueAsJSON(state, strict, *i->value, i->pos, out, context);
+ printValueAsJSON(state, strict, *i->value, i->pos, out, context, copyToStore);
break;
}
@@ -65,13 +68,13 @@ void printValueAsJSON(EvalState & state, bool strict,
auto list(out.list());
for (auto elem : v.listItems()) {
auto placeholder(list.placeholder());
- printValueAsJSON(state, strict, *elem, pos, placeholder, context);
+ printValueAsJSON(state, strict, *elem, pos, placeholder, context, copyToStore);
}
break;
}
case nExternal:
- v.external->printValueAsJSON(state, strict, out, context);
+ v.external->printValueAsJSON(state, strict, out, context, copyToStore);
break;
case nFloat:
@@ -91,14 +94,14 @@ void printValueAsJSON(EvalState & state, bool strict,
}
void printValueAsJSON(EvalState & state, bool strict,
- Value & v, const PosIdx pos, std::ostream & str, PathSet & context)
+ Value & v, const PosIdx pos, std::ostream & str, PathSet & context, bool copyToStore)
{
JSONPlaceholder out(str);
- printValueAsJSON(state, strict, v, pos, out, context);
+ printValueAsJSON(state, strict, v, pos, out, context, copyToStore);
}
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
- JSONPlaceholder & out, PathSet & context) const
+ JSONPlaceholder & out, PathSet & context, bool copyToStore) const
{
state.debugThrowLastTrace(TypeError("cannot convert %1% to JSON", showType()));
}
diff --git a/src/libexpr/value-to-json.hh b/src/libexpr/value-to-json.hh
index c020a817a..7ddc8a5b1 100644
--- a/src/libexpr/value-to-json.hh
+++ b/src/libexpr/value-to-json.hh
@@ -11,9 +11,9 @@ namespace nix {
class JSONPlaceholder;
void printValueAsJSON(EvalState & state, bool strict,
- Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context);
+ Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context, bool copyToStore = true);
void printValueAsJSON(EvalState & state, bool strict,
- Value & v, const PosIdx pos, std::ostream & str, PathSet & context);
+ Value & v, const PosIdx pos, std::ostream & str, PathSet & context, bool copyToStore = true);
}
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 2008df74d..590ba7783 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -99,7 +99,7 @@ class ExternalValueBase
/* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */
virtual void printValueAsJSON(EvalState & state, bool strict,
- JSONPlaceholder & out, PathSet & context) const;
+ JSONPlaceholder & out, PathSet & context, bool copyToStore = true) const;
/* Print the value as XML. Defaults to unevaluated */
virtual void printValueAsXML(EvalState & state, bool strict, bool location,