mirror of
https://github.com/NixOS/nix.git
synced 2025-11-11 21:16:02 +01:00
Apply clang-format universally.
* It is tough to contribute to a project that doesn't use a formatter, * It is extra hard to contribute to a project which has configured the formatter, but ignores it for some files * Code formatting makes it harder to hide obscure / weird bugs by accident or on purpose, Let's rip the bandaid off? Note that PRs currently in flight should be able to be merged relatively easily by applying `clang-format` to their tip prior to merge.
This commit is contained in:
parent
41bf87ec70
commit
e4f62e4608
587 changed files with 23258 additions and 23135 deletions
|
|
@ -1,10 +1,8 @@
|
|||
#include "nix/expr/attr-path.hh"
|
||||
#include "nix/expr/eval-inline.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static Strings parseAttrPath(std::string_view s)
|
||||
{
|
||||
Strings res;
|
||||
|
|
@ -19,18 +17,19 @@ static Strings parseAttrPath(std::string_view s)
|
|||
while (1) {
|
||||
if (i == s.end())
|
||||
throw ParseError("missing closing quote in selection path '%1%'", s);
|
||||
if (*i == '"') break;
|
||||
if (*i == '"')
|
||||
break;
|
||||
cur.push_back(*i++);
|
||||
}
|
||||
} else
|
||||
cur.push_back(*i);
|
||||
++i;
|
||||
}
|
||||
if (!cur.empty()) res.push_back(cur);
|
||||
if (!cur.empty())
|
||||
res.push_back(cur);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
|
||||
{
|
||||
std::vector<Symbol> res;
|
||||
|
|
@ -39,9 +38,8 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath,
|
||||
Bindings & autoArgs, Value & vIn)
|
||||
std::pair<Value *, PosIdx>
|
||||
findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn)
|
||||
{
|
||||
Strings tokens = parseAttrPath(attrPath);
|
||||
|
||||
|
|
@ -65,10 +63,12 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
|||
if (!attrIndex) {
|
||||
|
||||
if (v->type() != nAttrs)
|
||||
state.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||
attrPath,
|
||||
showType(*v)).debugThrow();
|
||||
state
|
||||
.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||
attrPath,
|
||||
showType(*v))
|
||||
.debugThrow();
|
||||
if (attr.empty())
|
||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||
|
||||
|
|
@ -79,7 +79,8 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
|||
attrNames.insert(std::string(state.symbols[attr.name]));
|
||||
|
||||
auto suggestions = Suggestions::bestMatches(attrNames, attr);
|
||||
throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
|
||||
throw AttrPathNotFound(
|
||||
suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
|
||||
}
|
||||
v = &*a->value;
|
||||
pos = a->pos;
|
||||
|
|
@ -88,23 +89,23 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
|||
else {
|
||||
|
||||
if (!v->isList())
|
||||
state.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a list but is %2%",
|
||||
attrPath,
|
||||
showType(*v)).debugThrow();
|
||||
state
|
||||
.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a list but is %2%",
|
||||
attrPath,
|
||||
showType(*v))
|
||||
.debugThrow();
|
||||
if (*attrIndex >= v->listSize())
|
||||
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath);
|
||||
|
||||
v = v->listView()[*attrIndex];
|
||||
pos = noPos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {v, pos};
|
||||
}
|
||||
|
||||
|
||||
std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||
{
|
||||
Value * v2;
|
||||
|
|
@ -118,17 +119,17 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
|||
// FIXME: is it possible to extract the Pos object instead of doing this
|
||||
// toString + parsing?
|
||||
NixStringContext context;
|
||||
auto path = state.coerceToPath(noPos, *v2, context, "while evaluating the 'meta.position' attribute of a derivation");
|
||||
auto path =
|
||||
state.coerceToPath(noPos, *v2, context, "while evaluating the 'meta.position' attribute of a derivation");
|
||||
|
||||
auto fn = path.path.abs();
|
||||
|
||||
auto fail = [fn]() {
|
||||
throw ParseError("cannot parse 'meta.position' attribute '%s'", fn);
|
||||
};
|
||||
auto fail = [fn]() { throw ParseError("cannot parse 'meta.position' attribute '%s'", fn); };
|
||||
|
||||
try {
|
||||
auto colon = fn.rfind(':');
|
||||
if (colon == std::string::npos) fail();
|
||||
if (colon == std::string::npos)
|
||||
fail();
|
||||
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
|
||||
return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno};
|
||||
} catch (std::invalid_argument & e) {
|
||||
|
|
@ -137,5 +138,4 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -3,11 +3,8 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
||||
/* Allocate a new array of attributes for an attribute set with a specific
|
||||
capacity. The space is implicitly reserved after the Bindings
|
||||
structure. */
|
||||
|
|
@ -22,7 +19,6 @@ Bindings * EvalState::allocBindings(size_t capacity)
|
|||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
|
||||
}
|
||||
|
||||
|
||||
Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
|
||||
{
|
||||
auto value = state.allocValue();
|
||||
|
|
@ -30,24 +26,21 @@ Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
|
|||
return *value;
|
||||
}
|
||||
|
||||
|
||||
Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos)
|
||||
{
|
||||
return alloc(state.symbols.create(name), pos);
|
||||
}
|
||||
|
||||
|
||||
void Bindings::sort()
|
||||
{
|
||||
if (size_) std::sort(begin(), end());
|
||||
if (size_)
|
||||
std::sort(begin(), end());
|
||||
}
|
||||
|
||||
|
||||
Value & Value::mkAttrs(BindingsBuilder & bindings)
|
||||
{
|
||||
mkAttrs(bindings.finish());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ namespace nix::eval_cache {
|
|||
|
||||
CachedEvalError::CachedEvalError(ref<AttrCursor> cursor, Symbol attr)
|
||||
: EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
|
||||
, cursor(cursor), attr(attr)
|
||||
{ }
|
||||
, cursor(cursor)
|
||||
, attr(attr)
|
||||
{
|
||||
}
|
||||
|
||||
void CachedEvalError::force()
|
||||
{
|
||||
|
|
@ -25,7 +27,8 @@ void CachedEvalError::force()
|
|||
}
|
||||
|
||||
// Shouldn't happen.
|
||||
throw EvalError(state, "evaluation of cached failed attribute '%s' unexpectedly succeeded", cursor->getAttrPathStr(attr));
|
||||
throw EvalError(
|
||||
state, "evaluation of cached failed attribute '%s' unexpectedly succeeded", cursor->getAttrPathStr(attr));
|
||||
}
|
||||
|
||||
static const char * schema = R"sql(
|
||||
|
|
@ -59,10 +62,7 @@ struct AttrDb
|
|||
|
||||
SymbolTable & symbols;
|
||||
|
||||
AttrDb(
|
||||
const StoreDirConfig & cfg,
|
||||
const Hash & fingerprint,
|
||||
SymbolTable & symbols)
|
||||
AttrDb(const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
|
||||
: cfg(cfg)
|
||||
, _state(std::make_unique<Sync<State>>())
|
||||
, symbols(symbols)
|
||||
|
|
@ -78,17 +78,16 @@ struct AttrDb
|
|||
state->db.isCache();
|
||||
state->db.exec(schema);
|
||||
|
||||
state->insertAttribute.create(state->db,
|
||||
"insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)");
|
||||
state->insertAttribute.create(
|
||||
state->db, "insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)");
|
||||
|
||||
state->insertAttributeWithContext.create(state->db,
|
||||
"insert or replace into Attributes(parent, name, type, value, context) values (?, ?, ?, ?, ?)");
|
||||
state->insertAttributeWithContext.create(
|
||||
state->db, "insert or replace into Attributes(parent, name, type, value, context) values (?, ?, ?, ?, ?)");
|
||||
|
||||
state->queryAttribute.create(state->db,
|
||||
"select rowid, type, value, context from Attributes where parent = ? and name = ?");
|
||||
state->queryAttribute.create(
|
||||
state->db, "select rowid, type, value, context from Attributes where parent = ? and name = ?");
|
||||
|
||||
state->queryAttributes.create(state->db,
|
||||
"select name from Attributes where parent = ?");
|
||||
state->queryAttributes.create(state->db, "select name from Attributes where parent = ?");
|
||||
|
||||
state->txn = std::make_unique<SQLiteTxn>(state->db);
|
||||
}
|
||||
|
|
@ -108,7 +107,8 @@ struct AttrDb
|
|||
template<typename F>
|
||||
AttrId doSQLite(F && fun)
|
||||
{
|
||||
if (failed) return 0;
|
||||
if (failed)
|
||||
return 0;
|
||||
try {
|
||||
return fun();
|
||||
} catch (SQLiteError &) {
|
||||
|
|
@ -118,116 +118,76 @@ struct AttrDb
|
|||
}
|
||||
}
|
||||
|
||||
AttrId setAttrs(
|
||||
AttrKey key,
|
||||
const std::vector<Symbol> & attrs)
|
||||
AttrId setAttrs(AttrKey key, const std::vector<Symbol> & attrs)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::FullAttrs)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::FullAttrs) (0, false).exec();
|
||||
|
||||
AttrId rowId = state->db.getLastInsertedRowId();
|
||||
assert(rowId);
|
||||
|
||||
for (auto & attr : attrs)
|
||||
state->insertAttribute.use()
|
||||
(rowId)
|
||||
(symbols[attr])
|
||||
(AttrType::Placeholder)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(rowId)(symbols[attr])(AttrType::Placeholder) (0, false).exec();
|
||||
|
||||
return rowId;
|
||||
});
|
||||
}
|
||||
|
||||
AttrId setString(
|
||||
AttrKey key,
|
||||
std::string_view s,
|
||||
const char * * context = nullptr)
|
||||
AttrId setString(AttrKey key, std::string_view s, const char ** context = nullptr)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
if (context) {
|
||||
std::string ctx;
|
||||
for (const char * * p = context; *p; ++p) {
|
||||
if (p != context) ctx.push_back(' ');
|
||||
for (const char ** p = context; *p; ++p) {
|
||||
if (p != context)
|
||||
ctx.push_back(' ');
|
||||
ctx.append(*p);
|
||||
}
|
||||
state->insertAttributeWithContext.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::String)
|
||||
(s)
|
||||
(ctx).exec();
|
||||
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
|
||||
.exec();
|
||||
} else {
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::String)
|
||||
(s).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::String) (s).exec();
|
||||
}
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
}
|
||||
|
||||
AttrId setBool(
|
||||
AttrKey key,
|
||||
bool b)
|
||||
AttrId setBool(AttrKey key, bool b)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Bool)
|
||||
(b ? 1 : 0).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Bool) (b ? 1 : 0).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
}
|
||||
|
||||
AttrId setInt(
|
||||
AttrKey key,
|
||||
int n)
|
||||
AttrId setInt(AttrKey key, int n)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Int)
|
||||
(n).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Int) (n).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
}
|
||||
|
||||
AttrId setListOfStrings(
|
||||
AttrKey key,
|
||||
const std::vector<std::string> & l)
|
||||
AttrId setListOfStrings(AttrKey key, const std::vector<std::string> & l)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::ListOfStrings)
|
||||
(dropEmptyInitThenConcatStringsSep("\t", l)).exec();
|
||||
state->insertAttribute
|
||||
.use()(key.first)(symbols[key.second])(
|
||||
AttrType::ListOfStrings) (dropEmptyInitThenConcatStringsSep("\t", l))
|
||||
.exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -235,15 +195,10 @@ struct AttrDb
|
|||
|
||||
AttrId setPlaceholder(AttrKey key)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Placeholder)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Placeholder) (0, false).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -251,15 +206,10 @@ struct AttrDb
|
|||
|
||||
AttrId setMissing(AttrKey key)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Missing)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Missing) (0, false).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -267,15 +217,10 @@ struct AttrDb
|
|||
|
||||
AttrId setMisc(AttrKey key)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Misc)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Misc) (0, false).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -283,15 +228,10 @@ struct AttrDb
|
|||
|
||||
AttrId setFailed(AttrKey key)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Failed)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Failed) (0, false).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -302,51 +242,49 @@ struct AttrDb
|
|||
auto state(_state->lock());
|
||||
|
||||
auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second]));
|
||||
if (!queryAttribute.next()) return {};
|
||||
if (!queryAttribute.next())
|
||||
return {};
|
||||
|
||||
auto rowId = (AttrId) queryAttribute.getInt(0);
|
||||
auto type = (AttrType) queryAttribute.getInt(1);
|
||||
|
||||
switch (type) {
|
||||
case AttrType::Placeholder:
|
||||
return {{rowId, placeholder_t()}};
|
||||
case AttrType::FullAttrs: {
|
||||
// FIXME: expensive, should separate this out.
|
||||
std::vector<Symbol> attrs;
|
||||
auto queryAttributes(state->queryAttributes.use()(rowId));
|
||||
while (queryAttributes.next())
|
||||
attrs.emplace_back(symbols.create(queryAttributes.getStr(0)));
|
||||
return {{rowId, attrs}};
|
||||
}
|
||||
case AttrType::String: {
|
||||
NixStringContext context;
|
||||
if (!queryAttribute.isNull(3))
|
||||
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
|
||||
context.insert(NixStringContextElem::parse(s));
|
||||
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
|
||||
}
|
||||
case AttrType::Bool:
|
||||
return {{rowId, queryAttribute.getInt(2) != 0}};
|
||||
case AttrType::Int:
|
||||
return {{rowId, int_t{NixInt{queryAttribute.getInt(2)}}}};
|
||||
case AttrType::ListOfStrings:
|
||||
return {{rowId, tokenizeString<std::vector<std::string>>(queryAttribute.getStr(2), "\t")}};
|
||||
case AttrType::Missing:
|
||||
return {{rowId, missing_t()}};
|
||||
case AttrType::Misc:
|
||||
return {{rowId, misc_t()}};
|
||||
case AttrType::Failed:
|
||||
return {{rowId, failed_t()}};
|
||||
default:
|
||||
throw Error("unexpected type in evaluation cache");
|
||||
case AttrType::Placeholder:
|
||||
return {{rowId, placeholder_t()}};
|
||||
case AttrType::FullAttrs: {
|
||||
// FIXME: expensive, should separate this out.
|
||||
std::vector<Symbol> attrs;
|
||||
auto queryAttributes(state->queryAttributes.use()(rowId));
|
||||
while (queryAttributes.next())
|
||||
attrs.emplace_back(symbols.create(queryAttributes.getStr(0)));
|
||||
return {{rowId, attrs}};
|
||||
}
|
||||
case AttrType::String: {
|
||||
NixStringContext context;
|
||||
if (!queryAttribute.isNull(3))
|
||||
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
|
||||
context.insert(NixStringContextElem::parse(s));
|
||||
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
|
||||
}
|
||||
case AttrType::Bool:
|
||||
return {{rowId, queryAttribute.getInt(2) != 0}};
|
||||
case AttrType::Int:
|
||||
return {{rowId, int_t{NixInt{queryAttribute.getInt(2)}}}};
|
||||
case AttrType::ListOfStrings:
|
||||
return {{rowId, tokenizeString<std::vector<std::string>>(queryAttribute.getStr(2), "\t")}};
|
||||
case AttrType::Missing:
|
||||
return {{rowId, missing_t()}};
|
||||
case AttrType::Misc:
|
||||
return {{rowId, misc_t()}};
|
||||
case AttrType::Failed:
|
||||
return {{rowId, failed_t()}};
|
||||
default:
|
||||
throw Error("unexpected type in evaluation cache");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static std::shared_ptr<AttrDb> makeAttrDb(
|
||||
const StoreDirConfig & cfg,
|
||||
const Hash & fingerprint,
|
||||
SymbolTable & symbols)
|
||||
static std::shared_ptr<AttrDb> makeAttrDb(const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
|
||||
{
|
||||
try {
|
||||
return std::make_shared<AttrDb>(cfg, fingerprint, symbols);
|
||||
|
|
@ -357,9 +295,7 @@ static std::shared_ptr<AttrDb> makeAttrDb(
|
|||
}
|
||||
|
||||
EvalCache::EvalCache(
|
||||
std::optional<std::reference_wrapper<const Hash>> useCache,
|
||||
EvalState & state,
|
||||
RootLoader rootLoader)
|
||||
std::optional<std::reference_wrapper<const Hash>> useCache, EvalState & state, RootLoader rootLoader)
|
||||
: db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr)
|
||||
, state(state)
|
||||
, rootLoader(rootLoader)
|
||||
|
|
@ -381,11 +317,10 @@ ref<AttrCursor> EvalCache::getRoot()
|
|||
}
|
||||
|
||||
AttrCursor::AttrCursor(
|
||||
ref<EvalCache> root,
|
||||
Parent parent,
|
||||
Value * value,
|
||||
std::optional<std::pair<AttrId, AttrValue>> && cachedValue)
|
||||
: root(root), parent(parent), cachedValue(std::move(cachedValue))
|
||||
ref<EvalCache> root, Parent parent, Value * value, std::optional<std::pair<AttrId, AttrValue>> && cachedValue)
|
||||
: root(root)
|
||||
, parent(parent)
|
||||
, cachedValue(std::move(cachedValue))
|
||||
{
|
||||
if (value)
|
||||
_value = allocRootValue(value);
|
||||
|
|
@ -470,13 +405,11 @@ Value & AttrCursor::forceValue()
|
|||
|
||||
if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) {
|
||||
if (v.type() == nString)
|
||||
cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()),
|
||||
string_t{v.c_str(), {}}};
|
||||
cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()), string_t{v.c_str(), {}}};
|
||||
else if (v.type() == nPath) {
|
||||
auto path = v.path().path;
|
||||
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
|
||||
}
|
||||
else if (v.type() == nBool)
|
||||
} else if (v.type() == nBool)
|
||||
cachedValue = {root->db->setBool(getKey(), v.boolean()), v.boolean()};
|
||||
else if (v.type() == nInt)
|
||||
cachedValue = {root->db->setInt(getKey(), v.integer().value), int_t{v.integer()}};
|
||||
|
|
@ -518,14 +451,14 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
|
|||
else if (std::get_if<failed_t>(&attr->second))
|
||||
throw CachedEvalError(ref(shared_from_this()), name);
|
||||
else
|
||||
return std::make_shared<AttrCursor>(root,
|
||||
std::make_pair(ref(shared_from_this()), name), nullptr, std::move(attr));
|
||||
return std::make_shared<AttrCursor>(
|
||||
root, std::make_pair(ref(shared_from_this()), name), nullptr, std::move(attr));
|
||||
}
|
||||
// Incomplete attrset, so need to fall thru and
|
||||
// evaluate to see whether 'name' exists
|
||||
} else
|
||||
return nullptr;
|
||||
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
// error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -533,7 +466,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
|
|||
|
||||
if (v.type() != nAttrs)
|
||||
return nullptr;
|
||||
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
// error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
|
||||
auto attr = v.attrs()->get(name);
|
||||
|
||||
|
|
@ -618,17 +551,15 @@ string_t AttrCursor::getStringWithContext()
|
|||
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
|
||||
bool valid = true;
|
||||
for (auto & c : s->second) {
|
||||
const StorePath & path = std::visit(overloaded {
|
||||
[&](const NixStringContextElem::DrvDeep & d) -> const StorePath & {
|
||||
return d.drvPath;
|
||||
const StorePath & path = std::visit(
|
||||
overloaded{
|
||||
[&](const NixStringContextElem::DrvDeep & d) -> const StorePath & { return d.drvPath; },
|
||||
[&](const NixStringContextElem::Built & b) -> const StorePath & {
|
||||
return b.drvPath->getBaseStorePath();
|
||||
},
|
||||
[&](const NixStringContextElem::Opaque & o) -> const StorePath & { return o.path; },
|
||||
},
|
||||
[&](const NixStringContextElem::Built & b) -> const StorePath & {
|
||||
return b.drvPath->getBaseStorePath();
|
||||
},
|
||||
[&](const NixStringContextElem::Opaque & o) -> const StorePath & {
|
||||
return o.path;
|
||||
},
|
||||
}, c.raw);
|
||||
c.raw);
|
||||
if (!root->state.store->isValidPath(path)) {
|
||||
valid = false;
|
||||
break;
|
||||
|
|
@ -649,8 +580,7 @@ string_t AttrCursor::getStringWithContext()
|
|||
NixStringContext context;
|
||||
copyContext(v, context);
|
||||
return {v.c_str(), std::move(context)};
|
||||
}
|
||||
else if (v.type() == nPath)
|
||||
} else if (v.type() == nPath)
|
||||
return {v.path().to_string(), {}};
|
||||
else
|
||||
root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr(), showType(v)).debugThrow();
|
||||
|
|
@ -722,7 +652,8 @@ std::vector<std::string> AttrCursor::getListOfStrings()
|
|||
std::vector<std::string> res;
|
||||
|
||||
for (auto elem : v.listView())
|
||||
res.push_back(std::string(root->state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
|
||||
res.push_back(
|
||||
std::string(root->state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
|
||||
|
||||
if (root->db)
|
||||
cachedValue = {root->db->setListOfStrings(getKey(), res), res};
|
||||
|
|
@ -778,10 +709,10 @@ StorePath AttrCursor::forceDerivation()
|
|||
been garbage-collected. So force it to be regenerated. */
|
||||
aDrvPath->forceValue();
|
||||
if (!root->state.store->isValidPath(drvPath))
|
||||
throw Error("don't know how to recreate store derivation '%s'!",
|
||||
root->state.store->printStorePath(drvPath));
|
||||
throw Error(
|
||||
"don't know how to recreate store derivation '%s'!", root->state.store->printStorePath(drvPath));
|
||||
}
|
||||
return drvPath;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix::eval_cache
|
||||
|
|
|
|||
|
|
@ -44,12 +44,13 @@ EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrame(const Env & env, const Expr
|
|||
// NOTE: This is abusing side-effects.
|
||||
// TODO: check compatibility with nested debugger calls.
|
||||
// TODO: What side-effects??
|
||||
error.state.debugTraces.push_front(DebugTrace{
|
||||
.pos = expr.getPos(),
|
||||
.expr = expr,
|
||||
.env = env,
|
||||
.hint = HintFmt("Fake frame for debugging purposes"),
|
||||
.isError = true});
|
||||
error.state.debugTraces.push_front(
|
||||
DebugTrace{
|
||||
.pos = expr.getPos(),
|
||||
.expr = expr,
|
||||
.env = env,
|
||||
.hint = HintFmt("Fake frame for debugging purposes"),
|
||||
.isError = true});
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +97,8 @@ template<class T>
|
|||
void EvalErrorBuilder<T>::panic()
|
||||
{
|
||||
logError(error.info());
|
||||
printError("This is a bug! An unexpected condition occurred, causing the Nix evaluator to have to stop. If you could share a reproducible example or a core dump, please open an issue at https://github.com/NixOS/nix/issues");
|
||||
printError(
|
||||
"This is a bug! An unexpected condition occurred, causing the Nix evaluator to have to stop. If you could share a reproducible example or a core dump, please open an issue at https://github.com/NixOS/nix/issues");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
|
@ -112,4 +114,4 @@ template class EvalErrorBuilder<InfiniteRecursionError>;
|
|||
template class EvalErrorBuilder<InvalidPathError>;
|
||||
template class EvalErrorBuilder<IFDError>;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -46,4 +46,4 @@ NLOHMANN_JSON_SERIALIZE_ENUM(
|
|||
/* Explicit instantiation of templates */
|
||||
template class BaseSetting<EvalProfilerMode>;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -352,4 +352,4 @@ ref<EvalProfiler> makeSampleStackProfiler(EvalState & state, std::filesystem::pa
|
|||
return make_ref<SampleStack>(state, profileFile, period);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -19,12 +19,14 @@ Strings EvalSettings::parseNixPath(const std::string & s)
|
|||
auto start2 = p;
|
||||
|
||||
while (p != s.end() && *p != ':') {
|
||||
if (*p == '=') start2 = p + 1;
|
||||
if (*p == '=')
|
||||
start2 = p + 1;
|
||||
++p;
|
||||
}
|
||||
|
||||
if (p == s.end()) {
|
||||
if (p != start) res.push_back(std::string(start, p));
|
||||
if (p != start)
|
||||
res.push_back(std::string(start, p));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -32,10 +34,12 @@ Strings EvalSettings::parseNixPath(const std::string & s)
|
|||
auto prefix = std::string(start2, s.end());
|
||||
if (EvalSettings::isPseudoUrl(prefix) || hasPrefix(prefix, "flake:")) {
|
||||
++p;
|
||||
while (p != s.end() && *p != ':') ++p;
|
||||
while (p != s.end() && *p != ':')
|
||||
++p;
|
||||
}
|
||||
res.push_back(std::string(start, p));
|
||||
if (p == s.end()) break;
|
||||
if (p == s.end())
|
||||
break;
|
||||
}
|
||||
|
||||
++p;
|
||||
|
|
@ -75,11 +79,14 @@ Strings EvalSettings::getDefaultNixPath()
|
|||
|
||||
bool EvalSettings::isPseudoUrl(std::string_view s)
|
||||
{
|
||||
if (s.compare(0, 8, "channel:") == 0) return true;
|
||||
if (s.compare(0, 8, "channel:") == 0)
|
||||
return true;
|
||||
size_t pos = s.find("://");
|
||||
if (pos == std::string::npos) return false;
|
||||
if (pos == std::string::npos)
|
||||
return false;
|
||||
std::string scheme(s, 0, pos);
|
||||
return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git" || scheme == "s3" || scheme == "ssh";
|
||||
return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git"
|
||||
|| scheme == "s3" || scheme == "ssh";
|
||||
}
|
||||
|
||||
std::string EvalSettings::resolvePseudoUrl(std::string_view url)
|
||||
|
|
@ -98,9 +105,7 @@ const std::string & EvalSettings::getCurrentSystem() const
|
|||
|
||||
Path getNixDefExpr()
|
||||
{
|
||||
return settings.useXDGBaseDirectories
|
||||
? getStateDir() + "/defexpr"
|
||||
: getHome() + "/.nix-defexpr";
|
||||
return settings.useXDGBaseDirectories ? getStateDir() + "/defexpr" : getHome() + "/.nix-defexpr";
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
1169
src/libexpr/eval.cc
1169
src/libexpr/eval.cc
File diff suppressed because it is too large
Load diff
|
|
@ -19,4 +19,4 @@ void FunctionCallTrace::postFunctionCallHook(
|
|||
printMsg(lvlInfo, "function-trace exited %1% at %2%", state.positions[pos], ns.count());
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -7,18 +7,19 @@
|
|||
#include <cstring>
|
||||
#include <regex>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
PackageInfo::PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs)
|
||||
: state(&state), attrs(attrs), attrPath(std::move(attrPath))
|
||||
: state(&state)
|
||||
, attrs(attrs)
|
||||
, attrPath(std::move(attrPath))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PackageInfo::PackageInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs)
|
||||
: state(&state), attrs(nullptr), attrPath("")
|
||||
: state(&state)
|
||||
, attrs(nullptr)
|
||||
, attrPath("")
|
||||
{
|
||||
auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs);
|
||||
|
||||
|
|
@ -31,10 +32,7 @@ PackageInfo::PackageInfo(EvalState & state, ref<Store> store, const std::string
|
|||
if (selectedOutputs.size() > 1)
|
||||
throw Error("building more than one derivation output is not supported, in '%s'", drvPathWithOutputs);
|
||||
|
||||
outputName =
|
||||
selectedOutputs.empty()
|
||||
? getOr(drv.env, "outputName", "out")
|
||||
: *selectedOutputs.begin();
|
||||
outputName = selectedOutputs.empty() ? getOr(drv.env, "outputName", "out") : *selectedOutputs.begin();
|
||||
|
||||
auto i = drv.outputs.find(outputName);
|
||||
if (i == drv.outputs.end())
|
||||
|
|
@ -44,34 +42,36 @@ PackageInfo::PackageInfo(EvalState & state, ref<Store> store, const std::string
|
|||
outPath = {output.path(*store, drv.name, outputName)};
|
||||
}
|
||||
|
||||
|
||||
std::string PackageInfo::queryName() const
|
||||
{
|
||||
if (name == "" && attrs) {
|
||||
auto i = attrs->find(state->sName);
|
||||
if (i == attrs->end()) state->error<TypeError>("derivation name missing").debugThrow();
|
||||
if (i == attrs->end())
|
||||
state->error<TypeError>("derivation name missing").debugThrow();
|
||||
name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
std::string PackageInfo::querySystem() const
|
||||
{
|
||||
if (system == "" && attrs) {
|
||||
auto i = attrs->find(state->sSystem);
|
||||
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
|
||||
system =
|
||||
i == attrs->end()
|
||||
? "unknown"
|
||||
: state->forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
|
||||
}
|
||||
return system;
|
||||
}
|
||||
|
||||
|
||||
std::optional<StorePath> PackageInfo::queryDrvPath() const
|
||||
{
|
||||
if (!drvPath && attrs) {
|
||||
if (auto i = attrs->get(state->sDrvPath)) {
|
||||
NixStringContext context;
|
||||
auto found = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation");
|
||||
auto found = state->coerceToStorePath(
|
||||
i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation");
|
||||
try {
|
||||
found.requireDerivation();
|
||||
} catch (Error & e) {
|
||||
|
|
@ -85,7 +85,6 @@ std::optional<StorePath> PackageInfo::queryDrvPath() const
|
|||
return drvPath.value_or(std::nullopt);
|
||||
}
|
||||
|
||||
|
||||
StorePath PackageInfo::requireDrvPath() const
|
||||
{
|
||||
if (auto drvPath = queryDrvPath())
|
||||
|
|
@ -93,21 +92,20 @@ StorePath PackageInfo::requireDrvPath() const
|
|||
throw Error("derivation does not contain a 'drvPath' attribute");
|
||||
}
|
||||
|
||||
|
||||
StorePath PackageInfo::queryOutPath() const
|
||||
{
|
||||
if (!outPath && attrs) {
|
||||
auto i = attrs->find(state->sOutPath);
|
||||
NixStringContext context;
|
||||
if (i != attrs->end())
|
||||
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
|
||||
outPath = state->coerceToStorePath(
|
||||
i->pos, *i->value, context, "while evaluating the output path of a derivation");
|
||||
}
|
||||
if (!outPath)
|
||||
throw UnimplementedError("CA derivations are not yet supported");
|
||||
return *outPath;
|
||||
}
|
||||
|
||||
|
||||
PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall)
|
||||
{
|
||||
if (outputs.empty()) {
|
||||
|
|
@ -118,19 +116,25 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
|||
|
||||
/* For each output... */
|
||||
for (auto elem : i->value->listView()) {
|
||||
std::string output(state->forceStringNoCtx(*elem, i->pos, "while evaluating the name of an output of a derivation"));
|
||||
std::string output(
|
||||
state->forceStringNoCtx(*elem, i->pos, "while evaluating the name of an output of a derivation"));
|
||||
|
||||
if (withPaths) {
|
||||
/* Evaluate the corresponding set. */
|
||||
auto out = attrs->get(state->symbols.create(output));
|
||||
if (!out) continue; // FIXME: throw error?
|
||||
if (!out)
|
||||
continue; // FIXME: throw error?
|
||||
state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation");
|
||||
|
||||
/* And evaluate its ‘outPath’ attribute. */
|
||||
auto outPath = out->value->attrs()->get(state->sOutPath);
|
||||
if (!outPath) continue; // FIXME: throw error?
|
||||
if (!outPath)
|
||||
continue; // FIXME: throw error?
|
||||
NixStringContext context;
|
||||
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
|
||||
outputs.emplace(
|
||||
output,
|
||||
state->coerceToStorePath(
|
||||
outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
|
||||
} else
|
||||
outputs.emplace(output, std::nullopt);
|
||||
}
|
||||
|
|
@ -142,7 +146,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
|||
return outputs;
|
||||
|
||||
const Attr * i;
|
||||
if (attrs && (i = attrs->get(state->sOutputSpecified)) && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
|
||||
if (attrs && (i = attrs->get(state->sOutputSpecified))
|
||||
&& state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
|
||||
Outputs result;
|
||||
auto out = outputs.find(queryOutputName());
|
||||
if (out == outputs.end())
|
||||
|
|
@ -154,95 +159,103 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
|||
else {
|
||||
/* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
|
||||
const Value * outTI = queryMeta("outputsToInstall");
|
||||
if (!outTI) return outputs;
|
||||
if (!outTI)
|
||||
return outputs;
|
||||
auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
if (!outTI->isList()) throw errMsg;
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
if (!outTI->isList())
|
||||
throw errMsg;
|
||||
Outputs result;
|
||||
for (auto elem : outTI->listView()) {
|
||||
if (elem->type() != nString) throw errMsg;
|
||||
if (elem->type() != nString)
|
||||
throw errMsg;
|
||||
auto out = outputs.find(elem->c_str());
|
||||
if (out == outputs.end()) throw errMsg;
|
||||
if (out == outputs.end())
|
||||
throw errMsg;
|
||||
result.insert(*out);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string PackageInfo::queryOutputName() const
|
||||
{
|
||||
if (outputName == "" && attrs) {
|
||||
auto i = attrs->get(state->sOutputName);
|
||||
outputName = i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
|
||||
outputName =
|
||||
i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
|
||||
}
|
||||
return outputName;
|
||||
}
|
||||
|
||||
|
||||
const Bindings * PackageInfo::getMeta()
|
||||
{
|
||||
if (meta) return meta;
|
||||
if (!attrs) return 0;
|
||||
if (meta)
|
||||
return meta;
|
||||
if (!attrs)
|
||||
return 0;
|
||||
auto a = attrs->get(state->sMeta);
|
||||
if (!a) return 0;
|
||||
if (!a)
|
||||
return 0;
|
||||
state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
|
||||
meta = a->value->attrs();
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
||||
StringSet PackageInfo::queryMetaNames()
|
||||
{
|
||||
StringSet res;
|
||||
if (!getMeta()) return res;
|
||||
if (!getMeta())
|
||||
return res;
|
||||
for (auto & i : *meta)
|
||||
res.emplace(state->symbols[i.name]);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool PackageInfo::checkMeta(Value & v)
|
||||
{
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
if (v.type() == nList) {
|
||||
for (auto elem : v.listView())
|
||||
if (!checkMeta(*elem)) return false;
|
||||
if (!checkMeta(*elem))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else if (v.type() == nAttrs) {
|
||||
if (v.attrs()->get(state->sOutPath)) return false;
|
||||
} else if (v.type() == nAttrs) {
|
||||
if (v.attrs()->get(state->sOutPath))
|
||||
return false;
|
||||
for (auto & i : *v.attrs())
|
||||
if (!checkMeta(*i.value)) return false;
|
||||
if (!checkMeta(*i.value))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else return v.type() == nInt || v.type() == nBool || v.type() == nString ||
|
||||
v.type() == nFloat;
|
||||
} else
|
||||
return v.type() == nInt || v.type() == nBool || v.type() == nString || v.type() == nFloat;
|
||||
}
|
||||
|
||||
|
||||
Value * PackageInfo::queryMeta(const std::string & name)
|
||||
{
|
||||
if (!getMeta()) return 0;
|
||||
if (!getMeta())
|
||||
return 0;
|
||||
auto a = meta->get(state->symbols.create(name));
|
||||
if (!a || !checkMeta(*a->value)) return 0;
|
||||
if (!a || !checkMeta(*a->value))
|
||||
return 0;
|
||||
return a->value;
|
||||
}
|
||||
|
||||
|
||||
std::string PackageInfo::queryMetaString(const std::string & name)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v || v->type() != nString) return "";
|
||||
if (!v || v->type() != nString)
|
||||
return "";
|
||||
return v->c_str();
|
||||
}
|
||||
|
||||
|
||||
NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nInt) return v->integer();
|
||||
if (!v)
|
||||
return def;
|
||||
if (v->type() == nInt)
|
||||
return v->integer();
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
integer meta fields. */
|
||||
|
|
@ -255,8 +268,10 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
|
|||
NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nFloat) return v->fpoint();
|
||||
if (!v)
|
||||
return def;
|
||||
if (v->type() == nFloat)
|
||||
return v->fpoint();
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
float meta fields. */
|
||||
|
|
@ -266,22 +281,24 @@ NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
|
|||
return def;
|
||||
}
|
||||
|
||||
|
||||
bool PackageInfo::queryMetaBool(const std::string & name, bool def)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nBool) return v->boolean();
|
||||
if (!v)
|
||||
return def;
|
||||
if (v->type() == nBool)
|
||||
return v->boolean();
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
Boolean meta fields. */
|
||||
if (v->string_view() == "true") return true;
|
||||
if (v->string_view() == "false") return false;
|
||||
if (v->string_view() == "true")
|
||||
return true;
|
||||
if (v->string_view() == "false")
|
||||
return false;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
void PackageInfo::setMeta(const std::string & name, Value * v)
|
||||
{
|
||||
getMeta();
|
||||
|
|
@ -291,30 +308,35 @@ void PackageInfo::setMeta(const std::string & name, Value * v)
|
|||
for (auto i : *meta)
|
||||
if (i.name != sym)
|
||||
attrs.insert(i);
|
||||
if (v) attrs.insert(sym, v);
|
||||
if (v)
|
||||
attrs.insert(sym, v);
|
||||
meta = attrs.finish();
|
||||
}
|
||||
|
||||
|
||||
/* Cache for already considered attrsets. */
|
||||
typedef std::set<const Bindings *> Done;
|
||||
|
||||
|
||||
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
|
||||
then put information about it in `drvs' (unless it's already in `done').
|
||||
The result boolean indicates whether it makes sense
|
||||
for the caller to recursively search for derivations in `v'. */
|
||||
static bool getDerivation(EvalState & state, Value & v,
|
||||
const std::string & attrPath, PackageInfos & drvs, Done & done,
|
||||
static bool getDerivation(
|
||||
EvalState & state,
|
||||
Value & v,
|
||||
const std::string & attrPath,
|
||||
PackageInfos & drvs,
|
||||
Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
try {
|
||||
state.forceValue(v, v.determinePos(noPos));
|
||||
if (!state.isDerivation(v)) return true;
|
||||
if (!state.isDerivation(v))
|
||||
return true;
|
||||
|
||||
/* Remove spurious duplicates (e.g., a set like `rec { x =
|
||||
derivation {...}; y = x;}'. */
|
||||
if (!done.insert(v.attrs()).second) return false;
|
||||
if (!done.insert(v.attrs()).second)
|
||||
return false;
|
||||
|
||||
PackageInfo drv(state, attrPath, v.attrs());
|
||||
|
||||
|
|
@ -325,42 +347,44 @@ static bool getDerivation(EvalState & state, Value & v,
|
|||
return false;
|
||||
|
||||
} catch (AssertionError & e) {
|
||||
if (ignoreAssertionFailures) return false;
|
||||
if (ignoreAssertionFailures)
|
||||
return false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::optional<PackageInfo> getDerivation(EvalState & state, Value & v,
|
||||
bool ignoreAssertionFailures)
|
||||
std::optional<PackageInfo> getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
PackageInfos drvs;
|
||||
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
|
||||
if (drvs.size() != 1) return {};
|
||||
if (drvs.size() != 1)
|
||||
return {};
|
||||
return std::move(drvs.front());
|
||||
}
|
||||
|
||||
|
||||
static std::string addToPath(const std::string & s1, std::string_view s2)
|
||||
{
|
||||
return s1.empty() ? std::string(s2) : s1 + "." + s2;
|
||||
}
|
||||
|
||||
|
||||
static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
|
||||
|
||||
|
||||
static void getDerivations(EvalState & state, Value & vIn,
|
||||
const std::string & pathPrefix, Bindings & autoArgs,
|
||||
PackageInfos & drvs, Done & done,
|
||||
static void getDerivations(
|
||||
EvalState & state,
|
||||
Value & vIn,
|
||||
const std::string & pathPrefix,
|
||||
Bindings & autoArgs,
|
||||
PackageInfos & drvs,
|
||||
Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Value v;
|
||||
state.autoCallFunction(autoArgs, vIn, v);
|
||||
|
||||
/* Process the expression. */
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures))
|
||||
;
|
||||
|
||||
else if (v.type() == nAttrs) {
|
||||
|
||||
|
|
@ -388,8 +412,11 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||
`recurseForDerivations = true' attribute. */
|
||||
if (i->value->type() == nAttrs) {
|
||||
auto j = i->value->attrs()->get(state.sRecurseForDerivations);
|
||||
if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
|
||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
if (j
|
||||
&& state.forceBool(
|
||||
*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
|
||||
getDerivations(
|
||||
state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
} catch (Error & e) {
|
||||
|
|
@ -412,13 +439,16 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||
state.error<TypeError>("expression does not evaluate to a derivation (or a set or list of those)").debugThrow();
|
||||
}
|
||||
|
||||
|
||||
void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix,
|
||||
Bindings & autoArgs, PackageInfos & drvs, bool ignoreAssertionFailures)
|
||||
void getDerivations(
|
||||
EvalState & state,
|
||||
Value & v,
|
||||
const std::string & pathPrefix,
|
||||
Bindings & autoArgs,
|
||||
PackageInfos & drvs,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
getDerivations(state, v, pathPrefix, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -11,11 +11,8 @@ namespace nix {
|
|||
MakeError(AttrPathNotFound, Error);
|
||||
MakeError(NoPositionInfo, Error);
|
||||
|
||||
std::pair<Value *, PosIdx> findAlongAttrPath(
|
||||
EvalState & state,
|
||||
const std::string & attrPath,
|
||||
Bindings & autoArgs,
|
||||
Value & vIn);
|
||||
std::pair<Value *, PosIdx>
|
||||
findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn);
|
||||
|
||||
/**
|
||||
* Heuristic to find the filename and lineno or a nix value.
|
||||
|
|
@ -24,4 +21,4 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
|||
|
||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
|
||||
class EvalState;
|
||||
struct Value;
|
||||
|
||||
|
|
@ -25,15 +24,19 @@ struct Attr
|
|||
PosIdx pos;
|
||||
Value * value;
|
||||
Attr(Symbol name, Value * value, PosIdx pos = noPos)
|
||||
: name(name), pos(pos), value(value) { };
|
||||
Attr() { };
|
||||
auto operator <=> (const Attr & a) const
|
||||
: name(name)
|
||||
, pos(pos)
|
||||
, value(value) {};
|
||||
Attr() {};
|
||||
|
||||
auto operator<=>(const Attr & a) const
|
||||
{
|
||||
return name <=> a.name;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *),
|
||||
static_assert(
|
||||
sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *),
|
||||
"performance of the evaluator is highly sensitive to the size of Attr. "
|
||||
"avoid introducing any padding into Attr if at all possible, and do not "
|
||||
"introduce new fields that need not be present for almost every instance.");
|
||||
|
|
@ -54,13 +57,24 @@ private:
|
|||
size_t size_, capacity_;
|
||||
Attr attrs[0];
|
||||
|
||||
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
|
||||
Bindings(size_t capacity)
|
||||
: size_(0)
|
||||
, capacity_(capacity)
|
||||
{
|
||||
}
|
||||
|
||||
Bindings(const Bindings & bindings) = delete;
|
||||
|
||||
public:
|
||||
size_t size() const { return size_; }
|
||||
size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
bool empty() const { return !size_; }
|
||||
bool empty() const
|
||||
{
|
||||
return !size_;
|
||||
}
|
||||
|
||||
typedef Attr * iterator;
|
||||
|
||||
|
|
@ -76,7 +90,8 @@ public:
|
|||
{
|
||||
Attr key(name, 0);
|
||||
const_iterator i = std::lower_bound(begin(), end(), key);
|
||||
if (i != end() && i->name == name) return i;
|
||||
if (i != end() && i->name == name)
|
||||
return i;
|
||||
return end();
|
||||
}
|
||||
|
||||
|
|
@ -84,15 +99,30 @@ public:
|
|||
{
|
||||
Attr key(name, 0);
|
||||
const_iterator i = std::lower_bound(begin(), end(), key);
|
||||
if (i != end() && i->name == name) return &*i;
|
||||
if (i != end() && i->name == name)
|
||||
return &*i;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
iterator begin() { return &attrs[0]; }
|
||||
iterator end() { return &attrs[size_]; }
|
||||
iterator begin()
|
||||
{
|
||||
return &attrs[0];
|
||||
}
|
||||
|
||||
const_iterator begin() const { return &attrs[0]; }
|
||||
const_iterator end() const { return &attrs[size_]; }
|
||||
iterator end()
|
||||
{
|
||||
return &attrs[size_];
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return &attrs[0];
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return &attrs[size_];
|
||||
}
|
||||
|
||||
Attr & operator[](size_t pos)
|
||||
{
|
||||
|
|
@ -106,7 +136,10 @@ public:
|
|||
|
||||
void sort();
|
||||
|
||||
size_t capacity() const { return capacity_; }
|
||||
size_t capacity() const
|
||||
{
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attributes in lexicographically sorted order.
|
||||
|
|
@ -143,8 +176,10 @@ public:
|
|||
EvalState & state;
|
||||
|
||||
BindingsBuilder(EvalState & state, Bindings * bindings)
|
||||
: bindings(bindings), state(state)
|
||||
{ }
|
||||
: bindings(bindings)
|
||||
, state(state)
|
||||
{
|
||||
}
|
||||
|
||||
void insert(Symbol name, Value * value, PosIdx pos = noPos)
|
||||
{
|
||||
|
|
@ -191,4 +226,4 @@ public:
|
|||
friend struct ExprAttrs;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -43,10 +43,7 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
|
|||
|
||||
public:
|
||||
|
||||
EvalCache(
|
||||
std::optional<std::reference_wrapper<const Hash>> useCache,
|
||||
EvalState & state,
|
||||
RootLoader rootLoader);
|
||||
EvalCache(std::optional<std::reference_wrapper<const Hash>> useCache, EvalState & state, RootLoader rootLoader);
|
||||
|
||||
ref<AttrCursor> getRoot();
|
||||
};
|
||||
|
|
@ -63,11 +60,23 @@ enum AttrType {
|
|||
Int = 8,
|
||||
};
|
||||
|
||||
struct placeholder_t {};
|
||||
struct missing_t {};
|
||||
struct misc_t {};
|
||||
struct failed_t {};
|
||||
struct int_t { NixInt x; };
|
||||
struct placeholder_t
|
||||
{};
|
||||
|
||||
struct missing_t
|
||||
{};
|
||||
|
||||
struct misc_t
|
||||
{};
|
||||
|
||||
struct failed_t
|
||||
{};
|
||||
|
||||
struct int_t
|
||||
{
|
||||
NixInt x;
|
||||
};
|
||||
|
||||
typedef uint64_t AttrId;
|
||||
typedef std::pair<AttrId, Symbol> AttrKey;
|
||||
typedef std::pair<std::string, NixStringContext> string_t;
|
||||
|
|
@ -81,8 +90,8 @@ typedef std::variant<
|
|||
failed_t,
|
||||
bool,
|
||||
int_t,
|
||||
std::vector<std::string>
|
||||
> AttrValue;
|
||||
std::vector<std::string>>
|
||||
AttrValue;
|
||||
|
||||
class AttrCursor : public std::enable_shared_from_this<AttrCursor>
|
||||
{
|
||||
|
|
@ -161,4 +170,4 @@ public:
|
|||
StorePath forceDerivation();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix::eval_cache
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ struct InvalidPathError : public EvalError
|
|||
{
|
||||
public:
|
||||
Path path;
|
||||
|
||||
InvalidPathError(EvalState & state, const Path & path)
|
||||
: EvalError(state, "path '%s' is not valid", path)
|
||||
{
|
||||
|
|
@ -119,4 +120,4 @@ public:
|
|||
[[gnu::noinline, gnu::noreturn]] void panic();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ inline void * allocBytes(size_t n)
|
|||
#else
|
||||
p = calloc(n, 1);
|
||||
#endif
|
||||
if (!p) throw std::bad_alloc();
|
||||
if (!p)
|
||||
throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
Value * EvalState::allocValue()
|
||||
{
|
||||
|
|
@ -38,7 +38,8 @@ Value * EvalState::allocValue()
|
|||
have to explicitly clear the first word of every object we take. */
|
||||
if (!*valueAllocCache) {
|
||||
*valueAllocCache = GC_malloc_many(sizeof(Value));
|
||||
if (!*valueAllocCache) throw std::bad_alloc();
|
||||
if (!*valueAllocCache)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
/* GC_NEXT is a convenience macro for accessing the first word of an object.
|
||||
|
|
@ -54,7 +55,6 @@ Value * EvalState::allocValue()
|
|||
return (Value *) p;
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
Env & EvalState::allocEnv(size_t size)
|
||||
{
|
||||
|
|
@ -68,7 +68,8 @@ Env & EvalState::allocEnv(size_t size)
|
|||
/* see allocValue for explanations. */
|
||||
if (!*env1AllocCache) {
|
||||
*env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *));
|
||||
if (!*env1AllocCache) throw std::bad_alloc();
|
||||
if (!*env1AllocCache)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
void * p = *env1AllocCache;
|
||||
|
|
@ -84,7 +85,6 @@ Env & EvalState::allocEnv(size_t size)
|
|||
return *env;
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||
{
|
||||
|
|
@ -94,7 +94,7 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
|||
Expr * expr = v.thunk().expr;
|
||||
try {
|
||||
v.mkBlackhole();
|
||||
//checkInterrupt();
|
||||
// checkInterrupt();
|
||||
if (env) [[likely]]
|
||||
expr->eval(*this, *env, v);
|
||||
else
|
||||
|
|
@ -104,54 +104,47 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
|||
tryFixupBlackHolePos(v, pos);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (v.isApp())
|
||||
} else if (v.isApp())
|
||||
callFunction(*v.app().left, *v.app().right, v, pos);
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceAttrs(v, [&]() { return pos; }, errorCtx);
|
||||
}
|
||||
|
||||
|
||||
template <typename Callable>
|
||||
template<typename Callable>
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view errorCtx)
|
||||
{
|
||||
PosIdx pos = getPos();
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nAttrs) {
|
||||
error<TypeError>(
|
||||
"expected a set but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).withTrace(pos, errorCtx).debugThrow();
|
||||
error<TypeError>("expected a set but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions))
|
||||
.withTrace(pos, errorCtx)
|
||||
.debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (!v.isList()) {
|
||||
error<TypeError>(
|
||||
"expected a list but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).withTrace(pos, errorCtx).debugThrow();
|
||||
error<TypeError>("expected a list but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions))
|
||||
.withTrace(pos, errorCtx)
|
||||
.debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline CallDepth EvalState::addCallDepth(const PosIdx pos) {
|
||||
inline CallDepth EvalState::addCallDepth(const PosIdx pos)
|
||||
{
|
||||
if (callDepth > settings.maxCallDepth)
|
||||
error<EvalBaseError>("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow();
|
||||
|
||||
return CallDepth(callDepth);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ EvalProfilerMode BaseSetting<EvalProfilerMode>::parse(const std::string & str) c
|
|||
template<>
|
||||
std::string BaseSetting<EvalProfilerMode>::to_string() const;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -111,4 +111,4 @@ public:
|
|||
|
||||
ref<EvalProfiler> makeSampleStackProfiler(EvalState & state, std::filesystem::path profileFile, uint64_t frequency);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -74,7 +74,9 @@ struct EvalSettings : Config
|
|||
)"};
|
||||
|
||||
Setting<Strings> nixPath{
|
||||
this, {}, "nix-path",
|
||||
this,
|
||||
{},
|
||||
"nix-path",
|
||||
R"(
|
||||
List of search paths to use for [lookup path](@docroot@/language/constructs/lookup-path.md) resolution.
|
||||
This setting determines the value of
|
||||
|
|
@ -107,10 +109,14 @@ struct EvalSettings : Config
|
|||
> If [restricted evaluation](@docroot@/command-ref/conf-file.md#conf-restrict-eval) is enabled, the default value is empty.
|
||||
>
|
||||
> If [pure evaluation](#conf-pure-eval) is enabled, `builtins.nixPath` *always* evaluates to the empty list `[ ]`.
|
||||
)", {}, false};
|
||||
)",
|
||||
{},
|
||||
false};
|
||||
|
||||
Setting<std::string> currentSystem{
|
||||
this, "", "eval-system",
|
||||
this,
|
||||
"",
|
||||
"eval-system",
|
||||
R"(
|
||||
This option defines
|
||||
[`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem)
|
||||
|
|
@ -130,7 +136,9 @@ struct EvalSettings : Config
|
|||
const std::string & getCurrentSystem() const;
|
||||
|
||||
Setting<bool> restrictEval{
|
||||
this, false, "restrict-eval",
|
||||
this,
|
||||
false,
|
||||
"restrict-eval",
|
||||
R"(
|
||||
If set to `true`, the Nix evaluator doesn't allow access to any
|
||||
files outside of
|
||||
|
|
@ -139,7 +147,10 @@ struct EvalSettings : Config
|
|||
[`allowed-uris`](@docroot@/command-ref/conf-file.md#conf-allowed-uris).
|
||||
)"};
|
||||
|
||||
Setting<bool> pureEval{this, false, "pure-eval",
|
||||
Setting<bool> pureEval{
|
||||
this,
|
||||
false,
|
||||
"pure-eval",
|
||||
R"(
|
||||
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
|
||||
|
||||
|
|
@ -149,21 +160,23 @@ struct EvalSettings : Config
|
|||
- [`builtins.currentTime`](@docroot@/language/builtins.md#builtins-currentTime)
|
||||
- [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath)
|
||||
- [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath)
|
||||
)"
|
||||
};
|
||||
)"};
|
||||
|
||||
Setting<bool> traceImportFromDerivation{
|
||||
this, false, "trace-import-from-derivation",
|
||||
this,
|
||||
false,
|
||||
"trace-import-from-derivation",
|
||||
R"(
|
||||
By default, Nix allows [Import from Derivation](@docroot@/language/import-from-derivation.md).
|
||||
|
||||
When this setting is `true`, Nix logs a warning indicating that it performed such an import.
|
||||
This option has no effect if `allow-import-from-derivation` is disabled.
|
||||
)"
|
||||
};
|
||||
)"};
|
||||
|
||||
Setting<bool> enableImportFromDerivation{
|
||||
this, true, "allow-import-from-derivation",
|
||||
this,
|
||||
true,
|
||||
"allow-import-from-derivation",
|
||||
R"(
|
||||
By default, Nix allows [Import from Derivation](@docroot@/language/import-from-derivation.md).
|
||||
|
||||
|
|
@ -173,7 +186,10 @@ struct EvalSettings : Config
|
|||
regardless of the state of the store.
|
||||
)"};
|
||||
|
||||
Setting<Strings> allowedUris{this, {}, "allowed-uris",
|
||||
Setting<Strings> allowedUris{
|
||||
this,
|
||||
{},
|
||||
"allowed-uris",
|
||||
R"(
|
||||
A list of URI prefixes to which access is allowed in restricted
|
||||
evaluation mode. For example, when set to
|
||||
|
|
@ -186,7 +202,10 @@ struct EvalSettings : Config
|
|||
- or the prefix is a URI scheme ended by a colon `:` and the URI has the same scheme.
|
||||
)"};
|
||||
|
||||
Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
|
||||
Setting<bool> traceFunctionCalls{
|
||||
this,
|
||||
false,
|
||||
"trace-function-calls",
|
||||
R"(
|
||||
If set to `true`, the Nix evaluator traces every function call.
|
||||
Nix prints a log message at the "vomit" level for every function
|
||||
|
|
@ -204,7 +223,10 @@ struct EvalSettings : Config
|
|||
`flamegraph.pl`.
|
||||
)"};
|
||||
|
||||
Setting<EvalProfilerMode> evalProfilerMode{this, EvalProfilerMode::disabled, "eval-profiler",
|
||||
Setting<EvalProfilerMode> evalProfilerMode{
|
||||
this,
|
||||
EvalProfilerMode::disabled,
|
||||
"eval-profiler",
|
||||
R"(
|
||||
Enables evaluation profiling. The following modes are supported:
|
||||
|
||||
|
|
@ -215,38 +237,56 @@ struct EvalSettings : Config
|
|||
See [Using the `eval-profiler`](@docroot@/advanced-topics/eval-profiler.md).
|
||||
)"};
|
||||
|
||||
Setting<Path> evalProfileFile{this, "nix.profile", "eval-profile-file",
|
||||
Setting<Path> evalProfileFile{
|
||||
this,
|
||||
"nix.profile",
|
||||
"eval-profile-file",
|
||||
R"(
|
||||
Specifies the file where [evaluation profile](#conf-eval-profiler) is saved.
|
||||
)"};
|
||||
|
||||
Setting<uint32_t> evalProfilerFrequency{this, 99, "eval-profiler-frequency",
|
||||
Setting<uint32_t> evalProfilerFrequency{
|
||||
this,
|
||||
99,
|
||||
"eval-profiler-frequency",
|
||||
R"(
|
||||
Specifies the sampling rate in hertz for sampling evaluation profilers.
|
||||
Use `0` to sample the stack after each function call.
|
||||
See [`eval-profiler`](#conf-eval-profiler).
|
||||
)"};
|
||||
|
||||
Setting<bool> useEvalCache{this, true, "eval-cache",
|
||||
Setting<bool> useEvalCache{
|
||||
this,
|
||||
true,
|
||||
"eval-cache",
|
||||
R"(
|
||||
Whether to use the flake evaluation cache.
|
||||
Certain commands won't have to evaluate when invoked for the second time with a particular version of a flake.
|
||||
Intermediate results are not cached.
|
||||
)"};
|
||||
|
||||
Setting<bool> ignoreExceptionsDuringTry{this, false, "ignore-try",
|
||||
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 pauses on all exceptions.
|
||||
)"};
|
||||
|
||||
Setting<bool> traceVerbose{this, false, "trace-verbose",
|
||||
Setting<bool> traceVerbose{
|
||||
this,
|
||||
false,
|
||||
"trace-verbose",
|
||||
"Whether `builtins.traceVerbose` should trace its first argument when evaluated."};
|
||||
|
||||
Setting<unsigned int> maxCallDepth{this, 10000, "max-call-depth",
|
||||
"The maximum function call depth to allow before erroring."};
|
||||
Setting<unsigned int> maxCallDepth{
|
||||
this, 10000, "max-call-depth", "The maximum function call depth to allow before erroring."};
|
||||
|
||||
Setting<bool> builtinsTraceDebugger{this, false, "debugger-on-trace",
|
||||
Setting<bool> builtinsTraceDebugger{
|
||||
this,
|
||||
false,
|
||||
"debugger-on-trace",
|
||||
R"(
|
||||
If set to true and the `--debugger` flag is given, the following functions
|
||||
enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break):
|
||||
|
|
@ -259,7 +299,10 @@ struct EvalSettings : Config
|
|||
This is useful for debugging warnings in third-party Nix code.
|
||||
)"};
|
||||
|
||||
Setting<bool> builtinsDebuggerOnWarn{this, false, "debugger-on-warn",
|
||||
Setting<bool> builtinsDebuggerOnWarn{
|
||||
this,
|
||||
false,
|
||||
"debugger-on-warn",
|
||||
R"(
|
||||
If set to true and the `--debugger` flag is given, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn)
|
||||
will enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break).
|
||||
|
|
@ -269,7 +312,10 @@ struct EvalSettings : Config
|
|||
Use [`debugger-on-trace`](#conf-debugger-on-trace) to also enter the debugger on legacy warnings that are logged with [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace).
|
||||
)"};
|
||||
|
||||
Setting<bool> builtinsAbortOnWarn{this, false, "abort-on-warn",
|
||||
Setting<bool> builtinsAbortOnWarn{
|
||||
this,
|
||||
false,
|
||||
"abort-on-warn",
|
||||
R"(
|
||||
If set to true, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) throws an error when logging a warning.
|
||||
|
||||
|
|
@ -288,4 +334,4 @@ struct EvalSettings : Config
|
|||
*/
|
||||
Path getNixDefExpr();
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -34,39 +34,46 @@ namespace nix {
|
|||
constexpr size_t maxPrimOpArity = 8;
|
||||
|
||||
class Store;
|
||||
|
||||
namespace fetchers {
|
||||
struct Settings;
|
||||
struct InputCache;
|
||||
}
|
||||
} // namespace fetchers
|
||||
struct EvalSettings;
|
||||
class EvalState;
|
||||
class StorePath;
|
||||
struct SingleDerivedPath;
|
||||
enum RepairFlag : bool;
|
||||
struct MemorySourceAccessor;
|
||||
|
||||
namespace eval_cache {
|
||||
class EvalCache;
|
||||
class EvalCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments a count on construction and decrements on destruction.
|
||||
*/
|
||||
class CallDepth {
|
||||
size_t & count;
|
||||
class CallDepth
|
||||
{
|
||||
size_t & count;
|
||||
|
||||
public:
|
||||
CallDepth(size_t & count) : count(count) {
|
||||
++count;
|
||||
}
|
||||
~CallDepth() {
|
||||
--count;
|
||||
}
|
||||
CallDepth(size_t & count)
|
||||
: count(count)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
|
||||
~CallDepth()
|
||||
{
|
||||
--count;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function that implements a primop.
|
||||
*/
|
||||
using PrimOpFun = void(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
using PrimOpFun = void(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
|
||||
/**
|
||||
* Info about a primitive operation, and its implementation
|
||||
|
|
@ -151,7 +158,9 @@ struct Constant
|
|||
bool impureOnly = false;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, Value *, std::less<std::string>, traceable_allocator<std::pair<const std::string, Value *> > > ValMap;
|
||||
typedef std::
|
||||
map<std::string, Value *, std::less<std::string>, traceable_allocator<std::pair<const std::string, Value *>>>
|
||||
ValMap;
|
||||
|
||||
typedef std::unordered_map<PosIdx, DocComment> DocCommentMap;
|
||||
|
||||
|
|
@ -161,23 +170,25 @@ struct Env
|
|||
Value * values[0];
|
||||
};
|
||||
|
||||
void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env);
|
||||
void printEnvBindings(const EvalState & es, const Expr & expr, const Env & env);
|
||||
void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env, int lvl = 0);
|
||||
|
||||
std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env);
|
||||
|
||||
void copyContext(const Value & v, NixStringContext & context, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
void copyContext(
|
||||
const Value & v,
|
||||
NixStringContext & context,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
std::string printValue(EvalState & state, Value & v);
|
||||
std::ostream & operator << (std::ostream & os, const ValueType t);
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const ValueType t);
|
||||
|
||||
struct RegexCache;
|
||||
|
||||
std::shared_ptr<RegexCache> makeRegexCache();
|
||||
|
||||
struct DebugTrace {
|
||||
struct DebugTrace
|
||||
{
|
||||
/* WARNING: Converting PosIdx -> Pos should be done with extra care. This is
|
||||
due to the fact that operator[] of PosTable is incredibly expensive. */
|
||||
std::variant<Pos, PosIdx> pos;
|
||||
|
|
@ -210,18 +221,11 @@ public:
|
|||
SymbolTable symbols;
|
||||
PosTable positions;
|
||||
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||
sFile, sLine, sColumn, sFunctor, sToString,
|
||||
sRight, sWrong, sStructuredAttrs, sJson,
|
||||
sAllowedReferences, sAllowedRequisites, sDisallowedReferences, sDisallowedRequisites,
|
||||
sMaxSize, sMaxClosureSize,
|
||||
sBuilder, sArgs,
|
||||
sContentAddressed, sImpure,
|
||||
sOutputHash, sOutputHashAlgo, sOutputHashMode,
|
||||
sRecurseForDerivations,
|
||||
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
|
||||
sPrefix,
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, sSystem, sOverrides, sOutputs, sOutputName,
|
||||
sIgnoreNulls, sFile, sLine, sColumn, sFunctor, sToString, sRight, sWrong, sStructuredAttrs, sJson,
|
||||
sAllowedReferences, sAllowedRequisites, sDisallowedReferences, sDisallowedRequisites, sMaxSize, sMaxClosureSize,
|
||||
sBuilder, sArgs, sContentAddressed, sImpure, sOutputHash, sOutputHashAlgo, sOutputHashMode,
|
||||
sRecurseForDerivations, sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath, sPrefix,
|
||||
sOutputSpecified;
|
||||
|
||||
const Expr::AstSymbols exprSymbols;
|
||||
|
|
@ -309,19 +313,21 @@ public:
|
|||
/**
|
||||
* Debugger
|
||||
*/
|
||||
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||
ReplExitStatus (*debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||
bool debugStop;
|
||||
bool inDebugger = false;
|
||||
int trylevel;
|
||||
std::list<DebugTrace> debugTraces;
|
||||
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
std::map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
|
||||
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
|
||||
{
|
||||
auto i = exprEnvs.find(&expr);
|
||||
if (i != exprEnvs.end())
|
||||
return i->second;
|
||||
else
|
||||
return std::shared_ptr<const StaticEnv>();;
|
||||
return std::shared_ptr<const StaticEnv>();
|
||||
;
|
||||
}
|
||||
|
||||
/** Whether a debug repl can be started. If `false`, `runDebugRepl(error)` will return without starting a repl. */
|
||||
|
|
@ -340,7 +346,8 @@ public:
|
|||
|
||||
template<class T, typename... Args>
|
||||
[[nodiscard, gnu::noinline]]
|
||||
EvalErrorBuilder<T> & error(const Args & ... args) {
|
||||
EvalErrorBuilder<T> & error(const Args &... args)
|
||||
{
|
||||
// `EvalErrorBuilder::debugThrow` performs the corresponding `delete`.
|
||||
return *new EvalErrorBuilder<T>(*this, args...);
|
||||
}
|
||||
|
|
@ -359,13 +366,25 @@ private:
|
|||
/**
|
||||
* A cache from path names to parse trees.
|
||||
*/
|
||||
typedef std::unordered_map<SourcePath, Expr *, std::hash<SourcePath>, std::equal_to<SourcePath>, traceable_allocator<std::pair<const SourcePath, Expr *>>> FileParseCache;
|
||||
typedef std::unordered_map<
|
||||
SourcePath,
|
||||
Expr *,
|
||||
std::hash<SourcePath>,
|
||||
std::equal_to<SourcePath>,
|
||||
traceable_allocator<std::pair<const SourcePath, Expr *>>>
|
||||
FileParseCache;
|
||||
FileParseCache fileParseCache;
|
||||
|
||||
/**
|
||||
* A cache from path names to values.
|
||||
*/
|
||||
typedef std::unordered_map<SourcePath, Value, std::hash<SourcePath>, std::equal_to<SourcePath>, traceable_allocator<std::pair<const SourcePath, Value>>> FileEvalCache;
|
||||
typedef std::unordered_map<
|
||||
SourcePath,
|
||||
Value,
|
||||
std::hash<SourcePath>,
|
||||
std::equal_to<SourcePath>,
|
||||
traceable_allocator<std::pair<const SourcePath, Value>>>
|
||||
FileEvalCache;
|
||||
FileEvalCache fileEvalCache;
|
||||
|
||||
/**
|
||||
|
|
@ -405,7 +424,10 @@ public:
|
|||
std::shared_ptr<Store> buildStore = nullptr);
|
||||
~EvalState();
|
||||
|
||||
LookupPath getLookupPath() { return lookupPath; }
|
||||
LookupPath getLookupPath()
|
||||
{
|
||||
return lookupPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a `SourcePath` that refers to `path` in the root
|
||||
|
|
@ -486,9 +508,7 @@ public:
|
|||
*
|
||||
* If it is not found, return `std::nullopt`.
|
||||
*/
|
||||
std::optional<SourcePath> resolveLookupPathPath(
|
||||
const LookupPath::Path & elem,
|
||||
bool initAccessControl = false);
|
||||
std::optional<SourcePath> resolveLookupPathPath(const LookupPath::Path & elem, bool initAccessControl = false);
|
||||
|
||||
/**
|
||||
* Evaluate an expression to normal form
|
||||
|
|
@ -530,7 +550,7 @@ public:
|
|||
|
||||
void forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
template <typename Callable>
|
||||
template<typename Callable>
|
||||
inline void forceAttrs(Value & v, Callable getPos, std::string_view errorCtx);
|
||||
|
||||
inline void forceList(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
|
|
@ -539,7 +559,12 @@ public:
|
|||
*/
|
||||
void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
std::string_view forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
std::string_view forceString(
|
||||
Value & v,
|
||||
NixStringContext & context,
|
||||
const PosIdx pos,
|
||||
std::string_view errorCtx,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
/**
|
||||
|
|
@ -549,10 +574,10 @@ public:
|
|||
|
||||
template<typename... Args>
|
||||
[[gnu::noinline]]
|
||||
void addErrorTrace(Error & e, const Args & ... formatArgs) const;
|
||||
void addErrorTrace(Error & e, const Args &... formatArgs) const;
|
||||
template<typename... Args>
|
||||
[[gnu::noinline]]
|
||||
void addErrorTrace(Error & e, const PosIdx pos, const Args & ... formatArgs) const;
|
||||
void addErrorTrace(Error & e, const PosIdx pos, const Args &... formatArgs) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
@ -561,8 +586,8 @@ public:
|
|||
*/
|
||||
bool isDerivation(Value & v);
|
||||
|
||||
std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
|
||||
NixStringContext & context, bool coerceMore = false, bool copyToStore = true);
|
||||
std::optional<std::string> tryAttrsToString(
|
||||
const PosIdx pos, Value & v, NixStringContext & context, bool coerceMore = false, bool copyToStore = true);
|
||||
|
||||
/**
|
||||
* String coercion.
|
||||
|
|
@ -572,9 +597,13 @@ public:
|
|||
* booleans and lists to a string. If `copyToStore` is set,
|
||||
* referenced paths are copied to the Nix store as a side effect.
|
||||
*/
|
||||
BackedStringView coerceToString(const PosIdx pos, Value & v, NixStringContext & context,
|
||||
BackedStringView coerceToString(
|
||||
const PosIdx pos,
|
||||
Value & v,
|
||||
NixStringContext & context,
|
||||
std::string_view errorCtx,
|
||||
bool coerceMore = false, bool copyToStore = true,
|
||||
bool coerceMore = false,
|
||||
bool copyToStore = true,
|
||||
bool canonicalizePath = true);
|
||||
|
||||
StorePath copyPathToStore(NixStringContext & context, const SourcePath & path);
|
||||
|
|
@ -596,7 +625,11 @@ public:
|
|||
/**
|
||||
* Part of `coerceToSingleDerivedPath()` without any store IO which is exposed for unit testing only.
|
||||
*/
|
||||
std::pair<SingleDerivedPath, std::string_view> coerceToSingleDerivedPathUnchecked(const PosIdx pos, Value & v, std::string_view errorCtx, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
std::pair<SingleDerivedPath, std::string_view> coerceToSingleDerivedPathUnchecked(
|
||||
const PosIdx pos,
|
||||
Value & v,
|
||||
std::string_view errorCtx,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
/**
|
||||
* Coerce to `SingleDerivedPath`.
|
||||
|
|
@ -636,7 +669,13 @@ public:
|
|||
/**
|
||||
* Internal primops not exposed to the user.
|
||||
*/
|
||||
std::unordered_map<std::string, Value *, std::hash<std::string>, std::equal_to<std::string>, traceable_allocator<std::pair<const std::string, Value *>>> internalPrimOps;
|
||||
std::unordered_map<
|
||||
std::string,
|
||||
Value *,
|
||||
std::hash<std::string>,
|
||||
std::equal_to<std::string>,
|
||||
traceable_allocator<std::pair<const std::string, Value *>>>
|
||||
internalPrimOps;
|
||||
|
||||
/**
|
||||
* Name and documentation about every constant.
|
||||
|
|
@ -710,7 +749,8 @@ private:
|
|||
std::shared_ptr<StaticEnv> & staticEnv);
|
||||
|
||||
/**
|
||||
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run out of system stack.
|
||||
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run
|
||||
* out of system stack.
|
||||
*/
|
||||
size_t callDepth = 0;
|
||||
|
||||
|
|
@ -773,7 +813,7 @@ public:
|
|||
/**
|
||||
* Return a boolean `Value *` without allocating.
|
||||
*/
|
||||
Value *getBool(bool b);
|
||||
Value * getBool(bool b);
|
||||
|
||||
void mkThunk_(Value & v, Expr * expr);
|
||||
void mkPos(Value & v, PosIdx pos);
|
||||
|
|
@ -817,9 +857,7 @@ public:
|
|||
*
|
||||
* A combination of `mkStorePathString` and `mkOutputString`.
|
||||
*/
|
||||
void mkSingleDerivedPathString(
|
||||
const SingleDerivedPath & p,
|
||||
Value & v);
|
||||
void mkSingleDerivedPathString(const SingleDerivedPath & p, Value & v);
|
||||
|
||||
void concatLists(Value & v, size_t nrLists, Value * const * lists, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
|
|
@ -850,22 +888,22 @@ public:
|
|||
* @param[out] maybePaths if not nullptr, all built or referenced store paths will be added to this set
|
||||
* @return a mapping from the placeholders used to construct the associated value to their final store path.
|
||||
*/
|
||||
[[nodiscard]] StringMap realiseContext(const NixStringContext & context, StorePathSet * maybePaths = nullptr, bool isIFD = true);
|
||||
[[nodiscard]] StringMap
|
||||
realiseContext(const NixStringContext & context, StorePathSet * maybePaths = nullptr, bool isIFD = true);
|
||||
|
||||
/**
|
||||
* Realise the given string with context, and return the string with outputs instead of downstream output placeholders.
|
||||
* Realise the given string with context, and return the string with outputs instead of downstream output
|
||||
* placeholders.
|
||||
* @param[in] str the string to realise
|
||||
* @param[out] paths all referenced store paths will be added to this set
|
||||
* @return the realised string
|
||||
* @throw EvalError if the value is not a string, path or derivation (see `coerceToString`)
|
||||
*/
|
||||
std::string realiseString(Value & str, StorePathSet * storePathsOutMaybe, bool isIFD = true, const PosIdx pos = noPos);
|
||||
std::string
|
||||
realiseString(Value & str, StorePathSet * storePathsOutMaybe, bool isIFD = true, const PosIdx pos = noPos);
|
||||
|
||||
/* Call the binary path filter predicate used builtins.path etc. */
|
||||
bool callPathFilter(
|
||||
Value * filterFun,
|
||||
const SourcePath & path,
|
||||
PosIdx pos);
|
||||
bool callPathFilter(Value * filterFun, const SourcePath & path, PosIdx pos);
|
||||
|
||||
DocComment getDocCommentForPos(PosIdx pos);
|
||||
|
||||
|
|
@ -884,8 +922,7 @@ private:
|
|||
* Like `mkSingleDerivedPathStringRaw` but just creates a raw string
|
||||
* Value, which would also have a string context.
|
||||
*/
|
||||
std::string mkSingleDerivedPathStringRaw(
|
||||
const SingleDerivedPath & p);
|
||||
std::string mkSingleDerivedPathStringRaw(const SingleDerivedPath & p);
|
||||
|
||||
unsigned long nrEnvs = 0;
|
||||
unsigned long nrValuesInEnvs = 0;
|
||||
|
|
@ -925,20 +962,23 @@ private:
|
|||
friend struct ExprFloat;
|
||||
friend struct ExprPath;
|
||||
friend struct ExprSelect;
|
||||
friend void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
friend void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
friend void prim_getAttr(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
friend void prim_match(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
friend void prim_split(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
|
||||
friend struct Value;
|
||||
friend class ListBuilder;
|
||||
};
|
||||
|
||||
struct DebugTraceStacker {
|
||||
struct DebugTraceStacker
|
||||
{
|
||||
DebugTraceStacker(EvalState & evalState, DebugTrace t);
|
||||
|
||||
~DebugTraceStacker()
|
||||
{
|
||||
evalState.debugTraces.pop_front();
|
||||
}
|
||||
|
||||
EvalState & evalState;
|
||||
DebugTrace trace;
|
||||
};
|
||||
|
|
@ -964,6 +1004,6 @@ SourcePath resolveExprPath(SourcePath path, bool addDefaultNix = true);
|
|||
*/
|
||||
bool isAllowedURI(std::string_view uri, const Strings & allowedPaths);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
#include "nix/expr/eval-inline.hh"
|
||||
|
|
|
|||
|
|
@ -22,4 +22,4 @@ public:
|
|||
postFunctionCallHook(EvalState & state, const Value & v, std::span<Value *> args, const PosIdx pos) override;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ namespace nix {
|
|||
/**
|
||||
* A GC compatible vector that may used a reserved portion of `nItems` on the stack instead of allocating on the heap.
|
||||
*/
|
||||
template <typename T, size_t nItems>
|
||||
template<typename T, size_t nItems>
|
||||
using SmallVector = boost::container::small_vector<T, nItems, traceable_allocator<T>>;
|
||||
|
||||
/**
|
||||
* A vector of value pointers. See `SmallVector`.
|
||||
*/
|
||||
template <size_t nItems>
|
||||
template<size_t nItems>
|
||||
using SmallValueVector = SmallVector<Value *, nItems>;
|
||||
|
||||
/**
|
||||
|
|
@ -23,7 +23,7 @@ using SmallValueVector = SmallVector<Value *, nItems>;
|
|||
*
|
||||
* See also `SmallValueVector`.
|
||||
*/
|
||||
template <size_t nItems>
|
||||
template<size_t nItems>
|
||||
using SmallTemporaryValueVector = SmallVector<Value, nItems>;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include <string>
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
|
|
@ -33,7 +32,7 @@ private:
|
|||
*/
|
||||
bool failed = false;
|
||||
|
||||
const Bindings * attrs = nullptr, * meta = nullptr;
|
||||
const Bindings *attrs = nullptr, *meta = nullptr;
|
||||
|
||||
const Bindings * getMeta();
|
||||
|
||||
|
|
@ -45,7 +44,8 @@ public:
|
|||
*/
|
||||
std::string attrPath;
|
||||
|
||||
PackageInfo(EvalState & state) : state(&state) { };
|
||||
PackageInfo(EvalState & state)
|
||||
: state(&state) {};
|
||||
PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs);
|
||||
PackageInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
|
||||
|
||||
|
|
@ -74,28 +74,46 @@ public:
|
|||
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
|
||||
*/
|
||||
|
||||
void setName(const std::string & s) { name = s; }
|
||||
void setDrvPath(StorePath path) { drvPath = {{std::move(path)}}; }
|
||||
void setOutPath(StorePath path) { outPath = {{std::move(path)}}; }
|
||||
void setName(const std::string & s)
|
||||
{
|
||||
name = s;
|
||||
}
|
||||
|
||||
void setFailed() { failed = true; };
|
||||
bool hasFailed() { return failed; };
|
||||
void setDrvPath(StorePath path)
|
||||
{
|
||||
drvPath = {{std::move(path)}};
|
||||
}
|
||||
|
||||
void setOutPath(StorePath path)
|
||||
{
|
||||
outPath = {{std::move(path)}};
|
||||
}
|
||||
|
||||
void setFailed()
|
||||
{
|
||||
failed = true;
|
||||
};
|
||||
|
||||
bool hasFailed()
|
||||
{
|
||||
return failed;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
typedef std::list<PackageInfo, traceable_allocator<PackageInfo>> PackageInfos;
|
||||
|
||||
|
||||
/**
|
||||
* If value `v` denotes a derivation, return a PackageInfo object
|
||||
* describing it. Otherwise return nothing.
|
||||
*/
|
||||
std::optional<PackageInfo> getDerivation(EvalState & state,
|
||||
Value & v, bool ignoreAssertionFailures);
|
||||
std::optional<PackageInfo> getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures);
|
||||
|
||||
void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix,
|
||||
Bindings & autoArgs, PackageInfos & drvs,
|
||||
void getDerivations(
|
||||
EvalState & state,
|
||||
Value & v,
|
||||
const std::string & pathPrefix,
|
||||
Bindings & autoArgs,
|
||||
PackageInfos & drvs,
|
||||
bool ignoreAssertionFailures);
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -14,4 +14,4 @@ MakeError(JSONParseError, Error);
|
|||
|
||||
void parseJSON(EvalState & state, const std::string_view & s, Value & v);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ struct StaticEnv;
|
|||
struct Value;
|
||||
|
||||
/**
|
||||
* A documentation comment, in the sense of [RFC 145](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md)
|
||||
* A documentation comment, in the sense of [RFC
|
||||
* 145](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md)
|
||||
*
|
||||
* Note that this does not implement the following:
|
||||
* - argument attribute names ("formals"): TBD
|
||||
|
|
@ -34,7 +35,8 @@ struct Value;
|
|||
* `f: g: final: prev: <...>`. The parameters `final` and `prev` are part
|
||||
* of the overlay concept, while distracting from the function's purpose.
|
||||
*/
|
||||
struct DocComment {
|
||||
struct DocComment
|
||||
{
|
||||
|
||||
/**
|
||||
* Start of the comment, including the opening, ie `/` and `**`.
|
||||
|
|
@ -53,10 +55,12 @@ struct DocComment {
|
|||
* therefore baking optionality into it is also useful, to avoiding the memory
|
||||
* overhead of `std::optional`.
|
||||
*/
|
||||
operator bool() const { return static_cast<bool>(begin); }
|
||||
operator bool() const
|
||||
{
|
||||
return static_cast<bool>(begin);
|
||||
}
|
||||
|
||||
std::string getInnerText(const PosTable & positions) const;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -66,52 +70,69 @@ struct AttrName
|
|||
{
|
||||
Symbol symbol;
|
||||
Expr * expr = nullptr;
|
||||
AttrName(Symbol s) : symbol(s) {};
|
||||
AttrName(Expr * e) : expr(e) {};
|
||||
AttrName(Symbol s)
|
||||
: symbol(s) {};
|
||||
AttrName(Expr * e)
|
||||
: expr(e) {};
|
||||
};
|
||||
|
||||
typedef std::vector<AttrName> AttrPath;
|
||||
|
||||
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath);
|
||||
|
||||
|
||||
/* Abstract syntax of Nix expressions. */
|
||||
|
||||
struct Expr
|
||||
{
|
||||
struct AstSymbols {
|
||||
struct AstSymbols
|
||||
{
|
||||
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body;
|
||||
};
|
||||
|
||||
|
||||
static unsigned long nrExprs;
|
||||
Expr() {
|
||||
|
||||
Expr()
|
||||
{
|
||||
nrExprs++;
|
||||
}
|
||||
virtual ~Expr() { };
|
||||
|
||||
virtual ~Expr() {};
|
||||
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
|
||||
virtual void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||
virtual Value * maybeThunk(EvalState & state, Env & env);
|
||||
virtual void setName(Symbol name);
|
||||
virtual void setDocComment(DocComment docComment) { };
|
||||
virtual PosIdx getPos() const { return noPos; }
|
||||
virtual void setDocComment(DocComment docComment) {};
|
||||
|
||||
virtual PosIdx getPos() const
|
||||
{
|
||||
return noPos;
|
||||
}
|
||||
|
||||
// These are temporary methods to be used only in parser.y
|
||||
virtual void resetCursedOr() { };
|
||||
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) { };
|
||||
virtual void resetCursedOr() {};
|
||||
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) {};
|
||||
};
|
||||
|
||||
#define COMMON_METHODS \
|
||||
#define COMMON_METHODS \
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override; \
|
||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override;
|
||||
|
||||
struct ExprInt : Expr
|
||||
{
|
||||
Value v;
|
||||
ExprInt(NixInt n) { v.mkInt(n); };
|
||||
ExprInt(NixInt::Inner n) { v.mkInt(n); };
|
||||
|
||||
ExprInt(NixInt n)
|
||||
{
|
||||
v.mkInt(n);
|
||||
};
|
||||
|
||||
ExprInt(NixInt::Inner n)
|
||||
{
|
||||
v.mkInt(n);
|
||||
};
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -119,7 +140,12 @@ struct ExprInt : Expr
|
|||
struct ExprFloat : Expr
|
||||
{
|
||||
Value v;
|
||||
ExprFloat(NixFloat nf) { v.mkFloat(nf); };
|
||||
|
||||
ExprFloat(NixFloat nf)
|
||||
{
|
||||
v.mkFloat(nf);
|
||||
};
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -128,7 +154,13 @@ struct ExprString : Expr
|
|||
{
|
||||
std::string s;
|
||||
Value v;
|
||||
ExprString(std::string &&s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
||||
|
||||
ExprString(std::string && s)
|
||||
: s(std::move(s))
|
||||
{
|
||||
v.mkString(this->s.data());
|
||||
};
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -138,10 +170,14 @@ struct ExprPath : Expr
|
|||
ref<SourceAccessor> accessor;
|
||||
std::string s;
|
||||
Value v;
|
||||
ExprPath(ref<SourceAccessor> accessor, std::string s) : accessor(accessor), s(std::move(s))
|
||||
|
||||
ExprPath(ref<SourceAccessor> accessor, std::string s)
|
||||
: accessor(accessor)
|
||||
, s(std::move(s))
|
||||
{
|
||||
v.mkPath(&*accessor, this->s.c_str());
|
||||
}
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -170,10 +206,18 @@ struct ExprVar : Expr
|
|||
Level level = 0;
|
||||
Displacement displ = 0;
|
||||
|
||||
ExprVar(Symbol name) : name(name) { };
|
||||
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
|
||||
ExprVar(Symbol name)
|
||||
: name(name) {};
|
||||
ExprVar(const PosIdx & pos, Symbol name)
|
||||
: pos(pos)
|
||||
, name(name) {};
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
PosIdx getPos() const override { return pos; }
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
|
@ -184,7 +228,8 @@ struct ExprVar : Expr
|
|||
*/
|
||||
struct ExprInheritFrom : ExprVar
|
||||
{
|
||||
ExprInheritFrom(PosIdx pos, Displacement displ): ExprVar(pos, {})
|
||||
ExprInheritFrom(PosIdx pos, Displacement displ)
|
||||
: ExprVar(pos, {})
|
||||
{
|
||||
this->level = 0;
|
||||
this->displ = displ;
|
||||
|
|
@ -197,11 +242,26 @@ struct ExprInheritFrom : ExprVar
|
|||
struct ExprSelect : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
Expr * e, * def;
|
||||
Expr *e, *def;
|
||||
AttrPath attrPath;
|
||||
ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { };
|
||||
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def)
|
||||
: pos(pos)
|
||||
, e(e)
|
||||
, def(def)
|
||||
, attrPath(std::move(attrPath)) {};
|
||||
|
||||
ExprSelect(const PosIdx & pos, Expr * e, Symbol name)
|
||||
: pos(pos)
|
||||
, e(e)
|
||||
, def(0)
|
||||
{
|
||||
attrPath.push_back(AttrName(name));
|
||||
};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the `a.b.c` part of `a.b.c.d`. This exists mostly for the purpose of :doc in the repl.
|
||||
|
|
@ -209,7 +269,8 @@ struct ExprSelect : Expr
|
|||
* @param[out] attrs The attribute set that should contain the last attribute name (if it exists).
|
||||
* @return The last attribute name in `attrPath`
|
||||
*
|
||||
* @note This does *not* evaluate the final attribute, and does not fail if that's the only attribute that does not exist.
|
||||
* @note This does *not* evaluate the final attribute, and does not fail if that's the only attribute that does not
|
||||
* exist.
|
||||
*/
|
||||
Symbol evalExceptFinalSelect(EvalState & state, Env & env, Value & attrs);
|
||||
|
||||
|
|
@ -220,8 +281,15 @@ struct ExprOpHasAttr : Expr
|
|||
{
|
||||
Expr * e;
|
||||
AttrPath attrPath;
|
||||
ExprOpHasAttr(Expr * e, AttrPath attrPath) : e(e), attrPath(std::move(attrPath)) { };
|
||||
PosIdx getPos() const override { return e->getPos(); }
|
||||
ExprOpHasAttr(Expr * e, AttrPath attrPath)
|
||||
: e(e)
|
||||
, attrPath(std::move(attrPath)) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return e->getPos();
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
|
@ -229,7 +297,9 @@ struct ExprAttrs : Expr
|
|||
{
|
||||
bool recursive;
|
||||
PosIdx pos;
|
||||
struct AttrDef {
|
||||
|
||||
struct AttrDef
|
||||
{
|
||||
enum class Kind {
|
||||
/** `attr = expr;` */
|
||||
Plain,
|
||||
|
|
@ -244,8 +314,10 @@ struct ExprAttrs : Expr
|
|||
PosIdx pos;
|
||||
Displacement displ = 0; // displacement
|
||||
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
|
||||
: kind(kind), e(e), pos(pos) { };
|
||||
AttrDef() { };
|
||||
: kind(kind)
|
||||
, e(e)
|
||||
, pos(pos) {};
|
||||
AttrDef() {};
|
||||
|
||||
template<typename T>
|
||||
const T & chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const
|
||||
|
|
@ -261,24 +333,37 @@ struct ExprAttrs : Expr
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||
AttrDefs attrs;
|
||||
std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
|
||||
struct DynamicAttrDef {
|
||||
Expr * nameExpr, * valueExpr;
|
||||
|
||||
struct DynamicAttrDef
|
||||
{
|
||||
Expr *nameExpr, *valueExpr;
|
||||
PosIdx pos;
|
||||
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const PosIdx & pos)
|
||||
: nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
|
||||
: nameExpr(nameExpr)
|
||||
, valueExpr(valueExpr)
|
||||
, pos(pos) {};
|
||||
};
|
||||
|
||||
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
||||
DynamicAttrDefs dynamicAttrs;
|
||||
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
|
||||
ExprAttrs() : recursive(false) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
ExprAttrs(const PosIdx & pos)
|
||||
: recursive(false)
|
||||
, pos(pos) {};
|
||||
ExprAttrs()
|
||||
: recursive(false) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
|
||||
std::shared_ptr<const StaticEnv> bindInheritSources(
|
||||
EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
std::shared_ptr<const StaticEnv> bindInheritSources(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
Env * buildInheritFromEnv(EvalState & state, Env & up);
|
||||
void showBindings(const SymbolTable & symbols, std::ostream & str) const;
|
||||
};
|
||||
|
|
@ -286,7 +371,7 @@ struct ExprAttrs : Expr
|
|||
struct ExprList : Expr
|
||||
{
|
||||
std::vector<Expr *> elems;
|
||||
ExprList() { };
|
||||
ExprList() {};
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
|
||||
|
|
@ -314,19 +399,18 @@ struct Formals
|
|||
|
||||
bool has(Symbol arg) const
|
||||
{
|
||||
auto it = std::lower_bound(formals.begin(), formals.end(), arg,
|
||||
[] (const Formal & f, const Symbol & sym) { return f.name < sym; });
|
||||
auto it = std::lower_bound(
|
||||
formals.begin(), formals.end(), arg, [](const Formal & f, const Symbol & sym) { return f.name < sym; });
|
||||
return it != formals.end() && it->name == arg;
|
||||
}
|
||||
|
||||
std::vector<Formal> lexicographicOrder(const SymbolTable & symbols) const
|
||||
{
|
||||
std::vector<Formal> result(formals.begin(), formals.end());
|
||||
std::sort(result.begin(), result.end(),
|
||||
[&] (const Formal & a, const Formal & b) {
|
||||
std::string_view sa = symbols[a.name], sb = symbols[b.name];
|
||||
return sa < sb;
|
||||
});
|
||||
std::sort(result.begin(), result.end(), [&](const Formal & a, const Formal & b) {
|
||||
std::string_view sa = symbols[a.name], sb = symbols[b.name];
|
||||
return sa < sb;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
|
@ -341,17 +425,31 @@ struct ExprLambda : Expr
|
|||
DocComment docComment;
|
||||
|
||||
ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body)
|
||||
: pos(pos), arg(arg), formals(formals), body(body)
|
||||
{
|
||||
};
|
||||
: pos(pos)
|
||||
, arg(arg)
|
||||
, formals(formals)
|
||||
, body(body) {};
|
||||
|
||||
ExprLambda(PosIdx pos, Formals * formals, Expr * body)
|
||||
: pos(pos), formals(formals), body(body)
|
||||
: pos(pos)
|
||||
, formals(formals)
|
||||
, body(body)
|
||||
{
|
||||
}
|
||||
|
||||
void setName(Symbol name) override;
|
||||
std::string showNamePos(const EvalState & state) const;
|
||||
inline bool hasFormals() const { return formals != nullptr; }
|
||||
PosIdx getPos() const override { return pos; }
|
||||
|
||||
inline bool hasFormals() const
|
||||
{
|
||||
return formals != nullptr;
|
||||
}
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
virtual void setDocComment(DocComment docComment) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -362,13 +460,28 @@ struct ExprCall : Expr
|
|||
std::vector<Expr *> args;
|
||||
PosIdx pos;
|
||||
std::optional<PosIdx> cursedOrEndPos; // used during parsing to warn about https://github.com/NixOS/nix/issues/11118
|
||||
|
||||
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
|
||||
: fun(fun), args(args), pos(pos), cursedOrEndPos({})
|
||||
{ }
|
||||
: fun(fun)
|
||||
, args(args)
|
||||
, pos(pos)
|
||||
, cursedOrEndPos({})
|
||||
{
|
||||
}
|
||||
|
||||
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos)
|
||||
: fun(fun), args(args), pos(pos), cursedOrEndPos(cursedOrEndPos)
|
||||
{ }
|
||||
PosIdx getPos() const override { return pos; }
|
||||
: fun(fun)
|
||||
, args(args)
|
||||
, pos(pos)
|
||||
, cursedOrEndPos(cursedOrEndPos)
|
||||
{
|
||||
}
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
virtual void resetCursedOr() override;
|
||||
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) override;
|
||||
COMMON_METHODS
|
||||
|
|
@ -378,90 +491,144 @@ struct ExprLet : Expr
|
|||
{
|
||||
ExprAttrs * attrs;
|
||||
Expr * body;
|
||||
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
|
||||
ExprLet(ExprAttrs * attrs, Expr * body)
|
||||
: attrs(attrs)
|
||||
, body(body) {};
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprWith : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
Expr * attrs, * body;
|
||||
Expr *attrs, *body;
|
||||
size_t prevWith;
|
||||
ExprWith * parentWith;
|
||||
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body)
|
||||
: pos(pos)
|
||||
, attrs(attrs)
|
||||
, body(body) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprIf : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
Expr * cond, * then, * else_;
|
||||
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
Expr *cond, *then, *else_;
|
||||
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_)
|
||||
: pos(pos)
|
||||
, cond(cond)
|
||||
, then(then)
|
||||
, else_(else_) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprAssert : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
Expr * cond, * body;
|
||||
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
Expr *cond, *body;
|
||||
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body)
|
||||
: pos(pos)
|
||||
, cond(cond)
|
||||
, body(body) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprOpNot : Expr
|
||||
{
|
||||
Expr * e;
|
||||
ExprOpNot(Expr * e) : e(e) { };
|
||||
PosIdx getPos() const override { return e->getPos(); }
|
||||
ExprOpNot(Expr * e)
|
||||
: e(e) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return e->getPos();
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
#define MakeBinOp(name, s) \
|
||||
struct name : Expr \
|
||||
{ \
|
||||
PosIdx pos; \
|
||||
Expr * e1, * e2; \
|
||||
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
|
||||
name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override \
|
||||
{ \
|
||||
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
|
||||
} \
|
||||
#define MakeBinOp(name, s) \
|
||||
struct name : Expr \
|
||||
{ \
|
||||
PosIdx pos; \
|
||||
Expr *e1, *e2; \
|
||||
name(Expr * e1, Expr * e2) \
|
||||
: e1(e1) \
|
||||
, e2(e2) {}; \
|
||||
name(const PosIdx & pos, Expr * e1, Expr * e2) \
|
||||
: pos(pos) \
|
||||
, e1(e1) \
|
||||
, e2(e2) {}; \
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override \
|
||||
{ \
|
||||
str << "("; \
|
||||
e1->show(symbols, str); \
|
||||
str << " " s " "; \
|
||||
e2->show(symbols, str); \
|
||||
str << ")"; \
|
||||
} \
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override \
|
||||
{ \
|
||||
e1->bindVars(es, env); e2->bindVars(es, env); \
|
||||
} \
|
||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||
PosIdx getPos() const override { return pos; } \
|
||||
{ \
|
||||
e1->bindVars(es, env); \
|
||||
e2->bindVars(es, env); \
|
||||
} \
|
||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||
PosIdx getPos() const override \
|
||||
{ \
|
||||
return pos; \
|
||||
} \
|
||||
};
|
||||
|
||||
MakeBinOp(ExprOpEq, "==")
|
||||
MakeBinOp(ExprOpNEq, "!=")
|
||||
MakeBinOp(ExprOpAnd, "&&")
|
||||
MakeBinOp(ExprOpOr, "||")
|
||||
MakeBinOp(ExprOpImpl, "->")
|
||||
MakeBinOp(ExprOpUpdate, "//")
|
||||
MakeBinOp(ExprOpConcatLists, "++")
|
||||
MakeBinOp(ExprOpEq, "==") MakeBinOp(ExprOpNEq, "!=") MakeBinOp(ExprOpAnd, "&&") MakeBinOp(ExprOpOr, "||")
|
||||
MakeBinOp(ExprOpImpl, "->") MakeBinOp(ExprOpUpdate, "//") MakeBinOp(ExprOpConcatLists, "++")
|
||||
|
||||
struct ExprConcatStrings : Expr
|
||||
struct ExprConcatStrings : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
bool forceString;
|
||||
std::vector<std::pair<PosIdx, Expr *>> * es;
|
||||
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *>> * es)
|
||||
: pos(pos), forceString(forceString), es(es) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
: pos(pos)
|
||||
, forceString(forceString)
|
||||
, es(es) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprPos : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
ExprPos(const PosIdx & pos) : pos(pos) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
ExprPos(const PosIdx & pos)
|
||||
: pos(pos) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
|
@ -469,14 +636,16 @@ struct ExprPos : Expr
|
|||
struct ExprBlackHole : Expr
|
||||
{
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override {}
|
||||
|
||||
void eval(EvalState & state, Env & env, Value & v) override;
|
||||
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override {}
|
||||
|
||||
[[noreturn]] static void throwInfiniteRecursionError(EvalState & state, Value & v);
|
||||
};
|
||||
|
||||
extern ExprBlackHole eBlackHole;
|
||||
|
||||
|
||||
/* Static environments are used to map variable names onto (level,
|
||||
displacement) pairs used to obtain the value of the variable at
|
||||
runtime. */
|
||||
|
|
@ -498,8 +667,9 @@ struct StaticEnv
|
|||
|
||||
void sort()
|
||||
{
|
||||
std::stable_sort(vars.begin(), vars.end(),
|
||||
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
|
||||
std::stable_sort(vars.begin(), vars.end(), [](const Vars::value_type & a, const Vars::value_type & b) {
|
||||
return a.first < b.first;
|
||||
});
|
||||
}
|
||||
|
||||
void deduplicate()
|
||||
|
|
@ -507,7 +677,8 @@ struct StaticEnv
|
|||
auto it = vars.begin(), jt = it, end = vars.end();
|
||||
while (jt != end) {
|
||||
*it = *jt++;
|
||||
while (jt != end && it->first == jt->first) *it = *jt++;
|
||||
while (jt != end && it->first == jt->first)
|
||||
*it = *jt++;
|
||||
it++;
|
||||
}
|
||||
vars.erase(it, end);
|
||||
|
|
@ -517,10 +688,10 @@ struct StaticEnv
|
|||
{
|
||||
Vars::value_type key(name, 0);
|
||||
auto i = std::lower_bound(vars.begin(), vars.end(), key);
|
||||
if (i != vars.end() && i->first == name) return i;
|
||||
if (i != vars.end() && i->first == name)
|
||||
return i;
|
||||
return vars.end();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -17,7 +17,11 @@ struct StringToken
|
|||
const char * p;
|
||||
size_t l;
|
||||
bool hasIndentation;
|
||||
operator std::string_view() const { return {p, l}; }
|
||||
|
||||
operator std::string_view() const
|
||||
{
|
||||
return {p, l};
|
||||
}
|
||||
};
|
||||
|
||||
// This type must be trivially copyable; see YYLTYPE_IS_TRIVIAL in parser.y.
|
||||
|
|
@ -29,12 +33,14 @@ struct ParserLocation
|
|||
// backup to recover from yyless(0)
|
||||
int stashedBeginOffset, stashedEndOffset;
|
||||
|
||||
void stash() {
|
||||
void stash()
|
||||
{
|
||||
stashedBeginOffset = beginOffset;
|
||||
stashedEndOffset = endOffset;
|
||||
}
|
||||
|
||||
void unstash() {
|
||||
void unstash()
|
||||
{
|
||||
beginOffset = stashedBeginOffset;
|
||||
endOffset = stashedEndOffset;
|
||||
}
|
||||
|
|
@ -87,32 +93,30 @@ struct ParserState
|
|||
|
||||
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
|
||||
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
|
||||
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
|
||||
void addAttr(
|
||||
ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
|
||||
void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
|
||||
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
|
||||
Expr * stripIndentation(const PosIdx pos,
|
||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
|
||||
Expr * stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
|
||||
PosIdx at(const ParserLocation & loc);
|
||||
};
|
||||
|
||||
inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
|
||||
{
|
||||
throw ParseError({
|
||||
.msg = HintFmt("attribute '%1%' already defined at %2%",
|
||||
showAttrPath(symbols, attrPath), positions[prevPos]),
|
||||
.pos = positions[pos]
|
||||
});
|
||||
throw ParseError(
|
||||
{.msg = HintFmt("attribute '%1%' already defined at %2%", showAttrPath(symbols, attrPath), positions[prevPos]),
|
||||
.pos = positions[pos]});
|
||||
}
|
||||
|
||||
inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
|
||||
{
|
||||
throw ParseError({
|
||||
.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
|
||||
.pos = positions[pos]
|
||||
});
|
||||
throw ParseError(
|
||||
{.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
|
||||
.pos = positions[pos]});
|
||||
}
|
||||
|
||||
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc)
|
||||
inline void ParserState::addAttr(
|
||||
ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc)
|
||||
{
|
||||
AttrPath::iterator i;
|
||||
// All attrpaths have at least one attr
|
||||
|
|
@ -159,7 +163,8 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const
|
|||
* Precondition: attrPath is used for error messages and should already contain
|
||||
* symbol as its last element.
|
||||
*/
|
||||
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
|
||||
inline void
|
||||
ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
|
||||
{
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol);
|
||||
if (j != attrs->attrs.end()) {
|
||||
|
|
@ -189,12 +194,14 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const S
|
|||
attrPath.pop_back();
|
||||
}
|
||||
ae->attrs.clear();
|
||||
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(),
|
||||
jAttrs->dynamicAttrs.insert(
|
||||
jAttrs->dynamicAttrs.end(),
|
||||
std::make_move_iterator(ae->dynamicAttrs.begin()),
|
||||
std::make_move_iterator(ae->dynamicAttrs.end()));
|
||||
ae->dynamicAttrs.clear();
|
||||
if (ae->inheritFromExprs) {
|
||||
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
|
||||
jAttrs->inheritFromExprs->insert(
|
||||
jAttrs->inheritFromExprs->end(),
|
||||
std::make_move_iterator(ae->inheritFromExprs->begin()),
|
||||
std::make_move_iterator(ae->inheritFromExprs->end()));
|
||||
ae->inheritFromExprs = nullptr;
|
||||
|
|
@ -211,10 +218,9 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const S
|
|||
|
||||
inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
|
||||
{
|
||||
std::sort(formals->formals.begin(), formals->formals.end(),
|
||||
[] (const auto & a, const auto & b) {
|
||||
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
|
||||
});
|
||||
std::sort(formals->formals.begin(), formals->formals.end(), [](const auto & a, const auto & b) {
|
||||
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
|
||||
});
|
||||
|
||||
std::optional<std::pair<Symbol, PosIdx>> duplicate;
|
||||
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
|
||||
|
|
@ -224,24 +230,22 @@ inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Sym
|
|||
duplicate = std::min(thisDup, duplicate.value_or(thisDup));
|
||||
}
|
||||
if (duplicate)
|
||||
throw ParseError({
|
||||
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
|
||||
.pos = positions[duplicate->second]
|
||||
});
|
||||
throw ParseError(
|
||||
{.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
|
||||
.pos = positions[duplicate->second]});
|
||||
|
||||
if (arg && formals->has(arg))
|
||||
throw ParseError({
|
||||
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]),
|
||||
.pos = positions[pos]
|
||||
});
|
||||
throw ParseError(
|
||||
{.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]), .pos = positions[pos]});
|
||||
|
||||
return formals;
|
||||
}
|
||||
|
||||
inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||
inline Expr *
|
||||
ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||
{
|
||||
if (es.empty()) return new ExprString("");
|
||||
if (es.empty())
|
||||
return new ExprString("");
|
||||
|
||||
/* Figure out the minimum indentation. Note that by design
|
||||
whitespace-only final lines are not taken into account. (So
|
||||
|
|
@ -255,7 +259,8 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
/* Anti-quotations and escaped characters end the current start-of-line whitespace. */
|
||||
if (atStartOfLine) {
|
||||
atStartOfLine = false;
|
||||
if (curIndent < minIndent) minIndent = curIndent;
|
||||
if (curIndent < minIndent)
|
||||
minIndent = curIndent;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -269,7 +274,8 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
curIndent = 0;
|
||||
} else {
|
||||
atStartOfLine = false;
|
||||
if (curIndent < minIndent) minIndent = curIndent;
|
||||
if (curIndent < minIndent)
|
||||
minIndent = curIndent;
|
||||
}
|
||||
} else if (str->p[j] == '\n') {
|
||||
atStartOfLine = true;
|
||||
|
|
@ -284,20 +290,19 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
size_t curDropped = 0;
|
||||
size_t n = es.size();
|
||||
auto i = es.begin();
|
||||
const auto trimExpr = [&] (Expr * e) {
|
||||
const auto trimExpr = [&](Expr * e) {
|
||||
atStartOfLine = false;
|
||||
curDropped = 0;
|
||||
es2->emplace_back(i->first, e);
|
||||
};
|
||||
const auto trimString = [&] (const StringToken & t) {
|
||||
const auto trimString = [&](const StringToken & t) {
|
||||
std::string s2;
|
||||
for (size_t j = 0; j < t.l; ++j) {
|
||||
if (atStartOfLine) {
|
||||
if (t.p[j] == ' ') {
|
||||
if (curDropped++ >= minIndent)
|
||||
s2 += t.p[j];
|
||||
}
|
||||
else if (t.p[j] == '\n') {
|
||||
} else if (t.p[j] == '\n') {
|
||||
curDropped = 0;
|
||||
s2 += t.p[j];
|
||||
} else {
|
||||
|
|
@ -307,7 +312,8 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
}
|
||||
} else {
|
||||
s2 += t.p[j];
|
||||
if (t.p[j] == '\n') atStartOfLine = true;
|
||||
if (t.p[j] == '\n')
|
||||
atStartOfLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,20 +331,20 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
}
|
||||
};
|
||||
for (; i != es.end(); ++i, --n) {
|
||||
std::visit(overloaded { trimExpr, trimString }, i->second);
|
||||
std::visit(overloaded{trimExpr, trimString}, i->second);
|
||||
}
|
||||
|
||||
// If there is nothing at all, return the empty string directly.
|
||||
// This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters.
|
||||
if (es2->size() == 0) {
|
||||
auto *const result = new ExprString("");
|
||||
auto * const result = new ExprString("");
|
||||
delete es2;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* If this is a single string, then don't do a concatenation. */
|
||||
if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) {
|
||||
auto *const result = (*es2)[0].second;
|
||||
auto * const result = (*es2)[0].second;
|
||||
delete es2;
|
||||
return result;
|
||||
}
|
||||
|
|
@ -355,4 +361,4 @@ inline PosIdx ParserState::at(const ParserLocation & loc)
|
|||
return positions.add(origin, loc.beginOffset);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@ struct RegisterPrimOp
|
|||
/**
|
||||
* Load a ValueInitializer from a DSO and return whatever it initializes
|
||||
*/
|
||||
void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
void prim_importNative(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
|
||||
/**
|
||||
* Execute a program and parse its output
|
||||
*/
|
||||
void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
void prim_exec(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
|
||||
void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ namespace nix {
|
|||
* See: https://github.com/NixOS/nix/issues/9730
|
||||
*/
|
||||
void printAmbiguous(
|
||||
Value &v,
|
||||
const SymbolTable &symbols,
|
||||
std::ostream &str,
|
||||
std::set<const void *> *seen,
|
||||
int depth);
|
||||
Value & v, const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen, int depth);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ struct PrintOptions
|
|||
* `PrintOptions` for unknown and therefore potentially large values in error messages,
|
||||
* to avoid printing "too much" output.
|
||||
*/
|
||||
static PrintOptions errorPrintOptions = PrintOptions {
|
||||
static PrintOptions errorPrintOptions = PrintOptions{
|
||||
.ansiColors = true,
|
||||
.maxDepth = 10,
|
||||
.maxAttrs = 10,
|
||||
|
|
@ -118,4 +118,4 @@ static PrintOptions errorPrintOptions = PrintOptions {
|
|||
.maxStringLength = 1024,
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -26,10 +26,14 @@ struct Value;
|
|||
* @param s The logical string
|
||||
*/
|
||||
std::ostream & printLiteralString(std::ostream & o, std::string_view s);
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const char * s) {
|
||||
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const char * s)
|
||||
{
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const std::string & s) {
|
||||
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const std::string & s)
|
||||
{
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
|
||||
|
|
@ -60,27 +64,31 @@ bool isReservedKeyword(const std::string_view str);
|
|||
*/
|
||||
std::ostream & printIdentifier(std::ostream & o, std::string_view s);
|
||||
|
||||
void printValue(EvalState & state, std::ostream & str, Value & v, PrintOptions options = PrintOptions {});
|
||||
void printValue(EvalState & state, std::ostream & str, Value & v, PrintOptions options = PrintOptions{});
|
||||
|
||||
/**
|
||||
* A partially-applied form of `printValue` which can be formatted using `<<`
|
||||
* without allocating an intermediate string.
|
||||
*/
|
||||
class ValuePrinter {
|
||||
friend std::ostream & operator << (std::ostream & output, const ValuePrinter & printer);
|
||||
class ValuePrinter
|
||||
{
|
||||
friend std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
|
||||
private:
|
||||
EvalState & state;
|
||||
Value & value;
|
||||
PrintOptions options;
|
||||
|
||||
public:
|
||||
ValuePrinter(EvalState & state, Value & value, PrintOptions options = PrintOptions {})
|
||||
: state(state), value(value), options(options) { }
|
||||
ValuePrinter(EvalState & state, Value & value, PrintOptions options = PrintOptions{})
|
||||
: state(state)
|
||||
, value(value)
|
||||
, options(options)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
|
||||
|
||||
|
||||
/**
|
||||
* `ValuePrinter` does its own ANSI formatting, so we don't color it
|
||||
* magenta.
|
||||
|
|
@ -88,4 +96,4 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
|
|||
template<>
|
||||
HintFmt & HintFmt::operator%(const ValuePrinter & value);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -17,4 +17,4 @@ enum class ReplExitStatus {
|
|||
Continue,
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -105,4 +105,4 @@ struct LookupPath::Elem
|
|||
static LookupPath::Elem parse(std::string_view rawElem);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -41,16 +41,32 @@ class Symbol
|
|||
private:
|
||||
uint32_t id;
|
||||
|
||||
explicit Symbol(uint32_t id) noexcept : id(id) {}
|
||||
explicit Symbol(uint32_t id) noexcept
|
||||
: id(id)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Symbol() noexcept : id(0) {}
|
||||
Symbol() noexcept
|
||||
: id(0)
|
||||
{
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
explicit operator bool() const noexcept { return id > 0; }
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return id > 0;
|
||||
}
|
||||
|
||||
auto operator<=>(const Symbol other) const noexcept { return id <=> other.id; }
|
||||
bool operator==(const Symbol other) const noexcept { return id == other.id; }
|
||||
auto operator<=>(const Symbol other) const noexcept
|
||||
{
|
||||
return id <=> other.id;
|
||||
}
|
||||
|
||||
bool operator==(const Symbol other) const noexcept
|
||||
{
|
||||
return id == other.id;
|
||||
}
|
||||
|
||||
friend class std::hash<Symbol>;
|
||||
};
|
||||
|
|
@ -82,11 +98,16 @@ class SymbolStr
|
|||
: store(store)
|
||||
, s(s)
|
||||
, hash(HashType{}(s))
|
||||
, alloc(stringAlloc) {}
|
||||
, alloc(stringAlloc)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
SymbolStr(const SymbolValue & s) noexcept : s(&s) {}
|
||||
SymbolStr(const SymbolValue & s) noexcept
|
||||
: s(&s)
|
||||
{
|
||||
}
|
||||
|
||||
SymbolStr(const Key & key)
|
||||
{
|
||||
|
|
@ -109,7 +130,7 @@ public:
|
|||
this->s = &v;
|
||||
}
|
||||
|
||||
bool operator == (std::string_view s2) const noexcept
|
||||
bool operator==(std::string_view s2) const noexcept
|
||||
{
|
||||
return *s == s2;
|
||||
}
|
||||
|
|
@ -120,13 +141,12 @@ public:
|
|||
return s->c_str();
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
operator std::string_view () const noexcept
|
||||
[[gnu::always_inline]] operator std::string_view() const noexcept
|
||||
{
|
||||
return *s;
|
||||
}
|
||||
|
||||
friend std::ostream & operator <<(std::ostream & os, const SymbolStr & symbol);
|
||||
friend std::ostream & operator<<(std::ostream & os, const SymbolStr & symbol);
|
||||
|
||||
[[gnu::always_inline]]
|
||||
bool empty() const noexcept
|
||||
|
|
@ -216,7 +236,8 @@ public:
|
|||
/**
|
||||
* Converts a string into a symbol.
|
||||
*/
|
||||
Symbol create(std::string_view s) {
|
||||
Symbol create(std::string_view s)
|
||||
{
|
||||
// Most symbols are looked up more than once, so we trade off insertion performance
|
||||
// for lookup performance.
|
||||
// FIXME: make this thread-safe.
|
||||
|
|
@ -255,7 +276,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
template<>
|
||||
struct std::hash<nix::Symbol>
|
||||
|
|
|
|||
|
|
@ -10,13 +10,18 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
nlohmann::json printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore = true);
|
||||
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore = true);
|
||||
nlohmann::json printValueAsJSON(
|
||||
EvalState & state, bool strict, Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore = true);
|
||||
|
||||
void printValueAsJSON(
|
||||
EvalState & state,
|
||||
bool strict,
|
||||
Value & v,
|
||||
const PosIdx pos,
|
||||
std::ostream & str,
|
||||
NixStringContext & context,
|
||||
bool copyToStore = true);
|
||||
|
||||
MakeError(JSONSerializationError, Error);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -9,7 +9,13 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
Value & v, std::ostream & out, NixStringContext & context, const PosIdx pos);
|
||||
void printValueAsXML(
|
||||
EvalState & state,
|
||||
bool strict,
|
||||
bool location,
|
||||
Value & v,
|
||||
std::ostream & out,
|
||||
NixStringContext & context,
|
||||
const PosIdx pos);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ public:
|
|||
{
|
||||
return &elems[0];
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return &elems[size];
|
||||
|
|
@ -306,7 +307,7 @@ NIX_VALUE_STORAGE_FOR_EACH_FIELD(NIX_VALUE_PAYLOAD_TYPE)
|
|||
template<typename T>
|
||||
inline constexpr InternalType payloadTypeToInternalType = PayloadTypeToInternalType<T>::value;
|
||||
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Discriminated union of types stored in the value.
|
||||
|
|
@ -865,10 +866,12 @@ public:
|
|||
{
|
||||
return isa<tThunk>();
|
||||
};
|
||||
|
||||
inline bool isApp() const
|
||||
{
|
||||
return isa<tApp>();
|
||||
};
|
||||
|
||||
inline bool isBlackhole() const;
|
||||
|
||||
// type() == nFunction
|
||||
|
|
@ -876,10 +879,12 @@ public:
|
|||
{
|
||||
return isa<tLambda>();
|
||||
};
|
||||
|
||||
inline bool isPrimOp() const
|
||||
{
|
||||
return isa<tPrimOp>();
|
||||
};
|
||||
|
||||
inline bool isPrimOpApp() const
|
||||
{
|
||||
return isa<tPrimOpApp>();
|
||||
|
|
@ -1171,4 +1176,4 @@ typedef std::shared_ptr<Value *> RootValue;
|
|||
RootValue allocRootValue(Value * v);
|
||||
|
||||
void forceNoNullByte(std::string_view s, std::function<Pos()> = nullptr);
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public:
|
|||
std::string_view raw;
|
||||
|
||||
template<typename... Args>
|
||||
BadNixStringContextElem(std::string_view raw_, const Args & ... args)
|
||||
BadNixStringContextElem(std::string_view raw_, const Args &... args)
|
||||
: Error("")
|
||||
{
|
||||
raw = raw_;
|
||||
|
|
@ -24,7 +24,8 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
struct NixStringContextElem {
|
||||
struct NixStringContextElem
|
||||
{
|
||||
/**
|
||||
* Plain opaque path to some store object.
|
||||
*
|
||||
|
|
@ -41,7 +42,8 @@ struct NixStringContextElem {
|
|||
*
|
||||
* Encoded in the form `=<drvPath>`.
|
||||
*/
|
||||
struct DrvDeep {
|
||||
struct DrvDeep
|
||||
{
|
||||
StorePath drvPath;
|
||||
|
||||
GENERATE_CMP(DrvDeep, me->drvPath);
|
||||
|
|
@ -54,11 +56,7 @@ struct NixStringContextElem {
|
|||
*/
|
||||
using Built = SingleDerivedPath::Built;
|
||||
|
||||
using Raw = std::variant<
|
||||
Opaque,
|
||||
DrvDeep,
|
||||
Built
|
||||
>;
|
||||
using Raw = std::variant<Opaque, DrvDeep, Built>;
|
||||
|
||||
Raw raw;
|
||||
|
||||
|
|
@ -74,12 +72,11 @@ struct NixStringContextElem {
|
|||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
static NixStringContextElem parse(
|
||||
std::string_view s,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
static NixStringContextElem
|
||||
parse(std::string_view s, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
std::string to_string() const;
|
||||
};
|
||||
|
||||
typedef std::set<NixStringContextElem> NixStringContext;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ namespace nix {
|
|||
|
||||
// for more information, refer to
|
||||
// https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp
|
||||
class JSONSax : nlohmann::json_sax<json> {
|
||||
class JSONState {
|
||||
class JSONSax : nlohmann::json_sax<json>
|
||||
{
|
||||
class JSONState
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<JSONState> parent;
|
||||
RootValue v;
|
||||
|
|
@ -22,22 +24,36 @@ class JSONSax : nlohmann::json_sax<json> {
|
|||
{
|
||||
throw std::logic_error("tried to close toplevel json parser state");
|
||||
}
|
||||
explicit JSONState(std::unique_ptr<JSONState> && p) : parent(std::move(p)) {}
|
||||
explicit JSONState(Value * v) : v(allocRootValue(v)) {}
|
||||
|
||||
explicit JSONState(std::unique_ptr<JSONState> && p)
|
||||
: parent(std::move(p))
|
||||
{
|
||||
}
|
||||
|
||||
explicit JSONState(Value * v)
|
||||
: v(allocRootValue(v))
|
||||
{
|
||||
}
|
||||
|
||||
JSONState(JSONState & p) = delete;
|
||||
|
||||
Value & value(EvalState & state)
|
||||
{
|
||||
if (!v)
|
||||
v = allocRootValue(state.allocValue());
|
||||
return **v;
|
||||
}
|
||||
|
||||
virtual ~JSONState() {}
|
||||
|
||||
virtual void add() {}
|
||||
};
|
||||
|
||||
class JSONObjectState : public JSONState {
|
||||
class JSONObjectState : public JSONState
|
||||
{
|
||||
using JSONState::JSONState;
|
||||
ValueMap attrs;
|
||||
|
||||
std::unique_ptr<JSONState> resolve(EvalState & state) override
|
||||
{
|
||||
auto attrs2 = state.buildBindings(attrs.size());
|
||||
|
|
@ -46,7 +62,11 @@ class JSONSax : nlohmann::json_sax<json> {
|
|||
parent->value(state).mkAttrs(attrs2);
|
||||
return std::move(parent);
|
||||
}
|
||||
void add() override { v = nullptr; }
|
||||
|
||||
void add() override
|
||||
{
|
||||
v = nullptr;
|
||||
}
|
||||
public:
|
||||
void key(string_t & name, EvalState & state)
|
||||
{
|
||||
|
|
@ -55,8 +75,10 @@ class JSONSax : nlohmann::json_sax<json> {
|
|||
}
|
||||
};
|
||||
|
||||
class JSONListState : public JSONState {
|
||||
class JSONListState : public JSONState
|
||||
{
|
||||
ValueVector values;
|
||||
|
||||
std::unique_ptr<JSONState> resolve(EvalState & state) override
|
||||
{
|
||||
auto list = state.buildList(values.size());
|
||||
|
|
@ -65,12 +87,15 @@ class JSONSax : nlohmann::json_sax<json> {
|
|||
parent->value(state).mkList(list);
|
||||
return std::move(parent);
|
||||
}
|
||||
void add() override {
|
||||
|
||||
void add() override
|
||||
{
|
||||
values.push_back(*v);
|
||||
v = nullptr;
|
||||
}
|
||||
public:
|
||||
JSONListState(std::unique_ptr<JSONState> && p, std::size_t reserve) : JSONState(std::move(p))
|
||||
JSONListState(std::unique_ptr<JSONState> && p, std::size_t reserve)
|
||||
: JSONState(std::move(p))
|
||||
{
|
||||
values.reserve(reserve);
|
||||
}
|
||||
|
|
@ -80,7 +105,9 @@ class JSONSax : nlohmann::json_sax<json> {
|
|||
std::unique_ptr<JSONState> rs;
|
||||
|
||||
public:
|
||||
JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {};
|
||||
JSONSax(EvalState & state, Value & v)
|
||||
: state(state)
|
||||
, rs(new JSONState(&v)) {};
|
||||
|
||||
bool null() override
|
||||
{
|
||||
|
|
@ -130,7 +157,7 @@ public:
|
|||
}
|
||||
|
||||
#if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8
|
||||
bool binary(binary_t&) override
|
||||
bool binary(binary_t &) override
|
||||
{
|
||||
// This function ought to be unreachable
|
||||
assert(false);
|
||||
|
|
@ -146,27 +173,30 @@ public:
|
|||
|
||||
bool key(string_t & name) override
|
||||
{
|
||||
dynamic_cast<JSONObjectState*>(rs.get())->key(name, state);
|
||||
dynamic_cast<JSONObjectState *>(rs.get())->key(name, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object() override {
|
||||
bool end_object() override
|
||||
{
|
||||
rs = rs->resolve(state);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array() override {
|
||||
bool end_array() override
|
||||
{
|
||||
return end_object();
|
||||
}
|
||||
|
||||
bool start_array(size_t len) override {
|
||||
rs = std::make_unique<JSONListState>(std::move(rs),
|
||||
len != std::numeric_limits<size_t>::max() ? len : 128);
|
||||
bool start_array(size_t len) override
|
||||
{
|
||||
rs = std::make_unique<JSONListState>(std::move(rs), len != std::numeric_limits<size_t>::max() ? len : 128);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) override {
|
||||
bool parse_error(std::size_t, const std::string &, const nlohmann::detail::exception & ex) override
|
||||
{
|
||||
throw JSONParseError("%s", ex.what());
|
||||
}
|
||||
};
|
||||
|
|
@ -179,4 +209,4 @@ void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
|
|||
throw JSONParseError("Invalid JSON Value");
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -14,4 +14,4 @@ void initLoc(YYLTYPE * loc);
|
|||
|
||||
void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len);
|
||||
|
||||
} // namespace nix::lexer
|
||||
} // namespace nix::lexer::internal
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ ExprBlackHole eBlackHole;
|
|||
|
||||
// FIXME: remove, because *symbols* are abstract and do not have a single
|
||||
// textual representation; see printIdentifier()
|
||||
std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
|
||||
std::ostream & operator<<(std::ostream & str, const SymbolStr & symbol)
|
||||
{
|
||||
std::string_view s = symbol;
|
||||
return printIdentifier(str, s);
|
||||
|
|
@ -76,7 +76,8 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
|||
{
|
||||
typedef const decltype(attrs)::value_type * Attr;
|
||||
std::vector<Attr> sorted;
|
||||
for (auto & i : attrs) sorted.push_back(&i);
|
||||
for (auto & i : attrs)
|
||||
sorted.push_back(&i);
|
||||
std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
|
||||
std::string_view sa = symbols[a->first], sb = symbols[b->first];
|
||||
return sa < sb;
|
||||
|
|
@ -102,14 +103,16 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
|||
}
|
||||
if (!inherits.empty()) {
|
||||
str << "inherit";
|
||||
for (auto sym : inherits) str << " " << symbols[sym];
|
||||
for (auto sym : inherits)
|
||||
str << " " << symbols[sym];
|
||||
str << "; ";
|
||||
}
|
||||
for (const auto & [from, syms] : inheritsFrom) {
|
||||
str << "inherit (";
|
||||
(*inheritFromExprs)[from]->show(symbols, str);
|
||||
str << ")";
|
||||
for (auto sym : syms) str << " " << symbols[sym];
|
||||
for (auto sym : syms)
|
||||
str << " " << symbols[sym];
|
||||
str << "; ";
|
||||
}
|
||||
for (auto & i : sorted) {
|
||||
|
|
@ -130,7 +133,8 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
|||
|
||||
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
if (recursive) str << "rec ";
|
||||
if (recursive)
|
||||
str << "rec ";
|
||||
str << "{ ";
|
||||
showBindings(symbols, str);
|
||||
str << "}";
|
||||
|
|
@ -157,7 +161,10 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
|
|||
// same expression being printed in two different ways depending on its
|
||||
// context. always use lexicographic ordering to avoid this.
|
||||
for (auto & i : formals->lexicographicOrder(symbols)) {
|
||||
if (first) first = false; else str << ", ";
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
str << ", ";
|
||||
str << symbols[i.name];
|
||||
if (i.def) {
|
||||
str << " ? ";
|
||||
|
|
@ -165,13 +172,16 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
|
|||
}
|
||||
}
|
||||
if (formals->ellipsis) {
|
||||
if (!first) str << ", ";
|
||||
if (!first)
|
||||
str << ", ";
|
||||
str << "...";
|
||||
}
|
||||
str << " }";
|
||||
if (arg) str << " @ ";
|
||||
if (arg)
|
||||
str << " @ ";
|
||||
}
|
||||
if (arg) str << symbols[arg];
|
||||
if (arg)
|
||||
str << symbols[arg];
|
||||
str << ": ";
|
||||
body->show(symbols, str);
|
||||
str << ")";
|
||||
|
|
@ -182,7 +192,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
|
|||
str << '(';
|
||||
fun->show(symbols, str);
|
||||
for (auto e : args) {
|
||||
str << ' ';
|
||||
str << ' ';
|
||||
e->show(symbols, str);
|
||||
}
|
||||
str << ')';
|
||||
|
|
@ -237,7 +247,10 @@ void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) co
|
|||
bool first = true;
|
||||
str << "(";
|
||||
for (auto & i : *es) {
|
||||
if (first) first = false; else str << " + ";
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
str << " + ";
|
||||
i.second->show(symbols, str);
|
||||
}
|
||||
str << ")";
|
||||
|
|
@ -248,13 +261,15 @@ void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
|
|||
str << "__curPos";
|
||||
}
|
||||
|
||||
|
||||
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
|
||||
{
|
||||
std::ostringstream out;
|
||||
bool first = true;
|
||||
for (auto & i : attrPath) {
|
||||
if (!first) out << '.'; else first = false;
|
||||
if (!first)
|
||||
out << '.';
|
||||
else
|
||||
first = false;
|
||||
if (i.symbol)
|
||||
out << symbols[i.symbol];
|
||||
else {
|
||||
|
|
@ -266,7 +281,6 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
|
|||
return out.str();
|
||||
}
|
||||
|
||||
|
||||
/* Computing levels/displacements for variables. */
|
||||
|
||||
void Expr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
|
|
@ -312,7 +326,8 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
|||
int withLevel = -1;
|
||||
for (curEnv = env.get(), level = 0; curEnv; curEnv = curEnv->up.get(), level++) {
|
||||
if (curEnv->isWith) {
|
||||
if (withLevel == -1) withLevel = level;
|
||||
if (withLevel == -1)
|
||||
withLevel = level;
|
||||
} else {
|
||||
auto i = curEnv->find(name);
|
||||
if (i != curEnv->vars.end()) {
|
||||
|
|
@ -327,10 +342,7 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
|||
enclosing `with'. If there is no `with', then we can issue an
|
||||
"undefined variable" error now. */
|
||||
if (withLevel == -1)
|
||||
es.error<UndefinedVarError>(
|
||||
"undefined variable '%1%'",
|
||||
es.symbols[name]
|
||||
).atPos(pos).debugThrow();
|
||||
es.error<UndefinedVarError>("undefined variable '%1%'", es.symbols[name]).atPos(pos).debugThrow();
|
||||
for (auto * e = env.get(); e && !fromWith; e = e->up.get())
|
||||
fromWith = e->isWith;
|
||||
this->level = withLevel;
|
||||
|
|
@ -348,7 +360,8 @@ void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
|||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
e->bindVars(es, env);
|
||||
if (def) def->bindVars(es, env);
|
||||
if (def)
|
||||
def->bindVars(es, env);
|
||||
for (auto & i : attrPath)
|
||||
if (!i.symbol)
|
||||
i.expr->bindVars(es, env);
|
||||
|
|
@ -365,8 +378,8 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticE
|
|||
i.expr->bindVars(es, env);
|
||||
}
|
||||
|
||||
std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
|
||||
EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
std::shared_ptr<const StaticEnv>
|
||||
ExprAttrs::bindInheritSources(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (!inheritFromExprs)
|
||||
return nullptr;
|
||||
|
|
@ -392,7 +405,7 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
|||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
if (recursive) {
|
||||
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs.size());
|
||||
|
||||
Displacement displ = 0;
|
||||
|
|
@ -411,8 +424,7 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
|||
i.nameExpr->bindVars(es, newEnv);
|
||||
i.valueExpr->bindVars(es, newEnv);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto inheritFromEnv = bindInheritSources(es, env);
|
||||
|
||||
for (auto & i : attrs)
|
||||
|
|
@ -439,14 +451,13 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
|||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
auto newEnv = std::make_shared<StaticEnv>(
|
||||
nullptr, env,
|
||||
(hasFormals() ? formals->formals.size() : 0) +
|
||||
(!arg ? 0 : 1));
|
||||
auto newEnv =
|
||||
std::make_shared<StaticEnv>(nullptr, env, (hasFormals() ? formals->formals.size() : 0) + (!arg ? 0 : 1));
|
||||
|
||||
Displacement displ = 0;
|
||||
|
||||
if (arg) newEnv->vars.emplace_back(arg, displ++);
|
||||
if (arg)
|
||||
newEnv->vars.emplace_back(arg, displ++);
|
||||
|
||||
if (hasFormals()) {
|
||||
for (auto & i : formals->formals)
|
||||
|
|
@ -455,7 +466,8 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
|||
newEnv->sort();
|
||||
|
||||
for (auto & i : formals->formals)
|
||||
if (i.def) i.def->bindVars(es, newEnv);
|
||||
if (i.def)
|
||||
i.def->bindVars(es, newEnv);
|
||||
}
|
||||
|
||||
body->bindVars(es, newEnv);
|
||||
|
|
@ -473,7 +485,7 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
|||
|
||||
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs.size());
|
||||
|
||||
Displacement displ = 0;
|
||||
|
|
@ -562,13 +574,9 @@ void ExprPos::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
|||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
}
|
||||
|
||||
|
||||
/* Storing function names. */
|
||||
|
||||
void Expr::setName(Symbol name)
|
||||
{
|
||||
}
|
||||
|
||||
void Expr::setName(Symbol name) {}
|
||||
|
||||
void ExprLambda::setName(Symbol name)
|
||||
{
|
||||
|
|
@ -576,16 +584,14 @@ void ExprLambda::setName(Symbol name)
|
|||
body->setName(name);
|
||||
}
|
||||
|
||||
|
||||
std::string ExprLambda::showNamePos(const EvalState & state) const
|
||||
{
|
||||
std::string id(name
|
||||
? concatStrings("'", state.symbols[name], "'")
|
||||
: "anonymous function");
|
||||
std::string id(name ? concatStrings("'", state.symbols[name], "'") : "anonymous function");
|
||||
return fmt("%1% at %2%", id, state.positions[pos]);
|
||||
}
|
||||
|
||||
void ExprLambda::setDocComment(DocComment docComment) {
|
||||
void ExprLambda::setDocComment(DocComment docComment)
|
||||
{
|
||||
// RFC 145 specifies that the innermost doc comment wins.
|
||||
// See https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md#ambiguous-placement
|
||||
if (!this->docComment) {
|
||||
|
|
@ -606,11 +612,12 @@ void ExprLambda::setDocComment(DocComment docComment) {
|
|||
size_t SymbolTable::totalSize() const
|
||||
{
|
||||
size_t n = 0;
|
||||
dump([&] (SymbolStr s) { n += s.size(); });
|
||||
dump([&](SymbolStr s) { n += s.size(); });
|
||||
return n;
|
||||
}
|
||||
|
||||
std::string DocComment::getInnerText(const PosTable & positions) const {
|
||||
std::string DocComment::getInnerText(const PosTable & positions) const
|
||||
{
|
||||
auto beginPos = positions[begin];
|
||||
auto endPos = positions[end];
|
||||
auto docCommentStr = beginPos.getSnippetUpTo(endPos).value_or("");
|
||||
|
|
@ -628,8 +635,6 @@ std::string DocComment::getInnerText(const PosTable & positions) const {
|
|||
return docStr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ‘Cursed or’ handling.
|
||||
*
|
||||
* In parser.y, every use of expr_select in a production must call one of the
|
||||
|
|
@ -647,13 +652,16 @@ void ExprCall::warnIfCursedOr(const SymbolTable & symbols, const PosTable & posi
|
|||
{
|
||||
if (cursedOrEndPos.has_value()) {
|
||||
std::ostringstream out;
|
||||
out << "at " << positions[pos] << ": "
|
||||
out << "at " << positions[pos]
|
||||
<< ": "
|
||||
"This expression uses `or` as an identifier in a way that will change in a future Nix release.\n"
|
||||
"Wrap this entire expression in parentheses to preserve its current meaning:\n"
|
||||
" (" << positions[pos].getSnippetUpTo(positions[*cursedOrEndPos]).value_or("could not read expression") << ")\n"
|
||||
" ("
|
||||
<< positions[pos].getSnippetUpTo(positions[*cursedOrEndPos]).value_or("could not read expression")
|
||||
<< ")\n"
|
||||
"Give feedback at https://github.com/NixOS/nix/pull/11121";
|
||||
warn(out.str());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -18,4 +18,4 @@ SourcePath EvalState::storePath(const StorePath & path)
|
|||
return {rootFS, CanonPath{store->printStorePath(path)}};
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -5,10 +5,11 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
|
||||
auto s = state.coerceToString(
|
||||
pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
|
||||
v.mkString(*s);
|
||||
}
|
||||
|
||||
|
|
@ -21,18 +22,17 @@ static RegisterPrimOp primop_unsafeDiscardStringContext({
|
|||
.fun = prim_unsafeDiscardStringContext,
|
||||
});
|
||||
|
||||
|
||||
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_hasContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext");
|
||||
v.mkBool(!context.empty());
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_hasContext({
|
||||
.name = "__hasContext",
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
static RegisterPrimOp primop_hasContext(
|
||||
{.name = "__hasContext",
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
Return `true` if string *s* has a non-empty context.
|
||||
The context can be obtained with
|
||||
[`getContext`](#builtins-getContext).
|
||||
|
|
@ -50,21 +50,18 @@ static RegisterPrimOp primop_hasContext({
|
|||
> else { ${name} = meta; }
|
||||
> ```
|
||||
)",
|
||||
.fun = prim_hasContext
|
||||
});
|
||||
.fun = prim_hasContext});
|
||||
|
||||
|
||||
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
|
||||
auto s = state.coerceToString(
|
||||
pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
|
||||
|
||||
NixStringContext context2;
|
||||
for (auto && c : context) {
|
||||
if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c.raw)) {
|
||||
context2.emplace(NixStringContextElem::Opaque {
|
||||
.path = ptr->drvPath
|
||||
});
|
||||
context2.emplace(NixStringContextElem::Opaque{.path = ptr->drvPath});
|
||||
} else {
|
||||
/* Can reuse original item */
|
||||
context2.emplace(std::move(c).raw);
|
||||
|
|
@ -74,10 +71,10 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
|
|||
v.mkString(*s, context2);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_unsafeDiscardOutputDependency({
|
||||
.name = "__unsafeDiscardOutputDependency",
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
static RegisterPrimOp primop_unsafeDiscardOutputDependency(
|
||||
{.name = "__unsafeDiscardOutputDependency",
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
Create a copy of the given string where every
|
||||
[derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep)
|
||||
string context element is turned into a
|
||||
|
|
@ -94,58 +91,58 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency({
|
|||
|
||||
[`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies
|
||||
)",
|
||||
.fun = prim_unsafeDiscardOutputDependency
|
||||
});
|
||||
.fun = prim_unsafeDiscardOutputDependency});
|
||||
|
||||
|
||||
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.addDrvOutputDependencies");
|
||||
auto s = state.coerceToString(
|
||||
pos, *args[0], context, "while evaluating the argument passed to builtins.addDrvOutputDependencies");
|
||||
|
||||
auto contextSize = context.size();
|
||||
auto contextSize = context.size();
|
||||
if (contextSize != 1) {
|
||||
state.error<EvalError>(
|
||||
"context of string '%s' must have exactly one element, but has %d",
|
||||
*s,
|
||||
contextSize
|
||||
).atPos(pos).debugThrow();
|
||||
state.error<EvalError>("context of string '%s' must have exactly one element, but has %d", *s, contextSize)
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
}
|
||||
NixStringContext context2 {
|
||||
(NixStringContextElem { std::visit(overloaded {
|
||||
[&](const NixStringContextElem::Opaque & c) -> NixStringContextElem::DrvDeep {
|
||||
if (!c.path.isDerivation()) {
|
||||
state.error<EvalError>(
|
||||
"path '%s' is not a derivation",
|
||||
state.store->printStorePath(c.path)
|
||||
).atPos(pos).debugThrow();
|
||||
}
|
||||
return NixStringContextElem::DrvDeep {
|
||||
.drvPath = c.path,
|
||||
};
|
||||
NixStringContext context2{
|
||||
(NixStringContextElem{std::visit(
|
||||
overloaded{
|
||||
[&](const NixStringContextElem::Opaque & c) -> NixStringContextElem::DrvDeep {
|
||||
if (!c.path.isDerivation()) {
|
||||
state.error<EvalError>("path '%s' is not a derivation", state.store->printStorePath(c.path))
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
}
|
||||
return NixStringContextElem::DrvDeep{
|
||||
.drvPath = c.path,
|
||||
};
|
||||
},
|
||||
[&](const NixStringContextElem::Built & c) -> NixStringContextElem::DrvDeep {
|
||||
state
|
||||
.error<EvalError>(
|
||||
"`addDrvOutputDependencies` can only act on derivations, not on a derivation output such as '%1%'",
|
||||
c.output)
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
},
|
||||
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
|
||||
/* Reuse original item because we want this to be idempotent. */
|
||||
/* FIXME: Suspicious move out of const. This is actually a copy, so the comment
|
||||
above does not make much sense. */
|
||||
return std::move(c);
|
||||
},
|
||||
},
|
||||
[&](const NixStringContextElem::Built & c) -> NixStringContextElem::DrvDeep {
|
||||
state.error<EvalError>(
|
||||
"`addDrvOutputDependencies` can only act on derivations, not on a derivation output such as '%1%'",
|
||||
c.output
|
||||
).atPos(pos).debugThrow();
|
||||
},
|
||||
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
|
||||
/* Reuse original item because we want this to be idempotent. */
|
||||
/* FIXME: Suspicious move out of const. This is actually a copy, so the comment
|
||||
above does not make much sense. */
|
||||
return std::move(c);
|
||||
},
|
||||
}, context.begin()->raw) }),
|
||||
context.begin()->raw)}),
|
||||
};
|
||||
|
||||
v.mkString(*s, context2);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_addDrvOutputDependencies({
|
||||
.name = "__addDrvOutputDependencies",
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
static RegisterPrimOp primop_addDrvOutputDependencies(
|
||||
{.name = "__addDrvOutputDependencies",
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
Create a copy of the given string where a single
|
||||
[constant](@docroot@/language/string-context.md#string-context-element-constant)
|
||||
string context element is turned into a
|
||||
|
|
@ -159,9 +156,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies({
|
|||
|
||||
This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency).
|
||||
)",
|
||||
.fun = prim_addDrvOutputDependencies
|
||||
});
|
||||
|
||||
.fun = prim_addDrvOutputDependencies});
|
||||
|
||||
/* Extract the context of a string as a structured Nix value.
|
||||
|
||||
|
|
@ -182,31 +177,31 @@ static RegisterPrimOp primop_addDrvOutputDependencies({
|
|||
Note that for a given path any combination of the above attributes
|
||||
may be present.
|
||||
*/
|
||||
static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_getContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
struct ContextInfo {
|
||||
struct ContextInfo
|
||||
{
|
||||
bool path = false;
|
||||
bool allOutputs = false;
|
||||
Strings outputs;
|
||||
};
|
||||
|
||||
NixStringContext context;
|
||||
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext");
|
||||
auto contextInfos = std::map<StorePath, ContextInfo>();
|
||||
for (auto && i : context) {
|
||||
std::visit(overloaded {
|
||||
[&](NixStringContextElem::DrvDeep && d) {
|
||||
contextInfos[std::move(d.drvPath)].allOutputs = true;
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](NixStringContextElem::DrvDeep && d) { contextInfos[std::move(d.drvPath)].allOutputs = true; },
|
||||
[&](NixStringContextElem::Built && b) {
|
||||
// FIXME should eventually show string context as is, no
|
||||
// resolving here.
|
||||
auto drvPath = resolveDerivedPath(*state.store, *b.drvPath);
|
||||
contextInfos[std::move(drvPath)].outputs.emplace_back(std::move(b.output));
|
||||
},
|
||||
[&](NixStringContextElem::Opaque && o) { contextInfos[std::move(o.path)].path = true; },
|
||||
},
|
||||
[&](NixStringContextElem::Built && b) {
|
||||
// FIXME should eventually show string context as is, no
|
||||
// resolving here.
|
||||
auto drvPath = resolveDerivedPath(*state.store, *b.drvPath);
|
||||
contextInfos[std::move(drvPath)].outputs.emplace_back(std::move(b.output));
|
||||
},
|
||||
[&](NixStringContextElem::Opaque && o) {
|
||||
contextInfos[std::move(o.path)].path = true;
|
||||
},
|
||||
}, ((NixStringContextElem &&) i).raw);
|
||||
((NixStringContextElem &&) i).raw);
|
||||
}
|
||||
|
||||
auto attrs = state.buildBindings(contextInfos.size());
|
||||
|
|
@ -231,10 +226,10 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
|
|||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_getContext({
|
||||
.name = "__getContext",
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
static RegisterPrimOp primop_getContext(
|
||||
{.name = "__getContext",
|
||||
.args = {"s"},
|
||||
.doc = R"(
|
||||
Return the string context of *s*.
|
||||
|
||||
The string context tracks references to derivations within a string.
|
||||
|
|
@ -253,19 +248,18 @@ static RegisterPrimOp primop_getContext({
|
|||
{ "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; }
|
||||
```
|
||||
)",
|
||||
.fun = prim_getContext
|
||||
});
|
||||
|
||||
.fun = prim_getContext});
|
||||
|
||||
/* Append the given context to a given string.
|
||||
|
||||
See the commentary above getContext for details of the
|
||||
context representation.
|
||||
*/
|
||||
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_appendContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
NixStringContext context;
|
||||
auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
|
||||
auto orig = state.forceString(
|
||||
*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
|
||||
|
||||
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext");
|
||||
|
||||
|
|
@ -274,10 +268,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
|||
for (auto & i : *args[1]->attrs()) {
|
||||
const auto & name = state.symbols[i.name];
|
||||
if (!state.store->isStorePath(name))
|
||||
state.error<EvalError>(
|
||||
"context key '%s' is not a store path",
|
||||
name
|
||||
).atPos(i.pos).debugThrow();
|
||||
state.error<EvalError>("context key '%s' is not a store path", name).atPos(i.pos).debugThrow();
|
||||
auto namePath = state.store->parseStorePath(name);
|
||||
if (!settings.readOnlyMode)
|
||||
state.store->ensurePath(namePath);
|
||||
|
|
@ -285,39 +276,46 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
|||
|
||||
if (auto attr = i.value->attrs()->get(sPath)) {
|
||||
if (state.forceBool(*attr->value, attr->pos, "while evaluating the `path` attribute of a string context"))
|
||||
context.emplace(NixStringContextElem::Opaque {
|
||||
.path = namePath,
|
||||
});
|
||||
context.emplace(
|
||||
NixStringContextElem::Opaque{
|
||||
.path = namePath,
|
||||
});
|
||||
}
|
||||
|
||||
if (auto attr = i.value->attrs()->get(sAllOutputs)) {
|
||||
if (state.forceBool(*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) {
|
||||
if (state.forceBool(
|
||||
*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) {
|
||||
if (!isDerivation(name)) {
|
||||
state.error<EvalError>(
|
||||
"tried to add all-outputs context of %s, which is not a derivation, to a string",
|
||||
name
|
||||
).atPos(i.pos).debugThrow();
|
||||
state
|
||||
.error<EvalError>(
|
||||
"tried to add all-outputs context of %s, which is not a derivation, to a string", name)
|
||||
.atPos(i.pos)
|
||||
.debugThrow();
|
||||
}
|
||||
context.emplace(NixStringContextElem::DrvDeep {
|
||||
.drvPath = namePath,
|
||||
});
|
||||
context.emplace(
|
||||
NixStringContextElem::DrvDeep{
|
||||
.drvPath = namePath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (auto attr = i.value->attrs()->get(state.sOutputs)) {
|
||||
state.forceList(*attr->value, attr->pos, "while evaluating the `outputs` attribute of a string context");
|
||||
if (attr->value->listSize() && !isDerivation(name)) {
|
||||
state.error<EvalError>(
|
||||
"tried to add derivation output context of %s, which is not a derivation, to a string",
|
||||
name
|
||||
).atPos(i.pos).debugThrow();
|
||||
state
|
||||
.error<EvalError>(
|
||||
"tried to add derivation output context of %s, which is not a derivation, to a string", name)
|
||||
.atPos(i.pos)
|
||||
.debugThrow();
|
||||
}
|
||||
for (auto elem : attr->value->listView()) {
|
||||
auto outputName = state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context");
|
||||
context.emplace(NixStringContextElem::Built {
|
||||
.drvPath = makeConstantStorePathRef(namePath),
|
||||
.output = std::string { outputName },
|
||||
});
|
||||
auto outputName =
|
||||
state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context");
|
||||
context.emplace(
|
||||
NixStringContextElem::Built{
|
||||
.drvPath = makeConstantStorePathRef(namePath),
|
||||
.output = std::string{outputName},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -325,10 +323,6 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
|||
v.mkString(orig, context);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_appendContext({
|
||||
.name = "__appendContext",
|
||||
.arity = 2,
|
||||
.fun = prim_appendContext
|
||||
});
|
||||
static RegisterPrimOp primop_appendContext({.name = "__appendContext", .arity = 2, .fun = prim_appendContext});
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -15,29 +15,35 @@ namespace nix {
|
|||
* @param toPathMaybe Path to write the rewritten path to. If empty, the error shows the actual path.
|
||||
* @param v Return `Value`
|
||||
*/
|
||||
static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, const std::optional<StorePath> & toPathMaybe, Value &v) {
|
||||
static void runFetchClosureWithRewrite(
|
||||
EvalState & state,
|
||||
const PosIdx pos,
|
||||
Store & fromStore,
|
||||
const StorePath & fromPath,
|
||||
const std::optional<StorePath> & toPathMaybe,
|
||||
Value & v)
|
||||
{
|
||||
|
||||
// establish toPath or throw
|
||||
|
||||
if (!toPathMaybe || !state.store->isValidPath(*toPathMaybe)) {
|
||||
auto rewrittenPath = makeContentAddressed(fromStore, *state.store, fromPath);
|
||||
if (toPathMaybe && *toPathMaybe != rewrittenPath)
|
||||
throw Error({
|
||||
.msg = HintFmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
|
||||
state.store->printStorePath(fromPath),
|
||||
state.store->printStorePath(rewrittenPath),
|
||||
state.store->printStorePath(*toPathMaybe)),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt(
|
||||
"rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
|
||||
state.store->printStorePath(fromPath),
|
||||
state.store->printStorePath(rewrittenPath),
|
||||
state.store->printStorePath(*toPathMaybe)),
|
||||
.pos = state.positions[pos]});
|
||||
if (!toPathMaybe)
|
||||
throw Error({
|
||||
.msg = HintFmt(
|
||||
"rewriting '%s' to content-addressed form yielded '%s'\n"
|
||||
"Use this value for the 'toPath' attribute passed to 'fetchClosure'",
|
||||
state.store->printStorePath(fromPath),
|
||||
state.store->printStorePath(rewrittenPath)),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt(
|
||||
"rewriting '%s' to content-addressed form yielded '%s'\n"
|
||||
"Use this value for the 'toPath' attribute passed to 'fetchClosure'",
|
||||
state.store->printStorePath(fromPath),
|
||||
state.store->printStorePath(rewrittenPath)),
|
||||
.pos = state.positions[pos]});
|
||||
}
|
||||
|
||||
const auto & toPath = *toPathMaybe;
|
||||
|
|
@ -49,13 +55,12 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
|
|||
if (!resultInfo->isContentAddressed(*state.store)) {
|
||||
// We don't perform the rewriting when outPath already exists, as an optimisation.
|
||||
// However, we can quickly detect a mistake if the toPath is input addressed.
|
||||
throw Error({
|
||||
.msg = HintFmt(
|
||||
"The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n"
|
||||
"Set 'toPath' to an empty string to make Nix report the correct content-addressed path.",
|
||||
state.store->printStorePath(toPath)),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt(
|
||||
"The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n"
|
||||
"Set 'toPath' to an empty string to make Nix report the correct content-addressed path.",
|
||||
state.store->printStorePath(toPath)),
|
||||
.pos = state.positions[pos]});
|
||||
}
|
||||
|
||||
state.mkStorePathString(toPath, v);
|
||||
|
|
@ -64,24 +69,25 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
|
|||
/**
|
||||
* Fetch the closure and make sure it's content addressed.
|
||||
*/
|
||||
static void runFetchClosureWithContentAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) {
|
||||
static void runFetchClosureWithContentAddressedPath(
|
||||
EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v)
|
||||
{
|
||||
|
||||
if (!state.store->isValidPath(fromPath))
|
||||
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath });
|
||||
copyClosure(fromStore, *state.store, RealisedPath::Set{fromPath});
|
||||
|
||||
auto info = state.store->queryPathInfo(fromPath);
|
||||
|
||||
if (!info->isContentAddressed(*state.store)) {
|
||||
throw Error({
|
||||
.msg = HintFmt(
|
||||
"The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n"
|
||||
"If you do intend to fetch an input-addressed store path, add\n\n"
|
||||
" inputAddressed = true;\n\n"
|
||||
"to the 'fetchClosure' arguments.\n\n"
|
||||
"Note that to ensure authenticity input-addressed store paths, users must configure a trusted binary cache public key on their systems. This is not needed for content-addressed paths.",
|
||||
state.store->printStorePath(fromPath)),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt(
|
||||
"The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n"
|
||||
"If you do intend to fetch an input-addressed store path, add\n\n"
|
||||
" inputAddressed = true;\n\n"
|
||||
"to the 'fetchClosure' arguments.\n\n"
|
||||
"Note that to ensure authenticity input-addressed store paths, users must configure a trusted binary cache public key on their systems. This is not needed for content-addressed paths.",
|
||||
state.store->printStorePath(fromPath)),
|
||||
.pos = state.positions[pos]});
|
||||
}
|
||||
|
||||
state.mkStorePathString(fromPath, v);
|
||||
|
|
@ -90,21 +96,22 @@ static void runFetchClosureWithContentAddressedPath(EvalState & state, const Pos
|
|||
/**
|
||||
* Fetch the closure and make sure it's input addressed.
|
||||
*/
|
||||
static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) {
|
||||
static void runFetchClosureWithInputAddressedPath(
|
||||
EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v)
|
||||
{
|
||||
|
||||
if (!state.store->isValidPath(fromPath))
|
||||
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath });
|
||||
copyClosure(fromStore, *state.store, RealisedPath::Set{fromPath});
|
||||
|
||||
auto info = state.store->queryPathInfo(fromPath);
|
||||
|
||||
if (info->isContentAddressed(*state.store)) {
|
||||
throw Error({
|
||||
.msg = HintFmt(
|
||||
"The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n"
|
||||
"Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed",
|
||||
state.store->printStorePath(fromPath)),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt(
|
||||
"The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n"
|
||||
"Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed",
|
||||
state.store->printStorePath(fromPath)),
|
||||
.pos = state.positions[pos]});
|
||||
}
|
||||
|
||||
state.mkStorePathString(fromPath, v);
|
||||
|
|
@ -112,7 +119,7 @@ static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosId
|
|||
|
||||
typedef std::optional<StorePath> StorePathOrGap;
|
||||
|
||||
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchClosure");
|
||||
|
||||
|
|
@ -136,67 +143,58 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
|
|||
state.forceValue(*attr.value, attr.pos);
|
||||
bool isEmptyString = attr.value->type() == nString && attr.value->string_view() == "";
|
||||
if (isEmptyString) {
|
||||
toPath = StorePathOrGap {};
|
||||
}
|
||||
else {
|
||||
toPath = StorePathOrGap{};
|
||||
} else {
|
||||
NixStringContext context;
|
||||
toPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint());
|
||||
}
|
||||
}
|
||||
|
||||
else if (attrName == "fromStore")
|
||||
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos,
|
||||
attrHint());
|
||||
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos, attrHint());
|
||||
|
||||
else if (attrName == "inputAddressed")
|
||||
inputAddressedMaybe = state.forceBool(*attr.value, attr.pos, attrHint());
|
||||
|
||||
else
|
||||
throw Error({
|
||||
.msg = HintFmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
|
||||
.pos = state.positions[pos]});
|
||||
}
|
||||
|
||||
if (!fromPath)
|
||||
throw Error({
|
||||
.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
|
||||
.pos = state.positions[pos]});
|
||||
|
||||
bool inputAddressed = inputAddressedMaybe.value_or(false);
|
||||
|
||||
if (inputAddressed) {
|
||||
if (toPath)
|
||||
throw Error({
|
||||
.msg = HintFmt("attribute '%s' is set to true, but '%s' is also set. Please remove one of them",
|
||||
"inputAddressed",
|
||||
"toPath"),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt(
|
||||
"attribute '%s' is set to true, but '%s' is also set. Please remove one of them",
|
||||
"inputAddressed",
|
||||
"toPath"),
|
||||
.pos = state.positions[pos]});
|
||||
}
|
||||
|
||||
if (!fromStoreUrl)
|
||||
throw Error({
|
||||
.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
|
||||
.pos = state.positions[pos]});
|
||||
|
||||
auto parsedURL = parseURL(*fromStoreUrl);
|
||||
|
||||
if (parsedURL.scheme != "http" &&
|
||||
parsedURL.scheme != "https" &&
|
||||
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
|
||||
throw Error({
|
||||
.msg = HintFmt("'fetchClosure' only supports http:// and https:// stores"),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
if (parsedURL.scheme != "http" && parsedURL.scheme != "https"
|
||||
&& !(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
|
||||
throw Error(
|
||||
{.msg = HintFmt("'fetchClosure' only supports http:// and https:// stores"), .pos = state.positions[pos]});
|
||||
|
||||
if (!parsedURL.query.empty())
|
||||
throw Error({
|
||||
.msg = HintFmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
|
||||
.pos = state.positions[pos]
|
||||
});
|
||||
throw Error(
|
||||
{.msg = HintFmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
|
||||
.pos = state.positions[pos]});
|
||||
|
||||
auto fromStore = openStore(parsedURL.to_string());
|
||||
|
||||
|
|
@ -284,4 +282,4 @@ static RegisterPrimOp primop_fetchClosure({
|
|||
.experimentalFeature = Xp::FetchClosure,
|
||||
});
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
std::string url;
|
||||
std::optional<Hash> rev;
|
||||
|
|
@ -23,31 +23,46 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
|||
for (auto & attr : *args[0]->attrs()) {
|
||||
std::string_view n(state.symbols[attr.name]);
|
||||
if (n == "url")
|
||||
url = state.coerceToString(attr.pos, *attr.value, context,
|
||||
"while evaluating the `url` attribute passed to builtins.fetchMercurial",
|
||||
false, false).toOwned();
|
||||
url = state
|
||||
.coerceToString(
|
||||
attr.pos,
|
||||
*attr.value,
|
||||
context,
|
||||
"while evaluating the `url` attribute passed to builtins.fetchMercurial",
|
||||
false,
|
||||
false)
|
||||
.toOwned();
|
||||
else if (n == "rev") {
|
||||
// Ugly: unlike fetchGit, here the "rev" attribute can
|
||||
// be both a revision or a branch/tag name.
|
||||
auto value = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `rev` attribute passed to builtins.fetchMercurial");
|
||||
auto value = state.forceStringNoCtx(
|
||||
*attr.value, attr.pos, "while evaluating the `rev` attribute passed to builtins.fetchMercurial");
|
||||
if (std::regex_match(value.begin(), value.end(), revRegex))
|
||||
rev = Hash::parseAny(value, HashAlgorithm::SHA1);
|
||||
else
|
||||
ref = value;
|
||||
}
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial");
|
||||
} else if (n == "name")
|
||||
name = state.forceStringNoCtx(
|
||||
*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial");
|
||||
else
|
||||
state.error<EvalError>("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]).atPos(attr.pos).debugThrow();
|
||||
state.error<EvalError>("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name])
|
||||
.atPos(attr.pos)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
state.error<EvalError>("'url' argument required").atPos(pos).debugThrow();
|
||||
|
||||
} else
|
||||
url = state.coerceToString(pos, *args[0], context,
|
||||
"while evaluating the first argument passed to builtins.fetchMercurial",
|
||||
false, false).toOwned();
|
||||
url = state
|
||||
.coerceToString(
|
||||
pos,
|
||||
*args[0],
|
||||
context,
|
||||
"while evaluating the first argument passed to builtins.fetchMercurial",
|
||||
false,
|
||||
false)
|
||||
.toOwned();
|
||||
|
||||
// FIXME: git externals probably can be used to bypass the URI
|
||||
// whitelist. Ah well.
|
||||
|
|
@ -60,8 +75,10 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
|||
attrs.insert_or_assign("type", "hg");
|
||||
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
|
||||
attrs.insert_or_assign("name", std::string(name));
|
||||
if (ref) attrs.insert_or_assign("ref", *ref);
|
||||
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
|
||||
if (ref)
|
||||
attrs.insert_or_assign("ref", *ref);
|
||||
if (rev)
|
||||
attrs.insert_or_assign("rev", rev->gitRev());
|
||||
auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
|
||||
|
||||
auto [storePath, input2] = input.fetchToStore(state.store);
|
||||
|
|
@ -82,10 +99,6 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
|||
state.allowPath(storePath);
|
||||
}
|
||||
|
||||
static RegisterPrimOp r_fetchMercurial({
|
||||
.name = "fetchMercurial",
|
||||
.arity = 1,
|
||||
.fun = prim_fetchMercurial
|
||||
});
|
||||
static RegisterPrimOp r_fetchMercurial({.name = "fetchMercurial", .arity = 1, .fun = prim_fetchMercurial});
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@ void emitTreeAttrs(
|
|||
attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true));
|
||||
|
||||
if (input.getType() == "git")
|
||||
attrs.alloc("submodules").mkBool(
|
||||
fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
|
||||
attrs.alloc("submodules").mkBool(fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
|
||||
|
||||
if (!forceDirty) {
|
||||
|
||||
|
|
@ -56,7 +55,6 @@ void emitTreeAttrs(
|
|||
attrs.alloc("revCount").mkInt(*revCount);
|
||||
else if (emptyRevFallback)
|
||||
attrs.alloc("revCount").mkInt(0);
|
||||
|
||||
}
|
||||
|
||||
if (auto dirtyRev = fetchers::maybeGetStrAttr(input.attrs, "dirtyRev")) {
|
||||
|
|
@ -66,14 +64,14 @@ void emitTreeAttrs(
|
|||
|
||||
if (auto lastModified = input.getLastModified()) {
|
||||
attrs.alloc("lastModified").mkInt(*lastModified);
|
||||
attrs.alloc("lastModifiedDate").mkString(
|
||||
fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
|
||||
attrs.alloc("lastModifiedDate").mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
||||
struct FetchTreeParams {
|
||||
struct FetchTreeParams
|
||||
{
|
||||
bool emptyRevFallback = false;
|
||||
bool allowNameArgument = false;
|
||||
bool isFetchGit = false;
|
||||
|
|
@ -81,17 +79,14 @@ struct FetchTreeParams {
|
|||
};
|
||||
|
||||
static void fetchTree(
|
||||
EvalState & state,
|
||||
const PosIdx pos,
|
||||
Value * * args,
|
||||
Value & v,
|
||||
const FetchTreeParams & params = FetchTreeParams{}
|
||||
) {
|
||||
fetchers::Input input { state.fetchSettings };
|
||||
EvalState & state, const PosIdx pos, Value ** args, Value & v, const FetchTreeParams & params = FetchTreeParams{})
|
||||
{
|
||||
fetchers::Input input{state.fetchSettings};
|
||||
NixStringContext context;
|
||||
std::optional<std::string> type;
|
||||
auto fetcher = params.isFetchGit ? "fetchGit" : "fetchTree";
|
||||
if (params.isFetchGit) type = "git";
|
||||
if (params.isFetchGit)
|
||||
type = "git";
|
||||
|
||||
state.forceValue(*args[0], pos);
|
||||
|
||||
|
|
@ -102,47 +97,55 @@ static void fetchTree(
|
|||
|
||||
if (auto aType = args[0]->attrs()->get(state.sType)) {
|
||||
if (type)
|
||||
state.error<EvalError>(
|
||||
"unexpected argument 'type'"
|
||||
).atPos(pos).debugThrow();
|
||||
type = state.forceStringNoCtx(*aType->value, aType->pos,
|
||||
fmt("while evaluating the `type` argument passed to '%s'", fetcher));
|
||||
state.error<EvalError>("unexpected argument 'type'").atPos(pos).debugThrow();
|
||||
type = state.forceStringNoCtx(
|
||||
*aType->value, aType->pos, fmt("while evaluating the `type` argument passed to '%s'", fetcher));
|
||||
} else if (!type)
|
||||
state.error<EvalError>(
|
||||
"argument 'type' is missing in call to '%s'", fetcher
|
||||
).atPos(pos).debugThrow();
|
||||
state.error<EvalError>("argument 'type' is missing in call to '%s'", fetcher).atPos(pos).debugThrow();
|
||||
|
||||
attrs.emplace("type", type.value());
|
||||
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
if (attr.name == state.sType) continue;
|
||||
if (attr.name == state.sType)
|
||||
continue;
|
||||
state.forceValue(*attr.value, attr.pos);
|
||||
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
||||
auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned();
|
||||
attrs.emplace(state.symbols[attr.name],
|
||||
params.isFetchGit && state.symbols[attr.name] == "url"
|
||||
? fixGitURL(s)
|
||||
: s);
|
||||
}
|
||||
else if (attr.value->type() == nBool)
|
||||
attrs.emplace(
|
||||
state.symbols[attr.name],
|
||||
params.isFetchGit && state.symbols[attr.name] == "url" ? fixGitURL(s) : s);
|
||||
} else if (attr.value->type() == nBool)
|
||||
attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean()});
|
||||
else if (attr.value->type() == nInt) {
|
||||
auto intValue = attr.value->integer().value;
|
||||
|
||||
if (intValue < 0)
|
||||
state.error<EvalError>("negative value given for '%s' argument '%s': %d", fetcher, state.symbols[attr.name], intValue).atPos(pos).debugThrow();
|
||||
state
|
||||
.error<EvalError>(
|
||||
"negative value given for '%s' argument '%s': %d",
|
||||
fetcher,
|
||||
state.symbols[attr.name],
|
||||
intValue)
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
|
||||
attrs.emplace(state.symbols[attr.name], uint64_t(intValue));
|
||||
} else if (state.symbols[attr.name] == "publicKeys") {
|
||||
experimentalFeatureSettings.require(Xp::VerifiedFetches);
|
||||
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
|
||||
}
|
||||
else
|
||||
state.error<TypeError>("argument '%s' to '%s' is %s while a string, Boolean or integer is expected",
|
||||
state.symbols[attr.name], fetcher, showType(*attr.value)).debugThrow();
|
||||
attrs.emplace(
|
||||
state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
|
||||
} else
|
||||
state
|
||||
.error<TypeError>(
|
||||
"argument '%s' to '%s' is %s while a string, Boolean or integer is expected",
|
||||
state.symbols[attr.name],
|
||||
fetcher,
|
||||
showType(*attr.value))
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
if (params.isFetchGit && !attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
|
||||
if (params.isFetchGit && !attrs.contains("exportIgnore")
|
||||
&& (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
|
||||
attrs.emplace("exportIgnore", Explicit<bool>{true});
|
||||
}
|
||||
|
||||
|
|
@ -153,29 +156,38 @@ static void fetchTree(
|
|||
|
||||
if (!params.allowNameArgument)
|
||||
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
||||
state.error<EvalError>(
|
||||
"argument 'name' isn’t supported in call to '%s'", fetcher
|
||||
).atPos(pos).debugThrow();
|
||||
state.error<EvalError>("argument 'name' isn’t supported in call to '%s'", fetcher)
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
|
||||
input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
|
||||
} else {
|
||||
auto url = state.coerceToString(pos, *args[0], context,
|
||||
fmt("while evaluating the first argument passed to '%s'", fetcher),
|
||||
false, false).toOwned();
|
||||
auto url = state
|
||||
.coerceToString(
|
||||
pos,
|
||||
*args[0],
|
||||
context,
|
||||
fmt("while evaluating the first argument passed to '%s'", fetcher),
|
||||
false,
|
||||
false)
|
||||
.toOwned();
|
||||
|
||||
if (params.isFetchGit) {
|
||||
fetchers::Attrs attrs;
|
||||
attrs.emplace("type", "git");
|
||||
attrs.emplace("url", fixGitURL(url));
|
||||
if (!attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
|
||||
if (!attrs.contains("exportIgnore")
|
||||
&& (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
|
||||
attrs.emplace("exportIgnore", Explicit<bool>{true});
|
||||
}
|
||||
input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
|
||||
} else {
|
||||
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
|
||||
state.error<EvalError>(
|
||||
"passing a string argument to '%s' requires the 'flakes' experimental feature", fetcher
|
||||
).atPos(pos).debugThrow();
|
||||
state
|
||||
.error<EvalError>(
|
||||
"passing a string argument to '%s' requires the 'flakes' experimental feature", fetcher)
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
input = fetchers::Input::fromURL(state.fetchSettings, url);
|
||||
}
|
||||
}
|
||||
|
|
@ -190,9 +202,11 @@ static void fetchTree(
|
|||
"This is deprecated since such inputs are verifiable but may not be reproducible.",
|
||||
input.to_string());
|
||||
else
|
||||
state.error<EvalError>(
|
||||
"in pure evaluation mode, '%s' doesn't fetch unlocked input '%s'",
|
||||
fetcher, input.to_string()).atPos(pos).debugThrow();
|
||||
state
|
||||
.error<EvalError>(
|
||||
"in pure evaluation mode, '%s' doesn't fetch unlocked input '%s'", fetcher, input.to_string())
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
state.checkURI(input.toURLString());
|
||||
|
|
@ -211,9 +225,9 @@ static void fetchTree(
|
|||
emitTreeAttrs(state, storePath, input2, v, params.emptyRevFallback, false);
|
||||
}
|
||||
|
||||
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
fetchTree(state, pos, args, v, { });
|
||||
fetchTree(state, pos, args, v, {});
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_fetchTree({
|
||||
|
|
@ -446,7 +460,7 @@ static RegisterPrimOp primop_fetchTree({
|
|||
.experimentalFeature = Xp::FetchTree,
|
||||
});
|
||||
|
||||
void prim_fetchFinalTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
void prim_fetchFinalTree(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
fetchTree(state, pos, args, v, {.isFinal = true});
|
||||
}
|
||||
|
|
@ -458,8 +472,14 @@ static RegisterPrimOp primop_fetchFinalTree({
|
|||
.internal = true,
|
||||
});
|
||||
|
||||
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
|
||||
const std::string & who, bool unpack, std::string name)
|
||||
static void fetch(
|
||||
EvalState & state,
|
||||
const PosIdx pos,
|
||||
Value ** args,
|
||||
Value & v,
|
||||
const std::string & who,
|
||||
bool unpack,
|
||||
std::string name)
|
||||
{
|
||||
std::optional<std::string> url;
|
||||
std::optional<Hash> expectedHash;
|
||||
|
|
@ -476,19 +496,20 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
|||
if (n == "url")
|
||||
url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch");
|
||||
else if (n == "sha256")
|
||||
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"), HashAlgorithm::SHA256);
|
||||
expectedHash = newHashAllowEmpty(
|
||||
state.forceStringNoCtx(
|
||||
*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"),
|
||||
HashAlgorithm::SHA256);
|
||||
else if (n == "name") {
|
||||
nameAttrPassed = true;
|
||||
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch");
|
||||
}
|
||||
else
|
||||
state.error<EvalError>("unsupported argument '%s' to '%s'", n, who)
|
||||
.atPos(pos).debugThrow();
|
||||
name = state.forceStringNoCtx(
|
||||
*attr.value, attr.pos, "while evaluating the name of the content we should fetch");
|
||||
} else
|
||||
state.error<EvalError>("unsupported argument '%s' to '%s'", n, who).atPos(pos).debugThrow();
|
||||
}
|
||||
|
||||
if (!url)
|
||||
state.error<EvalError>(
|
||||
"'url' argument required").atPos(pos).debugThrow();
|
||||
state.error<EvalError>("'url' argument required").atPos(pos).debugThrow();
|
||||
} else
|
||||
url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch");
|
||||
|
||||
|
|
@ -504,27 +525,41 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
|||
checkName(name);
|
||||
} catch (BadStorePathName & e) {
|
||||
auto resolution =
|
||||
nameAttrPassed ? HintFmt("Please change the value for the 'name' attribute passed to '%s', so that it can create a valid store path.", who) :
|
||||
isArgAttrs ? HintFmt("Please add a valid 'name' attribute to the argument for '%s', so that it can create a valid store path.", who) :
|
||||
HintFmt("Please pass an attribute set with 'url' and 'name' attributes to '%s', so that it can create a valid store path.", who);
|
||||
nameAttrPassed
|
||||
? HintFmt(
|
||||
"Please change the value for the 'name' attribute passed to '%s', so that it can create a valid store path.",
|
||||
who)
|
||||
: isArgAttrs
|
||||
? HintFmt(
|
||||
"Please add a valid 'name' attribute to the argument for '%s', so that it can create a valid store path.",
|
||||
who)
|
||||
: HintFmt(
|
||||
"Please pass an attribute set with 'url' and 'name' attributes to '%s', so that it can create a valid store path.",
|
||||
who);
|
||||
|
||||
state.error<EvalError>(
|
||||
std::string("invalid store path name when fetching URL '%s': %s. %s"), *url, Uncolored(e.message()), Uncolored(resolution.str()))
|
||||
.atPos(pos).debugThrow();
|
||||
state
|
||||
.error<EvalError>(
|
||||
std::string("invalid store path name when fetching URL '%s': %s. %s"),
|
||||
*url,
|
||||
Uncolored(e.message()),
|
||||
Uncolored(resolution.str()))
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
if (state.settings.pureEval && !expectedHash)
|
||||
state.error<EvalError>("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow();
|
||||
state.error<EvalError>("in pure evaluation mode, '%s' requires a 'sha256' argument", who)
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
|
||||
// early exit if pinned and already in the store
|
||||
if (expectedHash && expectedHash->algo == HashAlgorithm::SHA256) {
|
||||
auto expectedPath = state.store->makeFixedOutputPath(
|
||||
name,
|
||||
FixedOutputInfo {
|
||||
FixedOutputInfo{
|
||||
.method = unpack ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat,
|
||||
.hash = *expectedHash,
|
||||
.references = {}
|
||||
});
|
||||
.references = {}});
|
||||
|
||||
if (state.store->isValidPath(expectedPath)) {
|
||||
state.allowAndSetStorePathString(expectedPath, v);
|
||||
|
|
@ -534,35 +569,33 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
|||
|
||||
// TODO: fetching may fail, yet the path may be substitutable.
|
||||
// https://github.com/NixOS/nix/issues/4313
|
||||
auto storePath =
|
||||
unpack
|
||||
? fetchToStore(
|
||||
state.fetchSettings,
|
||||
*state.store,
|
||||
fetchers::downloadTarball(state.store, state.fetchSettings, *url),
|
||||
FetchMode::Copy,
|
||||
name)
|
||||
: fetchers::downloadFile(state.store, state.fetchSettings, *url, name).storePath;
|
||||
auto storePath = unpack ? fetchToStore(
|
||||
state.fetchSettings,
|
||||
*state.store,
|
||||
fetchers::downloadTarball(state.store, state.fetchSettings, *url),
|
||||
FetchMode::Copy,
|
||||
name)
|
||||
: fetchers::downloadFile(state.store, state.fetchSettings, *url, name).storePath;
|
||||
|
||||
if (expectedHash) {
|
||||
auto hash = unpack
|
||||
? state.store->queryPathInfo(storePath)->narHash
|
||||
: hashFile(HashAlgorithm::SHA256, state.store->toRealPath(storePath));
|
||||
auto hash = unpack ? state.store->queryPathInfo(storePath)->narHash
|
||||
: hashFile(HashAlgorithm::SHA256, state.store->toRealPath(storePath));
|
||||
if (hash != *expectedHash) {
|
||||
state.error<EvalError>(
|
||||
"hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
|
||||
*url,
|
||||
expectedHash->to_string(HashFormat::Nix32, true),
|
||||
hash.to_string(HashFormat::Nix32, true)
|
||||
).withExitStatus(102)
|
||||
.debugThrow();
|
||||
state
|
||||
.error<EvalError>(
|
||||
"hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
|
||||
*url,
|
||||
expectedHash->to_string(HashFormat::Nix32, true),
|
||||
hash.to_string(HashFormat::Nix32, true))
|
||||
.withExitStatus(102)
|
||||
.debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
state.allowAndSetStorePathString(storePath, v);
|
||||
}
|
||||
|
||||
static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_fetchurl(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
fetch(state, pos, args, v, "fetchurl", false, "");
|
||||
}
|
||||
|
|
@ -588,7 +621,7 @@ static RegisterPrimOp primop_fetchurl({
|
|||
.fun = prim_fetchurl,
|
||||
});
|
||||
|
||||
static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
fetch(state, pos, args, v, "fetchTarball", true, "source");
|
||||
}
|
||||
|
|
@ -638,14 +671,10 @@ static RegisterPrimOp primop_fetchTarball({
|
|||
.fun = prim_fetchTarball,
|
||||
});
|
||||
|
||||
static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
static void prim_fetchGit(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||
{
|
||||
fetchTree(state, pos, args, v,
|
||||
FetchTreeParams {
|
||||
.emptyRevFallback = true,
|
||||
.allowNameArgument = true,
|
||||
.isFetchGit = true
|
||||
});
|
||||
fetchTree(
|
||||
state, pos, args, v, FetchTreeParams{.emptyRevFallback = true, .allowNameArgument = true, .isFetchGit = true});
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_fetchGit({
|
||||
|
|
@ -858,4 +887,4 @@ static RegisterPrimOp primop_fetchGit({
|
|||
.fun = prim_fetchGit,
|
||||
});
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
|
||||
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Value & val)
|
||||
{
|
||||
auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML");
|
||||
|
||||
|
|
@ -16,75 +16,75 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
|
|||
std::function<void(Value &, toml::value)> visit;
|
||||
|
||||
visit = [&](Value & v, toml::value t) {
|
||||
switch (t.type()) {
|
||||
case toml::value_t::table: {
|
||||
auto table = toml::get<toml::table>(t);
|
||||
|
||||
switch(t.type())
|
||||
{
|
||||
case toml::value_t::table:
|
||||
{
|
||||
auto table = toml::get<toml::table>(t);
|
||||
size_t size = 0;
|
||||
for (auto & i : table) {
|
||||
(void) i;
|
||||
size++;
|
||||
}
|
||||
|
||||
size_t size = 0;
|
||||
for (auto & i : table) { (void) i; size++; }
|
||||
auto attrs = state.buildBindings(size);
|
||||
|
||||
auto attrs = state.buildBindings(size);
|
||||
for (auto & elem : table) {
|
||||
forceNoNullByte(elem.first);
|
||||
visit(attrs.alloc(elem.first), elem.second);
|
||||
}
|
||||
|
||||
for(auto & elem : table) {
|
||||
forceNoNullByte(elem.first);
|
||||
visit(attrs.alloc(elem.first), elem.second);
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
break;;
|
||||
case toml::value_t::array:
|
||||
{
|
||||
auto array = toml::get<std::vector<toml::value>>(t);
|
||||
|
||||
auto list = state.buildList(array.size());
|
||||
for (const auto & [n, v] : enumerate(list))
|
||||
visit(*(v = state.allocValue()), array[n]);
|
||||
v.mkList(list);
|
||||
}
|
||||
break;;
|
||||
case toml::value_t::boolean:
|
||||
v.mkBool(toml::get<bool>(t));
|
||||
break;;
|
||||
case toml::value_t::integer:
|
||||
v.mkInt(toml::get<int64_t>(t));
|
||||
break;;
|
||||
case toml::value_t::floating:
|
||||
v.mkFloat(toml::get<NixFloat>(t));
|
||||
break;;
|
||||
case toml::value_t::string:
|
||||
{
|
||||
auto s = toml::get<std::string_view>(t);
|
||||
forceNoNullByte(s);
|
||||
v.mkString(s);
|
||||
}
|
||||
break;;
|
||||
case toml::value_t::local_datetime:
|
||||
case toml::value_t::offset_datetime:
|
||||
case toml::value_t::local_date:
|
||||
case toml::value_t::local_time:
|
||||
{
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
|
||||
auto attrs = state.buildBindings(2);
|
||||
attrs.alloc("_type").mkString("timestamp");
|
||||
std::ostringstream s;
|
||||
s << t;
|
||||
auto str = toView(s);
|
||||
forceNoNullByte(str);
|
||||
attrs.alloc("value").mkString(str);
|
||||
v.mkAttrs(attrs);
|
||||
} else {
|
||||
throw std::runtime_error("Dates and times are not supported");
|
||||
}
|
||||
}
|
||||
break;;
|
||||
case toml::value_t::empty:
|
||||
v.mkNull();
|
||||
break;;
|
||||
v.mkAttrs(attrs);
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::array: {
|
||||
auto array = toml::get<std::vector<toml::value>>(t);
|
||||
|
||||
auto list = state.buildList(array.size());
|
||||
for (const auto & [n, v] : enumerate(list))
|
||||
visit(*(v = state.allocValue()), array[n]);
|
||||
v.mkList(list);
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::boolean:
|
||||
v.mkBool(toml::get<bool>(t));
|
||||
break;
|
||||
;
|
||||
case toml::value_t::integer:
|
||||
v.mkInt(toml::get<int64_t>(t));
|
||||
break;
|
||||
;
|
||||
case toml::value_t::floating:
|
||||
v.mkFloat(toml::get<NixFloat>(t));
|
||||
break;
|
||||
;
|
||||
case toml::value_t::string: {
|
||||
auto s = toml::get<std::string_view>(t);
|
||||
forceNoNullByte(s);
|
||||
v.mkString(s);
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::local_datetime:
|
||||
case toml::value_t::offset_datetime:
|
||||
case toml::value_t::local_date:
|
||||
case toml::value_t::local_time: {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
|
||||
auto attrs = state.buildBindings(2);
|
||||
attrs.alloc("_type").mkString("timestamp");
|
||||
std::ostringstream s;
|
||||
s << t;
|
||||
auto str = toView(s);
|
||||
forceNoNullByte(str);
|
||||
attrs.alloc("value").mkString(str);
|
||||
v.mkAttrs(attrs);
|
||||
} else {
|
||||
throw std::runtime_error("Dates and times are not supported");
|
||||
}
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::empty:
|
||||
v.mkNull();
|
||||
break;
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -95,10 +95,10 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
|
|||
}
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_fromTOML({
|
||||
.name = "fromTOML",
|
||||
.args = {"e"},
|
||||
.doc = R"(
|
||||
static RegisterPrimOp primop_fromTOML(
|
||||
{.name = "fromTOML",
|
||||
.args = {"e"},
|
||||
.doc = R"(
|
||||
Convert a TOML string to a Nix value. For example,
|
||||
|
||||
```nix
|
||||
|
|
@ -112,7 +112,6 @@ static RegisterPrimOp primop_fromTOML({
|
|||
|
||||
returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`.
|
||||
)",
|
||||
.fun = prim_fromTOML
|
||||
});
|
||||
.fun = prim_fromTOML});
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ namespace nix {
|
|||
|
||||
// See: https://github.com/NixOS/nix/issues/9730
|
||||
void printAmbiguous(
|
||||
Value &v,
|
||||
const SymbolTable &symbols,
|
||||
std::ostream &str,
|
||||
std::set<const void *> *seen,
|
||||
int depth)
|
||||
Value & v, const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen, int depth)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
|
|
@ -100,4 +96,4 @@ void printAmbiguous(
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -28,9 +28,7 @@ void printElided(
|
|||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
|
||||
std::ostream & printLiteralString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
|
||||
{
|
||||
size_t charsPrinted = 0;
|
||||
if (ansiColors)
|
||||
|
|
@ -43,12 +41,18 @@ printLiteralString(std::ostream & str, const std::string_view string, size_t max
|
|||
return str;
|
||||
}
|
||||
|
||||
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
||||
else if (*i == '\n') str << "\\n";
|
||||
else if (*i == '\r') str << "\\r";
|
||||
else if (*i == '\t') str << "\\t";
|
||||
else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
|
||||
else str << *i;
|
||||
if (*i == '\"' || *i == '\\')
|
||||
str << "\\" << *i;
|
||||
else if (*i == '\n')
|
||||
str << "\\n";
|
||||
else if (*i == '\r')
|
||||
str << "\\r";
|
||||
else if (*i == '\t')
|
||||
str << "\\t";
|
||||
else if (*i == '$' && *(i + 1) == '{')
|
||||
str << "\\" << *i;
|
||||
else
|
||||
str << *i;
|
||||
charsPrinted++;
|
||||
}
|
||||
str << "\"";
|
||||
|
|
@ -57,14 +61,12 @@ printLiteralString(std::ostream & str, const std::string_view string, size_t max
|
|||
return str;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string)
|
||||
std::ostream & printLiteralString(std::ostream & str, const std::string_view string)
|
||||
{
|
||||
return printLiteralString(str, string, std::numeric_limits<size_t>::max(), false);
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printLiteralBool(std::ostream & str, bool boolean)
|
||||
std::ostream & printLiteralBool(std::ostream & str, bool boolean)
|
||||
{
|
||||
str << (boolean ? "true" : "false");
|
||||
return str;
|
||||
|
|
@ -80,13 +82,12 @@ printLiteralBool(std::ostream & str, bool boolean)
|
|||
bool isReservedKeyword(const std::string_view str)
|
||||
{
|
||||
static const std::unordered_set<std::string_view> reservedKeywords = {
|
||||
"if", "then", "else", "assert", "with", "let", "in", "rec", "inherit"
|
||||
};
|
||||
"if", "then", "else", "assert", "with", "let", "in", "rec", "inherit"};
|
||||
return reservedKeywords.contains(str);
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printIdentifier(std::ostream & str, std::string_view s) {
|
||||
std::ostream & printIdentifier(std::ostream & str, std::string_view s)
|
||||
{
|
||||
if (s.empty())
|
||||
str << "\"\"";
|
||||
else if (isReservedKeyword(s))
|
||||
|
|
@ -98,10 +99,8 @@ printIdentifier(std::ostream & str, std::string_view s) {
|
|||
return str;
|
||||
}
|
||||
for (auto c : s)
|
||||
if (!((c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '_' || c == '\'' || c == '-')) {
|
||||
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '\''
|
||||
|| c == '-')) {
|
||||
printLiteralString(str, s);
|
||||
return str;
|
||||
}
|
||||
|
|
@ -112,21 +111,22 @@ printIdentifier(std::ostream & str, std::string_view s) {
|
|||
|
||||
static bool isVarName(std::string_view s)
|
||||
{
|
||||
if (s.size() == 0) return false;
|
||||
if (isReservedKeyword(s)) return false;
|
||||
if (s.size() == 0)
|
||||
return false;
|
||||
if (isReservedKeyword(s))
|
||||
return false;
|
||||
char c = s[0];
|
||||
if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false;
|
||||
if ((c >= '0' && c <= '9') || c == '-' || c == '\'')
|
||||
return false;
|
||||
for (auto & i : s)
|
||||
if (!((i >= 'a' && i <= 'z') ||
|
||||
(i >= 'A' && i <= 'Z') ||
|
||||
(i >= '0' && i <= '9') ||
|
||||
i == '_' || i == '-' || i == '\''))
|
||||
if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-'
|
||||
|| i == '\''))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printAttributeName(std::ostream & str, std::string_view name) {
|
||||
std::ostream & printAttributeName(std::ostream & str, std::string_view name)
|
||||
{
|
||||
if (isVarName(name))
|
||||
str << name;
|
||||
else
|
||||
|
|
@ -134,7 +134,7 @@ printAttributeName(std::ostream & str, std::string_view name) {
|
|||
return str;
|
||||
}
|
||||
|
||||
bool isImportantAttrName(const std::string& attrName)
|
||||
bool isImportantAttrName(const std::string & attrName)
|
||||
{
|
||||
return attrName == "type" || attrName == "_type";
|
||||
}
|
||||
|
|
@ -144,12 +144,11 @@ typedef std::pair<std::string, Value *> AttrPair;
|
|||
struct ImportantFirstAttrNameCmp
|
||||
{
|
||||
|
||||
bool operator()(const AttrPair& lhs, const AttrPair& rhs) const
|
||||
bool operator()(const AttrPair & lhs, const AttrPair & rhs) const
|
||||
{
|
||||
auto lhsIsImportant = isImportantAttrName(lhs.first);
|
||||
auto rhsIsImportant = isImportantAttrName(rhs.first);
|
||||
return std::forward_as_tuple(!lhsIsImportant, lhs.first)
|
||||
< std::forward_as_tuple(!rhsIsImportant, rhs.first);
|
||||
return std::forward_as_tuple(!lhsIsImportant, lhs.first) < std::forward_as_tuple(!rhsIsImportant, rhs.first);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -275,7 +274,8 @@ private:
|
|||
std::optional<StorePath> storePath;
|
||||
if (auto i = v.attrs()->get(state.sDrvPath)) {
|
||||
NixStringContext context;
|
||||
storePath = state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation");
|
||||
storePath =
|
||||
state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation");
|
||||
}
|
||||
|
||||
/* This unfortunately breaks printing nested values because of
|
||||
|
|
@ -499,10 +499,10 @@ private:
|
|||
output << ANSI_NORMAL;
|
||||
} else if (v.isThunk() || v.isApp()) {
|
||||
if (options.ansiColors)
|
||||
output << ANSI_MAGENTA;
|
||||
output << ANSI_MAGENTA;
|
||||
output << "«thunk»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
output << ANSI_NORMAL;
|
||||
} else {
|
||||
unreachable();
|
||||
}
|
||||
|
|
@ -593,8 +593,7 @@ private:
|
|||
}
|
||||
} catch (Error & e) {
|
||||
if (options.errors == ErrorPrintBehavior::Throw
|
||||
|| (options.errors == ErrorPrintBehavior::ThrowTopLevel
|
||||
&& depth == 0)) {
|
||||
|| (options.errors == ErrorPrintBehavior::ThrowTopLevel && depth == 0)) {
|
||||
throw;
|
||||
}
|
||||
printError_(e);
|
||||
|
|
@ -603,7 +602,11 @@ private:
|
|||
|
||||
public:
|
||||
Printer(std::ostream & output, EvalState & state, PrintOptions options)
|
||||
: output(output), state(state), options(options) { }
|
||||
: output(output)
|
||||
, state(state)
|
||||
, options(options)
|
||||
{
|
||||
}
|
||||
|
||||
void print(Value & v)
|
||||
{
|
||||
|
|
@ -636,8 +639,8 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer)
|
|||
template<>
|
||||
HintFmt & HintFmt::operator%(const ValuePrinter & value)
|
||||
{
|
||||
fmt % value;
|
||||
return *this;
|
||||
fmt % value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
std::optional<std::string_view> LookupPath::Prefix::suffixIfPotentialMatch(
|
||||
std::string_view path) const
|
||||
std::optional<std::string_view> LookupPath::Prefix::suffixIfPotentialMatch(std::string_view path) const
|
||||
{
|
||||
auto n = s.size();
|
||||
|
||||
|
|
@ -21,29 +20,25 @@ std::optional<std::string_view> LookupPath::Prefix::suffixIfPotentialMatch(
|
|||
}
|
||||
|
||||
/* Skip next path separator. */
|
||||
return {
|
||||
path.substr(needSeparator ? n + 1 : n)
|
||||
};
|
||||
return {path.substr(needSeparator ? n + 1 : n)};
|
||||
}
|
||||
|
||||
|
||||
LookupPath::Elem LookupPath::Elem::parse(std::string_view rawElem)
|
||||
{
|
||||
size_t pos = rawElem.find('=');
|
||||
|
||||
return LookupPath::Elem {
|
||||
.prefix = Prefix {
|
||||
.s = pos == std::string::npos
|
||||
? std::string { "" }
|
||||
: std::string { rawElem.substr(0, pos) },
|
||||
},
|
||||
.path = Path {
|
||||
.s = std::string { rawElem.substr(pos + 1) },
|
||||
},
|
||||
return LookupPath::Elem{
|
||||
.prefix =
|
||||
Prefix{
|
||||
.s = pos == std::string::npos ? std::string{""} : std::string{rawElem.substr(0, pos)},
|
||||
},
|
||||
.path =
|
||||
Path{
|
||||
.s = std::string{rawElem.substr(pos + 1)},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
LookupPath LookupPath::parse(const Strings & rawElems)
|
||||
{
|
||||
LookupPath res;
|
||||
|
|
@ -52,4 +47,4 @@ LookupPath LookupPath::parse(const Strings & rawElems)
|
|||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -7,107 +7,109 @@
|
|||
#include <iomanip>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
||||
namespace nix {
|
||||
using json = nlohmann::json;
|
||||
|
||||
// TODO: rename. It doesn't print.
|
||||
json printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore)
|
||||
json printValueAsJSON(
|
||||
EvalState & state, bool strict, Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (strict) state.forceValue(v, pos);
|
||||
if (strict)
|
||||
state.forceValue(v, pos);
|
||||
|
||||
json out;
|
||||
|
||||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
out = v.integer().value;
|
||||
break;
|
||||
case nInt:
|
||||
out = v.integer().value;
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
out = v.boolean();
|
||||
break;
|
||||
case nBool:
|
||||
out = v.boolean();
|
||||
break;
|
||||
|
||||
case nString:
|
||||
copyContext(v, context);
|
||||
out = v.c_str();
|
||||
break;
|
||||
case nString:
|
||||
copyContext(v, context);
|
||||
out = v.c_str();
|
||||
break;
|
||||
|
||||
case nPath:
|
||||
if (copyToStore)
|
||||
out = state.store->printStorePath(
|
||||
state.copyPathToStore(context, v.path()));
|
||||
else
|
||||
out = v.path().path.abs();
|
||||
break;
|
||||
case nPath:
|
||||
if (copyToStore)
|
||||
out = state.store->printStorePath(state.copyPathToStore(context, v.path()));
|
||||
else
|
||||
out = v.path().path.abs();
|
||||
break;
|
||||
|
||||
case nNull:
|
||||
// already initialized as null
|
||||
break;
|
||||
case nNull:
|
||||
// already initialized as null
|
||||
break;
|
||||
|
||||
case nAttrs: {
|
||||
auto maybeString = state.tryAttrsToString(pos, v, context, false, false);
|
||||
if (maybeString) {
|
||||
out = *maybeString;
|
||||
break;
|
||||
}
|
||||
if (auto i = v.attrs()->get(state.sOutPath))
|
||||
return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
|
||||
else {
|
||||
out = json::object();
|
||||
for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) {
|
||||
try {
|
||||
out.emplace(state.symbols[a->name], printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore));
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[a->pos],
|
||||
HintFmt("while evaluating attribute '%1%'", state.symbols[a->name]));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
case nAttrs: {
|
||||
auto maybeString = state.tryAttrsToString(pos, v, context, false, false);
|
||||
if (maybeString) {
|
||||
out = *maybeString;
|
||||
break;
|
||||
}
|
||||
|
||||
case nList: {
|
||||
out = json::array();
|
||||
int i = 0;
|
||||
for (auto elem : v.listView()) {
|
||||
if (auto i = v.attrs()->get(state.sOutPath))
|
||||
return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
|
||||
else {
|
||||
out = json::object();
|
||||
for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) {
|
||||
try {
|
||||
out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore));
|
||||
out.emplace(
|
||||
state.symbols[a->name],
|
||||
printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore));
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[pos],
|
||||
HintFmt("while evaluating list element at index %1%", i));
|
||||
e.addTrace(
|
||||
state.positions[a->pos], HintFmt("while evaluating attribute '%1%'", state.symbols[a->name]));
|
||||
throw;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case nExternal:
|
||||
return v.external()->printValueAsJSON(state, strict, context, copyToStore);
|
||||
break;
|
||||
case nList: {
|
||||
out = json::array();
|
||||
int i = 0;
|
||||
for (auto elem : v.listView()) {
|
||||
try {
|
||||
out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore));
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[pos], HintFmt("while evaluating list element at index %1%", i));
|
||||
throw;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case nFloat:
|
||||
out = v.fpoint();
|
||||
break;
|
||||
case nExternal:
|
||||
return v.external()->printValueAsJSON(state, strict, context, copyToStore);
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
case nFunction:
|
||||
state.error<TypeError>(
|
||||
"cannot convert %1% to JSON",
|
||||
showType(v)
|
||||
)
|
||||
.atPos(v.determinePos(pos))
|
||||
.debugThrow();
|
||||
case nFloat:
|
||||
out = v.fpoint();
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
case nFunction:
|
||||
state.error<TypeError>("cannot convert %1% to JSON", showType(v)).atPos(v.determinePos(pos)).debugThrow();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore)
|
||||
void printValueAsJSON(
|
||||
EvalState & state,
|
||||
bool strict,
|
||||
Value & v,
|
||||
const PosIdx pos,
|
||||
std::ostream & str,
|
||||
NixStringContext & context,
|
||||
bool copyToStore)
|
||||
{
|
||||
try {
|
||||
str << printValueAsJSON(state, strict, v, pos, context, copyToStore);
|
||||
|
|
@ -116,12 +118,10 @@ void printValueAsJSON(EvalState & state, bool strict,
|
|||
}
|
||||
}
|
||||
|
||||
json ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
|
||||
NixStringContext & context, bool copyToStore) const
|
||||
json ExternalValueBase::printValueAsJSON(
|
||||
EvalState & state, bool strict, NixStringContext & context, bool copyToStore) const
|
||||
{
|
||||
state.error<TypeError>("cannot convert %1% to JSON", showType())
|
||||
.debugThrow();
|
||||
state.error<TypeError>("cannot convert %1% to JSON", showType()).debugThrow();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@
|
|||
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static XMLAttrs singletonAttrs(const std::string & name, std::string_view value)
|
||||
{
|
||||
XMLAttrs attrs;
|
||||
|
|
@ -16,12 +14,16 @@ static XMLAttrs singletonAttrs(const std::string & name, std::string_view value)
|
|||
return attrs;
|
||||
}
|
||||
|
||||
|
||||
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
Value & v, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen,
|
||||
static void printValueAsXML(
|
||||
EvalState & state,
|
||||
bool strict,
|
||||
bool location,
|
||||
Value & v,
|
||||
XMLWriter & doc,
|
||||
NixStringContext & context,
|
||||
PathSet & drvsSeen,
|
||||
const PosIdx pos);
|
||||
|
||||
|
||||
static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
|
||||
{
|
||||
if (auto path = std::get_if<SourcePath>(&pos.origin))
|
||||
|
|
@ -30,142 +32,167 @@ static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
|
|||
xmlAttrs["column"] = fmt("%1%", pos.column);
|
||||
}
|
||||
|
||||
|
||||
static void showAttrs(EvalState & state, bool strict, bool location,
|
||||
const Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen)
|
||||
static void showAttrs(
|
||||
EvalState & state,
|
||||
bool strict,
|
||||
bool location,
|
||||
const Bindings & attrs,
|
||||
XMLWriter & doc,
|
||||
NixStringContext & context,
|
||||
PathSet & drvsSeen)
|
||||
{
|
||||
StringSet names;
|
||||
|
||||
for (auto & a : attrs.lexicographicOrder(state.symbols)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
xmlAttrs["name"] = state.symbols[a->name];
|
||||
if (location && a->pos) posToXML(state, xmlAttrs, state.positions[a->pos]);
|
||||
if (location && a->pos)
|
||||
posToXML(state, xmlAttrs, state.positions[a->pos]);
|
||||
|
||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||
printValueAsXML(state, strict, location,
|
||||
*a->value, doc, context, drvsSeen, a->pos);
|
||||
printValueAsXML(state, strict, location, *a->value, doc, context, drvsSeen, a->pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
Value & v, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen,
|
||||
static void printValueAsXML(
|
||||
EvalState & state,
|
||||
bool strict,
|
||||
bool location,
|
||||
Value & v,
|
||||
XMLWriter & doc,
|
||||
NixStringContext & context,
|
||||
PathSet & drvsSeen,
|
||||
const PosIdx pos)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (strict) state.forceValue(v, pos);
|
||||
if (strict)
|
||||
state.forceValue(v, pos);
|
||||
|
||||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer())));
|
||||
break;
|
||||
case nInt:
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer())));
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean() ? "true" : "false"));
|
||||
break;
|
||||
case nBool:
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean() ? "true" : "false"));
|
||||
break;
|
||||
|
||||
case nString:
|
||||
/* !!! show the context? */
|
||||
copyContext(v, context);
|
||||
doc.writeEmptyElement("string", singletonAttrs("value", v.c_str()));
|
||||
break;
|
||||
case nString:
|
||||
/* !!! show the context? */
|
||||
copyContext(v, context);
|
||||
doc.writeEmptyElement("string", singletonAttrs("value", v.c_str()));
|
||||
break;
|
||||
|
||||
case nPath:
|
||||
doc.writeEmptyElement("path", singletonAttrs("value", v.path().to_string()));
|
||||
break;
|
||||
case nPath:
|
||||
doc.writeEmptyElement("path", singletonAttrs("value", v.path().to_string()));
|
||||
break;
|
||||
|
||||
case nNull:
|
||||
doc.writeEmptyElement("null");
|
||||
break;
|
||||
case nNull:
|
||||
doc.writeEmptyElement("null");
|
||||
break;
|
||||
|
||||
case nAttrs:
|
||||
if (state.isDerivation(v)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
|
||||
Path drvPath;
|
||||
if (auto a = v.attrs()->get(state.sDrvPath)) {
|
||||
if (strict) state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->c_str();
|
||||
}
|
||||
|
||||
if (auto a = v.attrs()->get(state.sOutPath)) {
|
||||
if (strict) state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["outPath"] = a->value->c_str();
|
||||
}
|
||||
|
||||
XMLOpenElement _(doc, "derivation", xmlAttrs);
|
||||
|
||||
if (drvPath != "" && drvsSeen.insert(drvPath).second)
|
||||
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
|
||||
else
|
||||
doc.writeEmptyElement("repeated");
|
||||
}
|
||||
|
||||
else {
|
||||
XMLOpenElement _(doc, "attrs");
|
||||
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case nList: {
|
||||
XMLOpenElement _(doc, "list");
|
||||
for (auto v2 : v.listView())
|
||||
printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos);
|
||||
break;
|
||||
}
|
||||
|
||||
case nFunction: {
|
||||
if (!v.isLambda()) {
|
||||
// FIXME: Serialize primops and primopapps
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
break;
|
||||
}
|
||||
case nAttrs:
|
||||
if (state.isDerivation(v)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location) posToXML(state, xmlAttrs, state.positions[v.lambda().fun->pos]);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
||||
if (v.lambda().fun->hasFormals()) {
|
||||
XMLAttrs attrs;
|
||||
if (v.lambda().fun->arg) attrs["name"] = state.symbols[v.lambda().fun->arg];
|
||||
if (v.lambda().fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
XMLOpenElement _(doc, "attrspat", attrs);
|
||||
for (auto & i : v.lambda().fun->formals->lexicographicOrder(state.symbols))
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
||||
} else
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda().fun->arg]));
|
||||
Path drvPath;
|
||||
if (auto a = v.attrs()->get(state.sDrvPath)) {
|
||||
if (strict)
|
||||
state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->c_str();
|
||||
}
|
||||
|
||||
break;
|
||||
if (auto a = v.attrs()->get(state.sOutPath)) {
|
||||
if (strict)
|
||||
state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["outPath"] = a->value->c_str();
|
||||
}
|
||||
|
||||
XMLOpenElement _(doc, "derivation", xmlAttrs);
|
||||
|
||||
if (drvPath != "" && drvsSeen.insert(drvPath).second)
|
||||
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
|
||||
else
|
||||
doc.writeEmptyElement("repeated");
|
||||
}
|
||||
|
||||
case nExternal:
|
||||
v.external()->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
|
||||
break;
|
||||
else {
|
||||
XMLOpenElement _(doc, "attrs");
|
||||
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
|
||||
}
|
||||
|
||||
case nFloat:
|
||||
doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint())));
|
||||
break;
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
case nList: {
|
||||
XMLOpenElement _(doc, "list");
|
||||
for (auto v2 : v.listView())
|
||||
printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos);
|
||||
break;
|
||||
}
|
||||
|
||||
case nFunction: {
|
||||
if (!v.isLambda()) {
|
||||
// FIXME: Serialize primops and primopapps
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
break;
|
||||
}
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location)
|
||||
posToXML(state, xmlAttrs, state.positions[v.lambda().fun->pos]);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
||||
if (v.lambda().fun->hasFormals()) {
|
||||
XMLAttrs attrs;
|
||||
if (v.lambda().fun->arg)
|
||||
attrs["name"] = state.symbols[v.lambda().fun->arg];
|
||||
if (v.lambda().fun->formals->ellipsis)
|
||||
attrs["ellipsis"] = "1";
|
||||
XMLOpenElement _(doc, "attrspat", attrs);
|
||||
for (auto & i : v.lambda().fun->formals->lexicographicOrder(state.symbols))
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
||||
} else
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda().fun->arg]));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case nExternal:
|
||||
v.external()->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint())));
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
|
||||
bool location, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen,
|
||||
void ExternalValueBase::printValueAsXML(
|
||||
EvalState & state,
|
||||
bool strict,
|
||||
bool location,
|
||||
XMLWriter & doc,
|
||||
NixStringContext & context,
|
||||
PathSet & drvsSeen,
|
||||
const PosIdx pos) const
|
||||
{
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
}
|
||||
|
||||
|
||||
void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
Value & v, std::ostream & out, NixStringContext & context, const PosIdx pos)
|
||||
void printValueAsXML(
|
||||
EvalState & state,
|
||||
bool strict,
|
||||
bool location,
|
||||
Value & v,
|
||||
std::ostream & out,
|
||||
NixStringContext & context,
|
||||
const PosIdx pos)
|
||||
{
|
||||
XMLWriter doc(true, out);
|
||||
XMLOpenElement root(doc, "expr");
|
||||
|
|
@ -173,5 +200,4 @@ void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
printValueAsXML(state, strict, location, v, doc, context, drvsSeen, pos);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
NixStringContextElem NixStringContextElem::parse(
|
||||
std::string_view s0,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
NixStringContextElem NixStringContextElem::parse(std::string_view s0, const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
std::string_view s = s0;
|
||||
|
||||
|
|
@ -16,16 +14,16 @@ NixStringContextElem NixStringContextElem::parse(
|
|||
// Case on whether there is a '!'
|
||||
size_t index = s.find("!");
|
||||
if (index == std::string_view::npos) {
|
||||
return SingleDerivedPath::Opaque {
|
||||
.path = StorePath { s },
|
||||
return SingleDerivedPath::Opaque{
|
||||
.path = StorePath{s},
|
||||
};
|
||||
} else {
|
||||
std::string output { s.substr(0, index) };
|
||||
std::string output{s.substr(0, index)};
|
||||
// Advance string to parse after the '!'
|
||||
s = s.substr(index + 1);
|
||||
auto drv = make_ref<SingleDerivedPath>(parseRest());
|
||||
drvRequireExperiment(*drv, xpSettings);
|
||||
return SingleDerivedPath::Built {
|
||||
return SingleDerivedPath::Built{
|
||||
.drvPath = std::move(drv),
|
||||
.output = std::move(output),
|
||||
};
|
||||
|
|
@ -33,8 +31,7 @@ NixStringContextElem NixStringContextElem::parse(
|
|||
};
|
||||
|
||||
if (s.size() == 0) {
|
||||
throw BadNixStringContextElem(s0,
|
||||
"String context element should never be an empty string");
|
||||
throw BadNixStringContextElem(s0, "String context element should never be an empty string");
|
||||
}
|
||||
|
||||
switch (s.at(0)) {
|
||||
|
|
@ -44,28 +41,23 @@ NixStringContextElem NixStringContextElem::parse(
|
|||
|
||||
// Find *second* '!'
|
||||
if (s.find("!") == std::string_view::npos) {
|
||||
throw BadNixStringContextElem(s0,
|
||||
"String content element beginning with '!' should have a second '!'");
|
||||
throw BadNixStringContextElem(s0, "String content element beginning with '!' should have a second '!'");
|
||||
}
|
||||
|
||||
return std::visit(
|
||||
[&](auto x) -> NixStringContextElem { return std::move(x); },
|
||||
parseRest());
|
||||
return std::visit([&](auto x) -> NixStringContextElem { return std::move(x); }, parseRest());
|
||||
}
|
||||
case '=': {
|
||||
return NixStringContextElem::DrvDeep {
|
||||
.drvPath = StorePath { s.substr(1) },
|
||||
return NixStringContextElem::DrvDeep{
|
||||
.drvPath = StorePath{s.substr(1)},
|
||||
};
|
||||
}
|
||||
default: {
|
||||
// Ensure no '!'
|
||||
if (s.find("!") != std::string_view::npos) {
|
||||
throw BadNixStringContextElem(s0,
|
||||
"String content element not beginning with '!' should not have a second '!'");
|
||||
throw BadNixStringContextElem(
|
||||
s0, "String content element not beginning with '!' should not have a second '!'");
|
||||
}
|
||||
return std::visit(
|
||||
[&](auto x) -> NixStringContextElem { return std::move(x); },
|
||||
parseRest());
|
||||
return std::visit([&](auto x) -> NixStringContextElem { return std::move(x); }, parseRest());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -76,33 +68,33 @@ std::string NixStringContextElem::to_string() const
|
|||
|
||||
std::function<void(const SingleDerivedPath &)> toStringRest;
|
||||
toStringRest = [&](auto & p) {
|
||||
std::visit(overloaded {
|
||||
[&](const SingleDerivedPath::Opaque & o) {
|
||||
res += o.path.to_string();
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const SingleDerivedPath::Opaque & o) { res += o.path.to_string(); },
|
||||
[&](const SingleDerivedPath::Built & o) {
|
||||
res += o.output;
|
||||
res += '!';
|
||||
toStringRest(*o.drvPath);
|
||||
},
|
||||
},
|
||||
[&](const SingleDerivedPath::Built & o) {
|
||||
res += o.output;
|
||||
res += '!';
|
||||
toStringRest(*o.drvPath);
|
||||
},
|
||||
}, p.raw());
|
||||
p.raw());
|
||||
};
|
||||
|
||||
std::visit(overloaded {
|
||||
[&](const NixStringContextElem::Built & b) {
|
||||
res += '!';
|
||||
toStringRest(b);
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const NixStringContextElem::Built & b) {
|
||||
res += '!';
|
||||
toStringRest(b);
|
||||
},
|
||||
[&](const NixStringContextElem::Opaque & o) { toStringRest(o); },
|
||||
[&](const NixStringContextElem::DrvDeep & d) {
|
||||
res += '=';
|
||||
res += d.drvPath.to_string();
|
||||
},
|
||||
},
|
||||
[&](const NixStringContextElem::Opaque & o) {
|
||||
toStringRest(o);
|
||||
},
|
||||
[&](const NixStringContextElem::DrvDeep & d) {
|
||||
res += '=';
|
||||
res += d.drvPath.to_string();
|
||||
},
|
||||
}, raw);
|
||||
raw);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue