mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-22 14:06:16 +02:00
Merge remote-tracking branch 'origin/master' into nixbuildaddprintstorepaths
This commit is contained in:
commit
be28603dca
72 changed files with 1538 additions and 1163 deletions
|
@ -3,5 +3,9 @@
|
||||||
* `nix repl` has a new build-'n-link (`:bl`) command that builds a derivation
|
* `nix repl` has a new build-'n-link (`:bl`) command that builds a derivation
|
||||||
while creating GC root symlinks.
|
while creating GC root symlinks.
|
||||||
|
|
||||||
|
* The path produced by `builtins.toFile` is now allowed to be imported or read
|
||||||
|
even with restricted evaluation. Note that this will not work with a
|
||||||
|
read-only store.
|
||||||
|
|
||||||
* `nix build` has a new `--print-out-paths` flag to print the resulting output paths.
|
* `nix build` has a new `--print-out-paths` flag to print the resulting output paths.
|
||||||
This matches the default behaviour of `nix-build`.
|
This matches the default behaviour of `nix-build`.
|
||||||
|
|
|
@ -22,6 +22,7 @@ let
|
||||||
findutils
|
findutils
|
||||||
iana-etc
|
iana-etc
|
||||||
git
|
git
|
||||||
|
openssh
|
||||||
];
|
];
|
||||||
|
|
||||||
users = {
|
users = {
|
||||||
|
|
|
@ -197,17 +197,17 @@ void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePath
|
||||||
run(store, *storePaths.begin());
|
run(store, *storePaths.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings editorFor(const Pos & pos)
|
Strings editorFor(const Path & file, uint32_t line)
|
||||||
{
|
{
|
||||||
auto editor = getEnv("EDITOR").value_or("cat");
|
auto editor = getEnv("EDITOR").value_or("cat");
|
||||||
auto args = tokenizeString<Strings>(editor);
|
auto args = tokenizeString<Strings>(editor);
|
||||||
if (pos.line > 0 && (
|
if (line > 0 && (
|
||||||
editor.find("emacs") != std::string::npos ||
|
editor.find("emacs") != std::string::npos ||
|
||||||
editor.find("nano") != std::string::npos ||
|
editor.find("nano") != std::string::npos ||
|
||||||
editor.find("vim") != std::string::npos ||
|
editor.find("vim") != std::string::npos ||
|
||||||
editor.find("kak") != std::string::npos))
|
editor.find("kak") != std::string::npos))
|
||||||
args.push_back(fmt("+%d", pos.line));
|
args.push_back(fmt("+%d", line));
|
||||||
args.push_back(pos.file);
|
args.push_back(file);
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,7 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
|
||||||
|
|
||||||
/* Helper function to generate args that invoke $EDITOR on
|
/* Helper function to generate args that invoke $EDITOR on
|
||||||
filename:lineno. */
|
filename:lineno. */
|
||||||
Strings editorFor(const Pos & pos);
|
Strings editorFor(const Path & file, uint32_t line);
|
||||||
|
|
||||||
struct MixProfile : virtual StoreCommand
|
struct MixProfile : virtual StoreCommand
|
||||||
{
|
{
|
||||||
|
|
|
@ -235,7 +235,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||||
|
|
||||||
if (v2.type() == nAttrs) {
|
if (v2.type() == nAttrs) {
|
||||||
for (auto & i : *v2.attrs) {
|
for (auto & i : *v2.attrs) {
|
||||||
std::string name = i.name;
|
std::string name = state->symbols[i.name];
|
||||||
if (name.find(searchWord) == 0) {
|
if (name.find(searchWord) == 0) {
|
||||||
if (prefix_ == "")
|
if (prefix_ == "")
|
||||||
completions->add(name);
|
completions->add(name);
|
||||||
|
@ -473,7 +473,7 @@ struct InstallableAttrPath : InstallableValue
|
||||||
|
|
||||||
std::string what() const override { return attrPath; }
|
std::string what() const override { return attrPath; }
|
||||||
|
|
||||||
std::pair<Value *, Pos> toValue(EvalState & state) override
|
std::pair<Value *, PosIdx> toValue(EvalState & state) override
|
||||||
{
|
{
|
||||||
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
||||||
state.forceValue(*vRes, pos);
|
state.forceValue(*vRes, pos);
|
||||||
|
@ -600,7 +600,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
|
||||||
|
|
||||||
auto drvInfo = DerivationInfo {
|
auto drvInfo = DerivationInfo {
|
||||||
std::move(drvPath),
|
std::move(drvPath),
|
||||||
attr->getAttr(state->sOutputName)->getString()
|
attr->getAttr("outputName")->getString()
|
||||||
};
|
};
|
||||||
|
|
||||||
return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
|
return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
|
||||||
|
@ -613,7 +613,7 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
||||||
{
|
{
|
||||||
return {&getCursor(state)->forceValue(), noPos};
|
return {&getCursor(state)->forceValue(), noPos};
|
||||||
}
|
}
|
||||||
|
@ -831,8 +831,8 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
|
||||||
auto realisation = store->queryRealisation(outputId);
|
auto realisation = store->queryRealisation(outputId);
|
||||||
if (!realisation)
|
if (!realisation)
|
||||||
throw Error(
|
throw Error(
|
||||||
"cannot operate on an output of unbuilt "
|
"cannot operate on an output of the "
|
||||||
"content-addressed derivation '%s'",
|
"unbuilt derivation '%s'",
|
||||||
outputId.to_string());
|
outputId.to_string());
|
||||||
outputs.insert_or_assign(output, realisation->outPath);
|
outputs.insert_or_assign(output, realisation->outPath);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -68,7 +68,7 @@ struct Installable
|
||||||
|
|
||||||
UnresolvedApp toApp(EvalState & state);
|
UnresolvedApp toApp(EvalState & state);
|
||||||
|
|
||||||
virtual std::pair<Value *, Pos> toValue(EvalState & state)
|
virtual std::pair<Value *, PosIdx> toValue(EvalState & state)
|
||||||
{
|
{
|
||||||
throw Error("argument '%s' cannot be evaluated", what());
|
throw Error("argument '%s' cannot be evaluated", what());
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ struct InstallableFlake : InstallableValue
|
||||||
|
|
||||||
std::vector<DerivationInfo> toDerivations() override;
|
std::vector<DerivationInfo> toDerivations() override;
|
||||||
|
|
||||||
std::pair<Value *, Pos> toValue(EvalState & state) override;
|
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||||
|
|
||||||
/* Get a cursor to every attrpath in getActualAttrPaths() that
|
/* Get a cursor to every attrpath in getActualAttrPaths() that
|
||||||
exists. */
|
exists. */
|
||||||
|
|
|
@ -36,18 +36,18 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
|
||||||
{
|
{
|
||||||
std::vector<Symbol> res;
|
std::vector<Symbol> res;
|
||||||
for (auto & a : parseAttrPath(s))
|
for (auto & a : parseAttrPath(s))
|
||||||
res.push_back(state.symbols.create(a));
|
res.emplace_back(a);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string & attrPath,
|
std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath,
|
||||||
Bindings & autoArgs, Value & vIn)
|
Bindings & autoArgs, Value & vIn)
|
||||||
{
|
{
|
||||||
Strings tokens = parseAttrPath(attrPath);
|
Strings tokens = parseAttrPath(attrPath);
|
||||||
|
|
||||||
Value * v = &vIn;
|
Value * v = &vIn;
|
||||||
Pos pos = noPos;
|
PosIdx pos = noPos;
|
||||||
|
|
||||||
for (auto & attr : tokens) {
|
for (auto & attr : tokens) {
|
||||||
|
|
||||||
|
@ -77,13 +77,13 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string &
|
||||||
if (a == v->attrs->end()) {
|
if (a == v->attrs->end()) {
|
||||||
std::set<std::string> attrNames;
|
std::set<std::string> attrNames;
|
||||||
for (auto & attr : *v->attrs)
|
for (auto & attr : *v->attrs)
|
||||||
attrNames.insert(attr.name);
|
attrNames.insert(state.symbols[attr.name]);
|
||||||
|
|
||||||
auto suggestions = Suggestions::bestMatches(attrNames, attr);
|
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;
|
v = &*a->value;
|
||||||
pos = *a->pos;
|
pos = a->pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -106,7 +106,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string &
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pos findPackageFilename(EvalState & state, Value & v, std::string what)
|
std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||||
{
|
{
|
||||||
Value * v2;
|
Value * v2;
|
||||||
try {
|
try {
|
||||||
|
@ -132,9 +132,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||||
throw ParseError("cannot parse line number '%s'", pos);
|
throw ParseError("cannot parse line number '%s'", pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol file = state.symbols.create(filename);
|
return { std::move(filename), lineno };
|
||||||
|
|
||||||
return { foFile, file, lineno, 0 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,14 @@ namespace nix {
|
||||||
MakeError(AttrPathNotFound, Error);
|
MakeError(AttrPathNotFound, Error);
|
||||||
MakeError(NoPositionInfo, Error);
|
MakeError(NoPositionInfo, Error);
|
||||||
|
|
||||||
std::pair<Value *, Pos> findAlongAttrPath(
|
std::pair<Value *, PosIdx> findAlongAttrPath(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const std::string & attrPath,
|
const std::string & attrPath,
|
||||||
Bindings & autoArgs,
|
Bindings & autoArgs,
|
||||||
Value & vIn);
|
Value & vIn);
|
||||||
|
|
||||||
/* Heuristic to find the filename and lineno or a nix value. */
|
/* Heuristic to find the filename and lineno or a nix value. */
|
||||||
Pos findPackageFilename(EvalState & state, Value & v, std::string what);
|
std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what);
|
||||||
|
|
||||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ Bindings * EvalState::allocBindings(size_t capacity)
|
||||||
/* Create a new attribute named 'name' on an existing attribute set stored
|
/* Create a new attribute named 'name' on an existing attribute set stored
|
||||||
in 'vAttrs' and return the newly allocated Value which is associated with
|
in 'vAttrs' and return the newly allocated Value which is associated with
|
||||||
this attribute. */
|
this attribute. */
|
||||||
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
|
Value * EvalState::allocAttr(Value & vAttrs, const SymbolIdx & name)
|
||||||
{
|
{
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
vAttrs.attrs->push_back(Attr(name, v));
|
vAttrs.attrs->push_back(Attr(name, v));
|
||||||
|
@ -40,7 +40,7 @@ Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos)
|
Value & BindingsBuilder::alloc(const SymbolIdx & name, PosIdx pos)
|
||||||
{
|
{
|
||||||
auto value = state.allocValue();
|
auto value = state.allocValue();
|
||||||
bindings->push_back(Attr(name, value, pos));
|
bindings->push_back(Attr(name, value, pos));
|
||||||
|
@ -48,7 +48,7 @@ Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value & BindingsBuilder::alloc(std::string_view name, ptr<Pos> pos)
|
Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos)
|
||||||
{
|
{
|
||||||
return alloc(state.symbols.create(name), pos);
|
return alloc(state.symbols.create(name), pos);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,18 +15,27 @@ struct Value;
|
||||||
/* Map one attribute name to its value. */
|
/* Map one attribute name to its value. */
|
||||||
struct Attr
|
struct Attr
|
||||||
{
|
{
|
||||||
Symbol name;
|
/* the placement of `name` and `pos` in this struct is important.
|
||||||
|
both of them are uint32 wrappers, they are next to each other
|
||||||
|
to make sure that Attr has no padding on 64 bit machines. that
|
||||||
|
way we keep Attr size at two words with no wasted space. */
|
||||||
|
SymbolIdx name;
|
||||||
|
PosIdx pos;
|
||||||
Value * value;
|
Value * value;
|
||||||
ptr<Pos> pos;
|
Attr(SymbolIdx name, Value * value, PosIdx pos = noPos)
|
||||||
Attr(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
|
: name(name), pos(pos), value(value) { };
|
||||||
: name(name), value(value), pos(pos) { };
|
Attr() { };
|
||||||
Attr() : pos(&noPos) { };
|
|
||||||
bool operator < (const Attr & a) const
|
bool operator < (const Attr & a) const
|
||||||
{
|
{
|
||||||
return name < a.name;
|
return name < a.name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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.");
|
||||||
|
|
||||||
/* Bindings contains all the attributes of an attribute set. It is defined
|
/* Bindings contains all the attributes of an attribute set. It is defined
|
||||||
by its size and its capacity, the capacity being the number of Attr
|
by its size and its capacity, the capacity being the number of Attr
|
||||||
elements allocated after this structure, while the size corresponds to
|
elements allocated after this structure, while the size corresponds to
|
||||||
|
@ -35,13 +44,13 @@ class Bindings
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef uint32_t size_t;
|
typedef uint32_t size_t;
|
||||||
ptr<Pos> pos;
|
PosIdx pos;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t size_, capacity_;
|
size_t size_, capacity_;
|
||||||
Attr attrs[0];
|
Attr attrs[0];
|
||||||
|
|
||||||
Bindings(size_t capacity) : pos(&noPos), size_(0), capacity_(capacity) { }
|
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
|
||||||
Bindings(const Bindings & bindings) = delete;
|
Bindings(const Bindings & bindings) = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -57,7 +66,7 @@ public:
|
||||||
attrs[size_++] = attr;
|
attrs[size_++] = attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator find(const Symbol & name)
|
iterator find(const SymbolIdx & name)
|
||||||
{
|
{
|
||||||
Attr key(name, 0);
|
Attr key(name, 0);
|
||||||
iterator i = std::lower_bound(begin(), end(), key);
|
iterator i = std::lower_bound(begin(), end(), key);
|
||||||
|
@ -65,7 +74,7 @@ public:
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
Attr * get(const Symbol & name)
|
Attr * get(const SymbolIdx & name)
|
||||||
{
|
{
|
||||||
Attr key(name, 0);
|
Attr key(name, 0);
|
||||||
iterator i = std::lower_bound(begin(), end(), key);
|
iterator i = std::lower_bound(begin(), end(), key);
|
||||||
|
@ -73,18 +82,6 @@ public:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Attr & need(const Symbol & name, const Pos & pos = noPos)
|
|
||||||
{
|
|
||||||
auto a = get(name);
|
|
||||||
if (!a)
|
|
||||||
throw Error({
|
|
||||||
.msg = hintfmt("attribute '%s' missing", name),
|
|
||||||
.errPos = pos
|
|
||||||
});
|
|
||||||
|
|
||||||
return *a;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator begin() { return &attrs[0]; }
|
iterator begin() { return &attrs[0]; }
|
||||||
iterator end() { return &attrs[size_]; }
|
iterator end() { return &attrs[size_]; }
|
||||||
|
|
||||||
|
@ -98,14 +95,15 @@ public:
|
||||||
size_t capacity() { return capacity_; }
|
size_t capacity() { return capacity_; }
|
||||||
|
|
||||||
/* Returns the attributes in lexicographically sorted order. */
|
/* Returns the attributes in lexicographically sorted order. */
|
||||||
std::vector<const Attr *> lexicographicOrder() const
|
std::vector<const Attr *> lexicographicOrder(const SymbolTable & symbols) const
|
||||||
{
|
{
|
||||||
std::vector<const Attr *> res;
|
std::vector<const Attr *> res;
|
||||||
res.reserve(size_);
|
res.reserve(size_);
|
||||||
for (size_t n = 0; n < size_; n++)
|
for (size_t n = 0; n < size_; n++)
|
||||||
res.emplace_back(&attrs[n]);
|
res.emplace_back(&attrs[n]);
|
||||||
std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) {
|
std::sort(res.begin(), res.end(), [&](const Attr * a, const Attr * b) {
|
||||||
return (const std::string &) a->name < (const std::string &) b->name;
|
std::string_view sa = symbols[a->name], sb = symbols[b->name];
|
||||||
|
return sa < sb;
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +128,7 @@ public:
|
||||||
: bindings(bindings), state(state)
|
: bindings(bindings), state(state)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void insert(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
|
void insert(SymbolIdx name, Value * value, PosIdx pos = noPos)
|
||||||
{
|
{
|
||||||
insert(Attr(name, value, pos));
|
insert(Attr(name, value, pos));
|
||||||
}
|
}
|
||||||
|
@ -145,9 +143,9 @@ public:
|
||||||
bindings->push_back(attr);
|
bindings->push_back(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value & alloc(const Symbol & name, ptr<Pos> pos = ptr(&noPos));
|
Value & alloc(const SymbolIdx & name, PosIdx pos = noPos);
|
||||||
|
|
||||||
Value & alloc(std::string_view name, ptr<Pos> pos = ptr(&noPos));
|
Value & alloc(std::string_view name, PosIdx pos = noPos);
|
||||||
|
|
||||||
Bindings * finish()
|
Bindings * finish()
|
||||||
{
|
{
|
||||||
|
|
|
@ -253,7 +253,7 @@ struct AttrDb
|
||||||
std::vector<Symbol> attrs;
|
std::vector<Symbol> attrs;
|
||||||
auto queryAttributes(state->queryAttributes.use()(rowId));
|
auto queryAttributes(state->queryAttributes.use()(rowId));
|
||||||
while (queryAttributes.next())
|
while (queryAttributes.next())
|
||||||
attrs.push_back(symbols.create(queryAttributes.getStr(0)));
|
attrs.emplace_back(queryAttributes.getStr(0));
|
||||||
return {{rowId, attrs}};
|
return {{rowId, attrs}};
|
||||||
}
|
}
|
||||||
case AttrType::String: {
|
case AttrType::String: {
|
||||||
|
@ -325,7 +325,7 @@ AttrCursor::AttrCursor(
|
||||||
AttrKey AttrCursor::getKey()
|
AttrKey AttrCursor::getKey()
|
||||||
{
|
{
|
||||||
if (!parent)
|
if (!parent)
|
||||||
return {0, root->state.sEpsilon};
|
return {0, {""}};
|
||||||
if (!parent->first->cachedValue) {
|
if (!parent->first->cachedValue) {
|
||||||
parent->first->cachedValue = root->db->getAttr(
|
parent->first->cachedValue = root->db->getAttr(
|
||||||
parent->first->getKey(), root->state.symbols);
|
parent->first->getKey(), root->state.symbols);
|
||||||
|
@ -340,7 +340,7 @@ Value & AttrCursor::getValue()
|
||||||
if (parent) {
|
if (parent) {
|
||||||
auto & vParent = parent->first->getValue();
|
auto & vParent = parent->first->getValue();
|
||||||
root->state.forceAttrs(vParent, noPos);
|
root->state.forceAttrs(vParent, noPos);
|
||||||
auto attr = vParent.attrs->get(parent->second);
|
auto attr = vParent.attrs->get(root->state.symbols.create(parent->second));
|
||||||
if (!attr)
|
if (!attr)
|
||||||
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
||||||
_value = allocRootValue(attr->value);
|
_value = allocRootValue(attr->value);
|
||||||
|
@ -419,7 +419,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
|
||||||
return Suggestions::bestMatches(strAttrNames, name);
|
return Suggestions::bestMatches(strAttrNames, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
|
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name, bool forceErrors)
|
||||||
{
|
{
|
||||||
if (root->db) {
|
if (root->db) {
|
||||||
if (!cachedValue)
|
if (!cachedValue)
|
||||||
|
@ -461,10 +461,10 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
||||||
|
|
||||||
for (auto & attr : *v.attrs) {
|
for (auto & attr : *v.attrs) {
|
||||||
if (root->db)
|
if (root->db)
|
||||||
root->db->setPlaceholder({cachedValue->first, attr.name});
|
root->db->setPlaceholder({cachedValue->first, root->state.symbols[attr.name]});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto attr = v.attrs->get(name);
|
auto attr = v.attrs->get(root->state.symbols.create(name));
|
||||||
|
|
||||||
if (!attr) {
|
if (!attr) {
|
||||||
if (root->db) {
|
if (root->db) {
|
||||||
|
@ -486,12 +486,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
||||||
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
|
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name)
|
ref<AttrCursor> AttrCursor::getAttr(std::string_view name, bool forceErrors)
|
||||||
{
|
|
||||||
return maybeGetAttr(root->state.symbols.create(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
|
|
||||||
{
|
{
|
||||||
auto p = maybeGetAttr(name, forceErrors);
|
auto p = maybeGetAttr(name, forceErrors);
|
||||||
if (!p)
|
if (!p)
|
||||||
|
@ -499,11 +494,6 @@ ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
|
||||||
return ref(p);
|
return ref(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref<AttrCursor> AttrCursor::getAttr(std::string_view name)
|
|
||||||
{
|
|
||||||
return getAttr(root->state.symbols.create(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
|
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
|
||||||
{
|
{
|
||||||
auto res = shared_from_this();
|
auto res = shared_from_this();
|
||||||
|
@ -616,7 +606,7 @@ std::vector<Symbol> AttrCursor::getAttrs()
|
||||||
|
|
||||||
std::vector<Symbol> attrs;
|
std::vector<Symbol> attrs;
|
||||||
for (auto & attr : *getValue().attrs)
|
for (auto & attr : *getValue().attrs)
|
||||||
attrs.push_back(attr.name);
|
attrs.push_back(root->state.symbols[attr.name]);
|
||||||
std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
|
std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
|
||||||
return (const std::string &) a < (const std::string &) b;
|
return (const std::string &) a < (const std::string &) b;
|
||||||
});
|
});
|
||||||
|
@ -635,7 +625,7 @@ bool AttrCursor::isDerivation()
|
||||||
|
|
||||||
StorePath AttrCursor::forceDerivation()
|
StorePath AttrCursor::forceDerivation()
|
||||||
{
|
{
|
||||||
auto aDrvPath = getAttr(root->state.sDrvPath, true);
|
auto aDrvPath = getAttr("drvPath", true);
|
||||||
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
|
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
|
||||||
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
|
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
|
||||||
/* The eval cache contains 'drvPath', but the actual path has
|
/* The eval cache contains 'drvPath', but the actual path has
|
||||||
|
|
|
@ -96,13 +96,9 @@ public:
|
||||||
|
|
||||||
Suggestions getSuggestionsForAttr(Symbol name);
|
Suggestions getSuggestionsForAttr(Symbol name);
|
||||||
|
|
||||||
std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name, bool forceErrors = false);
|
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name, bool forceErrors = false);
|
||||||
|
|
||||||
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name);
|
ref<AttrCursor> getAttr(std::string_view name, bool forceErrors = false);
|
||||||
|
|
||||||
ref<AttrCursor> getAttr(Symbol name, bool forceErrors = false);
|
|
||||||
|
|
||||||
ref<AttrCursor> getAttr(std::string_view name);
|
|
||||||
|
|
||||||
/* Get an attribute along a chain of attrsets. Note that this does
|
/* Get an attribute along a chain of attrsets. Note that this does
|
||||||
not auto-call functors or functions. */
|
not auto-call functors or functions. */
|
||||||
|
|
|
@ -2,27 +2,8 @@
|
||||||
|
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
|
||||||
#define LocalNoInline(f) static f __attribute__((noinline)); f
|
|
||||||
#define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
|
|
||||||
{
|
|
||||||
throw EvalError({
|
|
||||||
.msg = hintfmt(s),
|
|
||||||
.errPos = pos
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
|
|
||||||
{
|
|
||||||
throw TypeError({
|
|
||||||
.msg = hintfmt(s, showType(v)),
|
|
||||||
.errPos = pos
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Note: Various places expect the allocated memory to be zeroed. */
|
/* Note: Various places expect the allocated memory to be zeroed. */
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
|
@ -99,7 +80,7 @@ Env & EvalState::allocEnv(size_t size)
|
||||||
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
void EvalState::forceValue(Value & v, const Pos & pos)
|
void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, [&]() { return pos; });
|
forceValue(v, [&]() { return pos; });
|
||||||
}
|
}
|
||||||
|
@ -128,7 +109,7 @@ void EvalState::forceValue(Value & v, Callable getPos)
|
||||||
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
inline void EvalState::forceAttrs(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceAttrs(v, [&]() { return pos; });
|
forceAttrs(v, [&]() { return pos; });
|
||||||
}
|
}
|
||||||
|
@ -145,7 +126,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos)
|
||||||
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
inline void EvalState::forceList(Value & v, const Pos & pos)
|
inline void EvalState::forceList(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (!v.isList())
|
if (!v.isList())
|
||||||
|
|
|
@ -96,7 +96,8 @@ RootValue allocRootValue(Value * v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Value::print(std::ostream & str, std::set<const void *> * seen) const
|
void Value::print(const SymbolTable & symbols, std::ostream & str,
|
||||||
|
std::set<const void *> * seen) const
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
|
@ -129,9 +130,9 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
|
||||||
str << "«repeated»";
|
str << "«repeated»";
|
||||||
else {
|
else {
|
||||||
str << "{ ";
|
str << "{ ";
|
||||||
for (auto & i : attrs->lexicographicOrder()) {
|
for (auto & i : attrs->lexicographicOrder(symbols)) {
|
||||||
str << i->name << " = ";
|
str << symbols[i->name] << " = ";
|
||||||
i->value->print(str, seen);
|
i->value->print(symbols, str, seen);
|
||||||
str << "; ";
|
str << "; ";
|
||||||
}
|
}
|
||||||
str << "}";
|
str << "}";
|
||||||
|
@ -146,7 +147,7 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
|
||||||
else {
|
else {
|
||||||
str << "[ ";
|
str << "[ ";
|
||||||
for (auto v2 : listItems()) {
|
for (auto v2 : listItems()) {
|
||||||
v2->print(str, seen);
|
v2->print(symbols, str, seen);
|
||||||
str << " ";
|
str << " ";
|
||||||
}
|
}
|
||||||
str << "]";
|
str << "]";
|
||||||
|
@ -177,17 +178,18 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Value::print(std::ostream & str, bool showRepeated) const
|
void Value::print(const SymbolTable & symbols, std::ostream & str, bool showRepeated) const
|
||||||
{
|
{
|
||||||
std::set<const void *> seen;
|
std::set<const void *> seen;
|
||||||
print(str, showRepeated ? nullptr : &seen);
|
print(symbols, str, showRepeated ? nullptr : &seen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Value & v)
|
std::string printValue(const EvalState & state, const Value & v)
|
||||||
{
|
{
|
||||||
v.print(str, false);
|
std::ostringstream out;
|
||||||
return str;
|
v.print(state.symbols, out);
|
||||||
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,10 +238,10 @@ std::string showType(const Value & v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pos Value::determinePos(const Pos & pos) const
|
PosIdx Value::determinePos(const PosIdx pos) const
|
||||||
{
|
{
|
||||||
switch (internalType) {
|
switch (internalType) {
|
||||||
case tAttrs: return *attrs->pos;
|
case tAttrs: return attrs->pos;
|
||||||
case tLambda: return lambda.fun->pos;
|
case tLambda: return lambda.fun->pos;
|
||||||
case tApp: return app.left->determinePos(pos);
|
case tApp: return app.left->determinePos(pos);
|
||||||
default: return pos;
|
default: return pos;
|
||||||
|
@ -306,9 +308,9 @@ static BoehmGCStackAllocator boehmGCStackAllocator;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static Symbol getName(const AttrName & name, EvalState & state, Env & env)
|
static SymbolIdx getName(const AttrName & name, EvalState & state, Env & env)
|
||||||
{
|
{
|
||||||
if (name.symbol.set()) {
|
if (name.symbol) {
|
||||||
return name.symbol;
|
return name.symbol;
|
||||||
} else {
|
} else {
|
||||||
Value nameValue;
|
Value nameValue;
|
||||||
|
@ -639,20 +641,20 @@ Value * EvalState::addPrimOp(const std::string & name,
|
||||||
size_t arity, PrimOpFun primOp)
|
size_t arity, PrimOpFun primOp)
|
||||||
{
|
{
|
||||||
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
|
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
|
||||||
Symbol sym = symbols.create(name2);
|
auto sym = symbols.create(name2);
|
||||||
|
|
||||||
/* Hack to make constants lazy: turn them into a application of
|
/* Hack to make constants lazy: turn them into a application of
|
||||||
the primop to a dummy value. */
|
the primop to a dummy value. */
|
||||||
if (arity == 0) {
|
if (arity == 0) {
|
||||||
auto vPrimOp = allocValue();
|
auto vPrimOp = allocValue();
|
||||||
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym });
|
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = name2 });
|
||||||
Value v;
|
Value v;
|
||||||
v.mkApp(vPrimOp, vPrimOp);
|
v.mkApp(vPrimOp, vPrimOp);
|
||||||
return addConstant(name, v);
|
return addConstant(name, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = sym });
|
v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = name2 });
|
||||||
staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl);
|
staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl);
|
||||||
baseEnv.values[baseEnvDispl++] = v;
|
baseEnv.values[baseEnvDispl++] = v;
|
||||||
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
||||||
|
@ -667,21 +669,21 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
||||||
if (primOp.arity == 0) {
|
if (primOp.arity == 0) {
|
||||||
primOp.arity = 1;
|
primOp.arity = 1;
|
||||||
auto vPrimOp = allocValue();
|
auto vPrimOp = allocValue();
|
||||||
vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
|
vPrimOp->mkPrimOp(new PrimOp(primOp));
|
||||||
Value v;
|
Value v;
|
||||||
v.mkApp(vPrimOp, vPrimOp);
|
v.mkApp(vPrimOp, vPrimOp);
|
||||||
return addConstant(primOp.name, v);
|
return addConstant(primOp.name, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol envName = primOp.name;
|
auto envName = symbols.create(primOp.name);
|
||||||
if (hasPrefix(primOp.name, "__"))
|
if (hasPrefix(primOp.name, "__"))
|
||||||
primOp.name = symbols.create(std::string(primOp.name, 2));
|
primOp.name = primOp.name.substr(2);
|
||||||
|
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
v->mkPrimOp(new PrimOp(std::move(primOp)));
|
v->mkPrimOp(new PrimOp(primOp));
|
||||||
staticBaseEnv.vars.emplace_back(envName, baseEnvDispl);
|
staticBaseEnv.vars.emplace_back(envName, baseEnvDispl);
|
||||||
baseEnv.values[baseEnvDispl++] = v;
|
baseEnv.values[baseEnvDispl++] = v;
|
||||||
baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v));
|
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v));
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,7 +700,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||||
auto v2 = &v;
|
auto v2 = &v;
|
||||||
if (v2->primOp->doc)
|
if (v2->primOp->doc)
|
||||||
return Doc {
|
return Doc {
|
||||||
.pos = noPos,
|
.pos = {},
|
||||||
.name = v2->primOp->name,
|
.name = v2->primOp->name,
|
||||||
.arity = v2->primOp->arity,
|
.arity = v2->primOp->arity,
|
||||||
.args = v2->primOp->args,
|
.args = v2->primOp->args,
|
||||||
|
@ -714,113 +716,133 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||||
evaluator. So here are some helper functions for throwing
|
evaluator. So here are some helper functions for throwing
|
||||||
exceptions. */
|
exceptions. */
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2))
|
void EvalState::throwEvalError(const PosIdx pos, const char * s) const
|
||||||
|
{
|
||||||
|
throw EvalError({
|
||||||
|
.msg = hintfmt(s),
|
||||||
|
.errPos = positions[pos]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) const
|
||||||
|
{
|
||||||
|
throw TypeError({
|
||||||
|
.msg = hintfmt(s, showType(v)),
|
||||||
|
.errPos = positions[pos]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void EvalState::throwEvalError(const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
throw EvalError(s, s2);
|
throw EvalError(s, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2))
|
void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||||
|
const std::string & s2) const
|
||||||
{
|
{
|
||||||
throw EvalError(ErrorInfo {
|
throw EvalError(ErrorInfo {
|
||||||
.msg = hintfmt(s, s2),
|
.msg = hintfmt(s, s2),
|
||||||
.errPos = pos,
|
.errPos = positions[pos],
|
||||||
.suggestions = suggestions,
|
.suggestions = suggestions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2))
|
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
throw EvalError(ErrorInfo {
|
throw EvalError(ErrorInfo {
|
||||||
.msg = hintfmt(s, s2),
|
.msg = hintfmt(s, s2),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, const std::string & s3))
|
void EvalState::throwEvalError(const char * s, const std::string & s2, const std::string & s3) const
|
||||||
{
|
{
|
||||||
throw EvalError(s, s2, s3);
|
throw EvalError(s, s2, s3);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3))
|
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
|
||||||
|
const std::string & s3) const
|
||||||
{
|
{
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt(s, s2, s3),
|
.msg = hintfmt(s, s2, s3),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2))
|
void EvalState::throwEvalError(const PosIdx p1, const char * s, const SymbolIdx sym, const PosIdx p2) const
|
||||||
{
|
{
|
||||||
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt(s, sym, p2),
|
.msg = hintfmt(s, symbols[sym], positions[p2]),
|
||||||
.errPos = p1
|
.errPos = positions[p1]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
|
void EvalState::throwTypeError(const PosIdx pos, const char * s) const
|
||||||
{
|
{
|
||||||
throw TypeError({
|
throw TypeError({
|
||||||
.msg = hintfmt(s),
|
.msg = hintfmt(s),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
|
void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
|
||||||
|
const SymbolIdx s2) const
|
||||||
{
|
{
|
||||||
throw TypeError({
|
throw TypeError({
|
||||||
.msg = hintfmt(s, fun.showNamePos(), s2),
|
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2))
|
void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||||
|
const ExprLambda & fun, const SymbolIdx s2) const
|
||||||
{
|
{
|
||||||
throw TypeError(ErrorInfo {
|
throw TypeError(ErrorInfo {
|
||||||
.msg = hintfmt(s, fun.showNamePos(), s2),
|
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
||||||
.errPos = pos,
|
.errPos = positions[pos],
|
||||||
.suggestions = suggestions,
|
.suggestions = suggestions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
void EvalState::throwTypeError(const char * s, const Value & v) const
|
||||||
{
|
{
|
||||||
throw TypeError(s, showType(v));
|
throw TypeError(s, showType(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1))
|
void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const
|
||||||
{
|
{
|
||||||
throw AssertionError({
|
throw AssertionError({
|
||||||
.msg = hintfmt(s, s1),
|
.msg = hintfmt(s, s1),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1))
|
void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const
|
||||||
{
|
{
|
||||||
throw UndefinedVarError({
|
throw UndefinedVarError({
|
||||||
.msg = hintfmt(s, s1),
|
.msg = hintfmt(s, s1),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1))
|
void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const
|
||||||
{
|
{
|
||||||
throw MissingArgumentError({
|
throw MissingArgumentError({
|
||||||
.msg = hintfmt(s, s1),
|
.msg = hintfmt(s, s1),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInline(void addErrorTrace(Error & e, const char * s, const std::string & s2))
|
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
e.addTrace(std::nullopt, s, s2);
|
e.addTrace(std::nullopt, s, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2))
|
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
e.addTrace(pos, s, s2);
|
e.addTrace(positions[pos], s, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -877,11 +899,11 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||||
}
|
}
|
||||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||||
if (j != env->values[0]->attrs->end()) {
|
if (j != env->values[0]->attrs->end()) {
|
||||||
if (countCalls) attrSelects[*j->pos]++;
|
if (countCalls) attrSelects[j->pos]++;
|
||||||
return j->value;
|
return j->value;
|
||||||
}
|
}
|
||||||
if (!env->prevWith)
|
if (!env->prevWith)
|
||||||
throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name);
|
throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name]);
|
||||||
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -911,13 +933,14 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::mkPos(Value & v, ptr<Pos> pos)
|
void EvalState::mkPos(Value & v, PosIdx p)
|
||||||
{
|
{
|
||||||
if (pos->file.set()) {
|
auto pos = positions[p];
|
||||||
|
if (!pos.file.empty()) {
|
||||||
auto attrs = buildBindings(3);
|
auto attrs = buildBindings(3);
|
||||||
attrs.alloc(sFile).mkString(pos->file);
|
attrs.alloc(sFile).mkString(pos.file);
|
||||||
attrs.alloc(sLine).mkInt(pos->line);
|
attrs.alloc(sLine).mkInt(pos.line);
|
||||||
attrs.alloc(sColumn).mkInt(pos->column);
|
attrs.alloc(sColumn).mkInt(pos.column);
|
||||||
v.mkAttrs(attrs);
|
v.mkAttrs(attrs);
|
||||||
} else
|
} else
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
|
@ -1050,7 +1073,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
|
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
|
||||||
{
|
{
|
||||||
Value v;
|
Value v;
|
||||||
e->eval(*this, env, v);
|
e->eval(*this, env, v);
|
||||||
|
@ -1124,7 +1147,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
} else
|
} else
|
||||||
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
||||||
env2.values[displ++] = vAttr;
|
env2.values[displ++] = vAttr;
|
||||||
v.attrs->push_back(Attr(i.first, vAttr, ptr(&i.second.pos)));
|
v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the rec contains an attribute called `__overrides', then
|
/* If the rec contains an attribute called `__overrides', then
|
||||||
|
@ -1156,7 +1179,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
else
|
else
|
||||||
for (auto & i : attrs)
|
for (auto & i : attrs)
|
||||||
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), ptr(&i.second.pos)));
|
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos));
|
||||||
|
|
||||||
/* Dynamic attrs apply *after* rec and __overrides. */
|
/* Dynamic attrs apply *after* rec and __overrides. */
|
||||||
for (auto & i : dynamicAttrs) {
|
for (auto & i : dynamicAttrs) {
|
||||||
|
@ -1166,18 +1189,18 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
if (nameVal.type() == nNull)
|
if (nameVal.type() == nNull)
|
||||||
continue;
|
continue;
|
||||||
state.forceStringNoCtx(nameVal);
|
state.forceStringNoCtx(nameVal);
|
||||||
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
auto nameSym = state.symbols.create(nameVal.string.s);
|
||||||
Bindings::iterator j = v.attrs->find(nameSym);
|
Bindings::iterator j = v.attrs->find(nameSym);
|
||||||
if (j != v.attrs->end())
|
if (j != v.attrs->end())
|
||||||
throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos);
|
state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos);
|
||||||
|
|
||||||
i.valueExpr->setName(nameSym);
|
i.valueExpr->setName(nameSym);
|
||||||
/* Keep sorted order so find can catch duplicates */
|
/* Keep sorted order so find can catch duplicates */
|
||||||
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), ptr(&i.pos)));
|
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos));
|
||||||
v.attrs->sort(); // FIXME: inefficient
|
v.attrs->sort(); // FIXME: inefficient
|
||||||
}
|
}
|
||||||
|
|
||||||
v.attrs->pos = ptr(&pos);
|
v.attrs->pos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1222,10 +1245,12 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
|
||||||
for (auto & i : attrPath) {
|
for (auto & i : attrPath) {
|
||||||
if (!first) out << '.'; else first = false;
|
if (!first) out << '.'; else first = false;
|
||||||
try {
|
try {
|
||||||
out << getName(i, state, env);
|
out << state.symbols[getName(i, state, env)];
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
assert(!i.symbol.set());
|
assert(!i.symbol);
|
||||||
out << "\"${" << *i.expr << "}\"";
|
out << "\"${";
|
||||||
|
i.expr->show(state.symbols, out);
|
||||||
|
out << "}\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out.str();
|
return out.str();
|
||||||
|
@ -1235,7 +1260,7 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
|
||||||
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
ptr<Pos> pos2(&noPos);
|
PosIdx pos2;
|
||||||
Value * vAttrs = &vTmp;
|
Value * vAttrs = &vTmp;
|
||||||
|
|
||||||
e->eval(state, env, vTmp);
|
e->eval(state, env, vTmp);
|
||||||
|
@ -1245,7 +1270,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
for (auto & i : attrPath) {
|
for (auto & i : attrPath) {
|
||||||
state.nrLookups++;
|
state.nrLookups++;
|
||||||
Bindings::iterator j;
|
Bindings::iterator j;
|
||||||
Symbol name = getName(i, state, env);
|
auto name = getName(i, state, env);
|
||||||
if (def) {
|
if (def) {
|
||||||
state.forceValue(*vAttrs, pos);
|
state.forceValue(*vAttrs, pos);
|
||||||
if (vAttrs->type() != nAttrs ||
|
if (vAttrs->type() != nAttrs ||
|
||||||
|
@ -1259,23 +1284,24 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
||||||
std::set<std::string> allAttrNames;
|
std::set<std::string> allAttrNames;
|
||||||
for (auto & attr : *vAttrs->attrs)
|
for (auto & attr : *vAttrs->attrs)
|
||||||
allAttrNames.insert(attr.name);
|
allAttrNames.insert(state.symbols[attr.name]);
|
||||||
throwEvalError(
|
state.throwEvalError(
|
||||||
pos,
|
pos,
|
||||||
Suggestions::bestMatches(allAttrNames, name),
|
Suggestions::bestMatches(allAttrNames, state.symbols[name]),
|
||||||
"attribute '%1%' missing", name);
|
"attribute '%1%' missing", state.symbols[name]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vAttrs = j->value;
|
vAttrs = j->value;
|
||||||
pos2 = j->pos;
|
pos2 = j->pos;
|
||||||
if (state.countCalls) state.attrSelects[*pos2]++;
|
if (state.countCalls) state.attrSelects[pos2]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.forceValue(*vAttrs, (*pos2 != noPos ? *pos2 : this->pos ) );
|
state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (*pos2 != noPos && pos2->file != state.sDerivationNix)
|
auto pos2r = state.positions[pos2];
|
||||||
addErrorTrace(e, *pos2, "while evaluating the attribute '%1%'",
|
if (pos2 && pos2r.file != state.derivationNixPath)
|
||||||
|
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
|
||||||
showAttrPath(state, env, attrPath));
|
showAttrPath(state, env, attrPath));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -1294,7 +1320,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||||
for (auto & i : attrPath) {
|
for (auto & i : attrPath) {
|
||||||
state.forceValue(*vAttrs, noPos);
|
state.forceValue(*vAttrs, noPos);
|
||||||
Bindings::iterator j;
|
Bindings::iterator j;
|
||||||
Symbol name = getName(i, state, env);
|
auto name = getName(i, state, env);
|
||||||
if (vAttrs->type() != nAttrs ||
|
if (vAttrs->type() != nAttrs ||
|
||||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||||
{
|
{
|
||||||
|
@ -1315,9 +1341,11 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos)
|
void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos)
|
||||||
{
|
{
|
||||||
auto trace = evalSettings.traceFunctionCalls ? std::make_unique<FunctionCallTrace>(pos) : nullptr;
|
auto trace = evalSettings.traceFunctionCalls
|
||||||
|
? std::make_unique<FunctionCallTrace>(positions[pos])
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
forceValue(fun, pos);
|
forceValue(fun, pos);
|
||||||
|
|
||||||
|
@ -1342,7 +1370,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
ExprLambda & lambda(*vCur.lambda.fun);
|
ExprLambda & lambda(*vCur.lambda.fun);
|
||||||
|
|
||||||
auto size =
|
auto size =
|
||||||
(lambda.arg.empty() ? 0 : 1) +
|
(!lambda.arg ? 0 : 1) +
|
||||||
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
||||||
Env & env2(allocEnv(size));
|
Env & env2(allocEnv(size));
|
||||||
env2.up = vCur.lambda.env;
|
env2.up = vCur.lambda.env;
|
||||||
|
@ -1355,7 +1383,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
else {
|
else {
|
||||||
forceAttrs(*args[0], pos);
|
forceAttrs(*args[0], pos);
|
||||||
|
|
||||||
if (!lambda.arg.empty())
|
if (lambda.arg)
|
||||||
env2.values[displ++] = args[0];
|
env2.values[displ++] = args[0];
|
||||||
|
|
||||||
/* For each formal argument, get the actual argument. If
|
/* For each formal argument, get the actual argument. If
|
||||||
|
@ -1383,10 +1411,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
if (!lambda.formals->has(i.name)) {
|
if (!lambda.formals->has(i.name)) {
|
||||||
std::set<std::string> formalNames;
|
std::set<std::string> formalNames;
|
||||||
for (auto & formal : lambda.formals->formals)
|
for (auto & formal : lambda.formals->formals)
|
||||||
formalNames.insert(formal.name);
|
formalNames.insert(symbols[formal.name]);
|
||||||
throwTypeError(
|
throwTypeError(
|
||||||
pos,
|
pos,
|
||||||
Suggestions::bestMatches(formalNames, i.name),
|
Suggestions::bestMatches(formalNames, symbols[i.name]),
|
||||||
"%1% called with unexpected argument '%2%'",
|
"%1% called with unexpected argument '%2%'",
|
||||||
lambda,
|
lambda,
|
||||||
i.name);
|
i.name);
|
||||||
|
@ -1404,8 +1432,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (loggerSettings.showTrace.get()) {
|
if (loggerSettings.showTrace.get()) {
|
||||||
addErrorTrace(e, lambda.pos, "while evaluating %s",
|
addErrorTrace(e, lambda.pos, "while evaluating %s",
|
||||||
(lambda.name.set()
|
(lambda.name
|
||||||
? "'" + (const std::string &) lambda.name + "'"
|
? concatStrings("'", symbols[lambda.name], "'")
|
||||||
: "anonymous lambda"));
|
: "anonymous lambda"));
|
||||||
addErrorTrace(e, pos, "from call site%s", "");
|
addErrorTrace(e, pos, "from call site%s", "");
|
||||||
}
|
}
|
||||||
|
@ -1554,7 +1582,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||||
Nix attempted to evaluate a function as a top level expression; in
|
Nix attempted to evaluate a function as a top level expression; in
|
||||||
this case it must have its arguments supplied either by default
|
this case it must have its arguments supplied either by default
|
||||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||||
https://nixos.org/manual/nix/stable/#ss-functions.)", i.name);
|
https://nixos.org/manual/nix/stable/#ss-functions.)", symbols[i.name]);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1586,8 +1614,8 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
if (!state.evalBool(env, cond, pos)) {
|
if (!state.evalBool(env, cond, pos)) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
cond->show(out);
|
cond->show(state.symbols, out);
|
||||||
throwAssertionError(pos, "assertion '%1%' failed", out.str());
|
state.throwAssertionError(pos, "assertion '%1%' failed", out.str());
|
||||||
}
|
}
|
||||||
body->eval(state, env, v);
|
body->eval(state, env, v);
|
||||||
}
|
}
|
||||||
|
@ -1680,7 +1708,7 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos)
|
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos)
|
||||||
{
|
{
|
||||||
nrListConcats++;
|
nrListConcats++;
|
||||||
|
|
||||||
|
@ -1764,14 +1792,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
nf = n;
|
nf = n;
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp));
|
state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp));
|
||||||
} else if (firstType == nFloat) {
|
} else if (firstType == nFloat) {
|
||||||
if (vTmp.type() == nInt) {
|
if (vTmp.type() == nInt) {
|
||||||
nf += vTmp.integer;
|
nf += vTmp.integer;
|
||||||
} else if (vTmp.type() == nFloat) {
|
} else if (vTmp.type() == nFloat) {
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
|
state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
|
||||||
} else {
|
} else {
|
||||||
if (s.empty()) s.reserve(es->size());
|
if (s.empty()) s.reserve(es->size());
|
||||||
/* skip canonization of first path, which would only be not
|
/* skip canonization of first path, which would only be not
|
||||||
|
@ -1791,7 +1819,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
v.mkFloat(nf);
|
v.mkFloat(nf);
|
||||||
else if (firstType == nPath) {
|
else if (firstType == nPath) {
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
||||||
v.mkPath(canonPath(str()));
|
v.mkPath(canonPath(str()));
|
||||||
} else
|
} else
|
||||||
v.mkStringMove(c_str(), context);
|
v.mkStringMove(c_str(), context);
|
||||||
|
@ -1800,7 +1828,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
void ExprPos::eval(EvalState & state, Env & env, Value & v)
|
void ExprPos::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
state.mkPos(v, ptr(&pos));
|
state.mkPos(v, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1820,7 +1848,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||||
try {
|
try {
|
||||||
recurse(*i.value);
|
recurse(*i.value);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
addErrorTrace(e, *i.pos, "while evaluating the attribute '%1%'", i.name);
|
addErrorTrace(e, i.pos, "while evaluating the attribute '%1%'", symbols[i.name]);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1835,7 +1863,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
NixInt EvalState::forceInt(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nInt)
|
if (v.type() != nInt)
|
||||||
|
@ -1844,7 +1872,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
|
NixFloat EvalState::forceFloat(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() == nInt)
|
if (v.type() == nInt)
|
||||||
|
@ -1855,7 +1883,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EvalState::forceBool(Value & v, const Pos & pos)
|
bool EvalState::forceBool(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nBool)
|
if (v.type() != nBool)
|
||||||
|
@ -1870,7 +1898,7 @@ bool EvalState::isFunctor(Value & fun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::forceFunction(Value & v, const Pos & pos)
|
void EvalState::forceFunction(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nFunction && !isFunctor(v))
|
if (v.type() != nFunction && !isFunctor(v))
|
||||||
|
@ -1878,7 +1906,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string_view EvalState::forceString(Value & v, const Pos & pos)
|
std::string_view EvalState::forceString(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nString) {
|
if (v.type() != nString) {
|
||||||
|
@ -1931,7 +1959,7 @@ NixStringContext Value::getContext(const Store & store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
|
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos)
|
||||||
{
|
{
|
||||||
auto s = forceString(v, pos);
|
auto s = forceString(v, pos);
|
||||||
copyContext(v, context);
|
copyContext(v, context);
|
||||||
|
@ -1939,7 +1967,7 @@ std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos)
|
std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
auto s = forceString(v, pos);
|
auto s = forceString(v, pos);
|
||||||
if (v.string.context) {
|
if (v.string.context) {
|
||||||
|
@ -1959,13 +1987,13 @@ bool EvalState::isDerivation(Value & v)
|
||||||
if (v.type() != nAttrs) return false;
|
if (v.type() != nAttrs) return false;
|
||||||
Bindings::iterator i = v.attrs->find(sType);
|
Bindings::iterator i = v.attrs->find(sType);
|
||||||
if (i == v.attrs->end()) return false;
|
if (i == v.attrs->end()) return false;
|
||||||
forceValue(*i->value, *i->pos);
|
forceValue(*i->value, i->pos);
|
||||||
if (i->value->type() != nString) return false;
|
if (i->value->type() != nString) return false;
|
||||||
return strcmp(i->value->string.s, "derivation") == 0;
|
return strcmp(i->value->string.s, "derivation") == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::optional<std::string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
|
std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v,
|
||||||
PathSet & context, bool coerceMore, bool copyToStore)
|
PathSet & context, bool coerceMore, bool copyToStore)
|
||||||
{
|
{
|
||||||
auto i = v.attrs->find(sToString);
|
auto i = v.attrs->find(sToString);
|
||||||
|
@ -1978,7 +2006,7 @@ std::optional<std::string> EvalState::tryAttrsToString(const Pos & pos, Value &
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet & context,
|
||||||
bool coerceMore, bool copyToStore, bool canonicalizePath)
|
bool coerceMore, bool copyToStore, bool canonicalizePath)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
|
@ -2007,7 +2035,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.type() == nExternal)
|
if (v.type() == nExternal)
|
||||||
return v.external->coerceToString(pos, context, coerceMore, copyToStore);
|
return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
|
||||||
|
|
||||||
if (coerceMore) {
|
if (coerceMore) {
|
||||||
|
|
||||||
|
@ -2060,7 +2088,7 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context)
|
||||||
{
|
{
|
||||||
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
||||||
if (path == "" || path[0] != '/')
|
if (path == "" || path[0] != '/')
|
||||||
|
@ -2069,14 +2097,14 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context)
|
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context)
|
||||||
{
|
{
|
||||||
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
||||||
if (auto storePath = store->maybeParseStorePath(path))
|
if (auto storePath = store->maybeParseStorePath(path))
|
||||||
return *storePath;
|
return *storePath;
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("path '%1%' is not in the Nix store", path),
|
.msg = hintfmt("path '%1%' is not in the Nix store", path),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2243,14 +2271,14 @@ void EvalState::printStats()
|
||||||
auto list = topObj.list("functions");
|
auto list = topObj.list("functions");
|
||||||
for (auto & i : functionCalls) {
|
for (auto & i : functionCalls) {
|
||||||
auto obj = list.object();
|
auto obj = list.object();
|
||||||
if (i.first->name.set())
|
if (i.first->name)
|
||||||
obj.attr("name", (const std::string &) i.first->name);
|
obj.attr("name", (const std::string &) i.first->name);
|
||||||
else
|
else
|
||||||
obj.attr("name", nullptr);
|
obj.attr("name", nullptr);
|
||||||
if (i.first->pos) {
|
if (auto pos = positions[i.first->pos]) {
|
||||||
obj.attr("file", (const std::string &) i.first->pos.file);
|
obj.attr("file", (const std::string &) pos.file);
|
||||||
obj.attr("line", i.first->pos.line);
|
obj.attr("line", pos.line);
|
||||||
obj.attr("column", i.first->pos.column);
|
obj.attr("column", pos.column);
|
||||||
}
|
}
|
||||||
obj.attr("count", i.second);
|
obj.attr("count", i.second);
|
||||||
}
|
}
|
||||||
|
@ -2259,10 +2287,10 @@ void EvalState::printStats()
|
||||||
auto list = topObj.list("attributes");
|
auto list = topObj.list("attributes");
|
||||||
for (auto & i : attrSelects) {
|
for (auto & i : attrSelects) {
|
||||||
auto obj = list.object();
|
auto obj = list.object();
|
||||||
if (i.first) {
|
if (auto pos = positions[i.first]) {
|
||||||
obj.attr("file", (const std::string &) i.first.file);
|
obj.attr("file", (const std::string &) pos.file);
|
||||||
obj.attr("line", i.first.line);
|
obj.attr("line", pos.line);
|
||||||
obj.attr("column", i.first.column);
|
obj.attr("column", pos.column);
|
||||||
}
|
}
|
||||||
obj.attr("count", i.second);
|
obj.attr("count", i.second);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,14 +23,14 @@ class StorePath;
|
||||||
enum RepairFlag : bool;
|
enum RepairFlag : bool;
|
||||||
|
|
||||||
|
|
||||||
typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
|
typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
|
|
||||||
|
|
||||||
struct PrimOp
|
struct PrimOp
|
||||||
{
|
{
|
||||||
PrimOpFun fun;
|
PrimOpFun fun;
|
||||||
size_t arity;
|
size_t arity;
|
||||||
Symbol name;
|
std::string name;
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
const char * doc = nullptr;
|
const char * doc = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -53,7 +53,8 @@ void copyContext(const Value & v, PathSet & context);
|
||||||
typedef std::map<Path, StorePath> SrcToStore;
|
typedef std::map<Path, StorePath> SrcToStore;
|
||||||
|
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Value & v);
|
std::ostream & printValue(const EvalState & state, std::ostream & str, const Value & v);
|
||||||
|
std::string printValue(const EvalState & state, const Value & v);
|
||||||
|
|
||||||
|
|
||||||
typedef std::pair<std::string, std::string> SearchPathElem;
|
typedef std::pair<std::string, std::string> SearchPathElem;
|
||||||
|
@ -73,8 +74,11 @@ class EvalState
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SymbolTable symbols;
|
SymbolTable symbols;
|
||||||
|
PosTable positions;
|
||||||
|
|
||||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
static inline std::string derivationNixPath = "//builtin/derivation.nix";
|
||||||
|
|
||||||
|
const SymbolIdx sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||||
sFile, sLine, sColumn, sFunctor, sToString,
|
sFile, sLine, sColumn, sFunctor, sToString,
|
||||||
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
|
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
|
||||||
|
@ -83,7 +87,7 @@ public:
|
||||||
sRecurseForDerivations,
|
sRecurseForDerivations,
|
||||||
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
|
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
|
||||||
sPrefix;
|
sPrefix;
|
||||||
Symbol sDerivationNix;
|
SymbolIdx sDerivationNix;
|
||||||
|
|
||||||
/* If set, force copying files to the Nix store even if they
|
/* If set, force copying files to the Nix store even if they
|
||||||
already exist there. */
|
already exist there. */
|
||||||
|
@ -205,7 +209,7 @@ public:
|
||||||
|
|
||||||
/* Look up a file in the search path. */
|
/* Look up a file in the search path. */
|
||||||
Path findFile(const std::string_view path);
|
Path findFile(const std::string_view path);
|
||||||
Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
|
Path findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
|
||||||
|
|
||||||
/* If the specified search path element is a URI, download it. */
|
/* If the specified search path element is a URI, download it. */
|
||||||
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
|
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
|
||||||
|
@ -217,14 +221,14 @@ public:
|
||||||
/* Evaluation the expression, then verify that it has the expected
|
/* Evaluation the expression, then verify that it has the expected
|
||||||
type. */
|
type. */
|
||||||
inline bool evalBool(Env & env, Expr * e);
|
inline bool evalBool(Env & env, Expr * e);
|
||||||
inline bool evalBool(Env & env, Expr * e, const Pos & pos);
|
inline bool evalBool(Env & env, Expr * e, const PosIdx pos);
|
||||||
inline void evalAttrs(Env & env, Expr * e, Value & v);
|
inline void evalAttrs(Env & env, Expr * e, Value & v);
|
||||||
|
|
||||||
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
||||||
of the evaluation of the thunk. If `v' is a delayed function
|
of the evaluation of the thunk. If `v' is a delayed function
|
||||||
application, call the function and overwrite `v' with the
|
application, call the function and overwrite `v' with the
|
||||||
result. Otherwise, this is a no-op. */
|
result. Otherwise, this is a no-op. */
|
||||||
inline void forceValue(Value & v, const Pos & pos);
|
inline void forceValue(Value & v, const PosIdx pos);
|
||||||
|
|
||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
inline void forceValue(Value & v, Callable getPos);
|
inline void forceValue(Value & v, Callable getPos);
|
||||||
|
@ -234,33 +238,72 @@ public:
|
||||||
void forceValueDeep(Value & v);
|
void forceValueDeep(Value & v);
|
||||||
|
|
||||||
/* Force `v', and then verify that it has the expected type. */
|
/* Force `v', and then verify that it has the expected type. */
|
||||||
NixInt forceInt(Value & v, const Pos & pos);
|
NixInt forceInt(Value & v, const PosIdx pos);
|
||||||
NixFloat forceFloat(Value & v, const Pos & pos);
|
NixFloat forceFloat(Value & v, const PosIdx pos);
|
||||||
bool forceBool(Value & v, const Pos & pos);
|
bool forceBool(Value & v, const PosIdx pos);
|
||||||
|
|
||||||
void forceAttrs(Value & v, const Pos & pos);
|
void forceAttrs(Value & v, const PosIdx pos);
|
||||||
|
|
||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
inline void forceAttrs(Value & v, Callable getPos);
|
inline void forceAttrs(Value & v, Callable getPos);
|
||||||
|
|
||||||
inline void forceList(Value & v, const Pos & pos);
|
inline void forceList(Value & v, const PosIdx pos);
|
||||||
void forceFunction(Value & v, const Pos & pos); // either lambda or primop
|
void forceFunction(Value & v, const PosIdx pos); // either lambda or primop
|
||||||
std::string_view forceString(Value & v, const Pos & pos = noPos);
|
std::string_view forceString(Value & v, const PosIdx pos = noPos);
|
||||||
std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos);
|
std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos);
|
||||||
std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos);
|
std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
|
||||||
|
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx pos, const char * s) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const PosIdx pos, const char * s, const Value & v) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const char * s, const std::string & s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||||
|
const std::string & s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const char * s, const std::string & s2, const std::string & s3) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx p1, const char * s, const SymbolIdx sym, const PosIdx p2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const PosIdx pos, const char * s) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const SymbolIdx s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||||
|
const ExprLambda & fun, const SymbolIdx s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const char * s, const Value & v) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const;
|
||||||
|
|
||||||
|
[[gnu::noinline]]
|
||||||
|
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
|
||||||
|
[[gnu::noinline]]
|
||||||
|
void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const;
|
||||||
|
|
||||||
|
public:
|
||||||
/* Return true iff the value `v' denotes a derivation (i.e. a
|
/* Return true iff the value `v' denotes a derivation (i.e. a
|
||||||
set with attribute `type = "derivation"'). */
|
set with attribute `type = "derivation"'). */
|
||||||
bool isDerivation(Value & v);
|
bool isDerivation(Value & v);
|
||||||
|
|
||||||
std::optional<std::string> tryAttrsToString(const Pos & pos, Value & v,
|
std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
|
||||||
PathSet & context, bool coerceMore = false, bool copyToStore = true);
|
PathSet & context, bool coerceMore = false, bool copyToStore = true);
|
||||||
|
|
||||||
/* String coercion. Converts strings, paths and derivations to a
|
/* String coercion. Converts strings, paths and derivations to a
|
||||||
string. If `coerceMore' is set, also converts nulls, integers,
|
string. If `coerceMore' is set, also converts nulls, integers,
|
||||||
booleans and lists to a string. If `copyToStore' is set,
|
booleans and lists to a string. If `copyToStore' is set,
|
||||||
referenced paths are copied to the Nix store as a side effect. */
|
referenced paths are copied to the Nix store as a side effect. */
|
||||||
BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
|
BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
|
||||||
bool coerceMore = false, bool copyToStore = true,
|
bool coerceMore = false, bool copyToStore = true,
|
||||||
bool canonicalizePath = true);
|
bool canonicalizePath = true);
|
||||||
|
|
||||||
|
@ -269,10 +312,10 @@ public:
|
||||||
/* Path coercion. Converts strings, paths and derivations to a
|
/* Path coercion. Converts strings, paths and derivations to a
|
||||||
path. The result is guaranteed to be a canonicalised, absolute
|
path. The result is guaranteed to be a canonicalised, absolute
|
||||||
path. Nothing is copied to the store. */
|
path. Nothing is copied to the store. */
|
||||||
Path coerceToPath(const Pos & pos, Value & v, PathSet & context);
|
Path coerceToPath(const PosIdx pos, Value & v, PathSet & context);
|
||||||
|
|
||||||
/* Like coerceToPath, but the result must be a store path. */
|
/* Like coerceToPath, but the result must be a store path. */
|
||||||
StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context);
|
StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -305,7 +348,7 @@ public:
|
||||||
struct Doc
|
struct Doc
|
||||||
{
|
{
|
||||||
Pos pos;
|
Pos pos;
|
||||||
std::optional<Symbol> name;
|
std::optional<std::string> name;
|
||||||
size_t arity;
|
size_t arity;
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
const char * doc;
|
const char * doc;
|
||||||
|
@ -333,9 +376,9 @@ public:
|
||||||
bool isFunctor(Value & fun);
|
bool isFunctor(Value & fun);
|
||||||
|
|
||||||
// FIXME: use std::span
|
// FIXME: use std::span
|
||||||
void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos);
|
void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos);
|
||||||
|
|
||||||
void callFunction(Value & fun, Value & arg, Value & vRes, const Pos & pos)
|
void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
|
||||||
{
|
{
|
||||||
Value * args[] = {&arg};
|
Value * args[] = {&arg};
|
||||||
callFunction(fun, 1, args, vRes, pos);
|
callFunction(fun, 1, args, vRes, pos);
|
||||||
|
@ -349,7 +392,7 @@ public:
|
||||||
inline Value * allocValue();
|
inline Value * allocValue();
|
||||||
inline Env & allocEnv(size_t size);
|
inline Env & allocEnv(size_t size);
|
||||||
|
|
||||||
Value * allocAttr(Value & vAttrs, const Symbol & name);
|
Value * allocAttr(Value & vAttrs, const SymbolIdx & name);
|
||||||
Value * allocAttr(Value & vAttrs, std::string_view name);
|
Value * allocAttr(Value & vAttrs, std::string_view name);
|
||||||
|
|
||||||
Bindings * allocBindings(size_t capacity);
|
Bindings * allocBindings(size_t capacity);
|
||||||
|
@ -361,9 +404,9 @@ public:
|
||||||
|
|
||||||
void mkList(Value & v, size_t length);
|
void mkList(Value & v, size_t length);
|
||||||
void mkThunk_(Value & v, Expr * expr);
|
void mkThunk_(Value & v, Expr * expr);
|
||||||
void mkPos(Value & v, ptr<Pos> pos);
|
void mkPos(Value & v, PosIdx pos);
|
||||||
|
|
||||||
void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos);
|
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos);
|
||||||
|
|
||||||
/* Print statistics. */
|
/* Print statistics. */
|
||||||
void printStats();
|
void printStats();
|
||||||
|
@ -391,7 +434,7 @@ private:
|
||||||
|
|
||||||
bool countCalls;
|
bool countCalls;
|
||||||
|
|
||||||
typedef std::map<Symbol, size_t> PrimOpCalls;
|
typedef std::map<std::string, size_t> PrimOpCalls;
|
||||||
PrimOpCalls primOpCalls;
|
PrimOpCalls primOpCalls;
|
||||||
|
|
||||||
typedef std::map<ExprLambda *, size_t> FunctionCalls;
|
typedef std::map<ExprLambda *, size_t> FunctionCalls;
|
||||||
|
@ -399,7 +442,7 @@ private:
|
||||||
|
|
||||||
void incrFunctionCall(ExprLambda * fun);
|
void incrFunctionCall(ExprLambda * fun);
|
||||||
|
|
||||||
typedef std::map<Pos, size_t> AttrSelects;
|
typedef std::map<PosIdx, size_t> AttrSelects;
|
||||||
AttrSelects attrSelects;
|
AttrSelects attrSelects;
|
||||||
|
|
||||||
friend struct ExprOpUpdate;
|
friend struct ExprOpUpdate;
|
||||||
|
@ -410,9 +453,9 @@ private:
|
||||||
friend struct ExprFloat;
|
friend struct ExprFloat;
|
||||||
friend struct ExprPath;
|
friend struct ExprPath;
|
||||||
friend struct ExprSelect;
|
friend struct ExprSelect;
|
||||||
friend void prim_getAttr(EvalState & state, const Pos & 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 Pos & 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 Pos & pos, Value * * args, Value & v);
|
friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
|
|
||||||
friend struct Value;
|
friend struct Value;
|
||||||
};
|
};
|
||||||
|
|
|
@ -72,7 +72,7 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||||
return {std::move(tree), resolvedRef, lockedRef};
|
return {std::move(tree), resolvedRef, lockedRef};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
|
static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
|
||||||
{
|
{
|
||||||
if (value.isThunk() && value.isTrivial())
|
if (value.isThunk() && value.isTrivial())
|
||||||
state.forceValue(value, pos);
|
state.forceValue(value, pos);
|
||||||
|
@ -80,20 +80,20 @@ static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
|
||||||
|
|
||||||
|
|
||||||
static void expectType(EvalState & state, ValueType type,
|
static void expectType(EvalState & state, ValueType type,
|
||||||
Value & value, const Pos & pos)
|
Value & value, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceTrivialValue(state, value, pos);
|
forceTrivialValue(state, value, pos);
|
||||||
if (value.type() != type)
|
if (value.type() != type)
|
||||||
throw Error("expected %s but got %s at %s",
|
throw Error("expected %s but got %s at %s",
|
||||||
showType(type), showType(value.type()), pos);
|
showType(type), showType(value.type()), state.positions[pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
EvalState & state, Value * value, const Pos & pos,
|
EvalState & state, Value * value, const PosIdx pos,
|
||||||
const std::optional<Path> & baseDir, InputPath lockRootPath);
|
const std::optional<Path> & baseDir, InputPath lockRootPath);
|
||||||
|
|
||||||
static FlakeInput parseFlakeInput(EvalState & state,
|
static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
const std::string & inputName, Value * value, const Pos & pos,
|
const std::string & inputName, Value * value, const PosIdx pos,
|
||||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||||
{
|
{
|
||||||
expectType(state, nAttrs, *value, pos);
|
expectType(state, nAttrs, *value, pos);
|
||||||
|
@ -111,37 +111,39 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
for (nix::Attr attr : *(value->attrs)) {
|
for (nix::Attr attr : *(value->attrs)) {
|
||||||
try {
|
try {
|
||||||
if (attr.name == sUrl) {
|
if (attr.name == sUrl) {
|
||||||
expectType(state, nString, *attr.value, *attr.pos);
|
expectType(state, nString, *attr.value, attr.pos);
|
||||||
url = attr.value->string.s;
|
url = attr.value->string.s;
|
||||||
attrs.emplace("url", *url);
|
attrs.emplace("url", *url);
|
||||||
} else if (attr.name == sFlake) {
|
} else if (attr.name == sFlake) {
|
||||||
expectType(state, nBool, *attr.value, *attr.pos);
|
expectType(state, nBool, *attr.value, attr.pos);
|
||||||
input.isFlake = attr.value->boolean;
|
input.isFlake = attr.value->boolean;
|
||||||
} else if (attr.name == sInputs) {
|
} else if (attr.name == sInputs) {
|
||||||
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir, lockRootPath);
|
input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath);
|
||||||
} else if (attr.name == sFollows) {
|
} else if (attr.name == sFollows) {
|
||||||
expectType(state, nString, *attr.value, *attr.pos);
|
expectType(state, nString, *attr.value, attr.pos);
|
||||||
auto follows(parseInputPath(attr.value->string.s));
|
auto follows(parseInputPath(attr.value->string.s));
|
||||||
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
||||||
input.follows = follows;
|
input.follows = follows;
|
||||||
} else {
|
} else {
|
||||||
switch (attr.value->type()) {
|
switch (attr.value->type()) {
|
||||||
case nString:
|
case nString:
|
||||||
attrs.emplace(attr.name, attr.value->string.s);
|
attrs.emplace(state.symbols[attr.name], attr.value->string.s);
|
||||||
break;
|
break;
|
||||||
case nBool:
|
case nBool:
|
||||||
attrs.emplace(attr.name, Explicit<bool> { attr.value->boolean });
|
attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean });
|
||||||
break;
|
break;
|
||||||
case nInt:
|
case nInt:
|
||||||
attrs.emplace(attr.name, (long unsigned int)attr.value->integer);
|
attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||||
attr.name, showType(*attr.value));
|
state.symbols[attr.name], showType(*attr.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(*attr.pos, hintfmt("in flake attribute '%s'", attr.name));
|
e.addTrace(
|
||||||
|
state.positions[attr.pos],
|
||||||
|
hintfmt("in flake attribute '%s'", state.symbols[attr.name]));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,13 +152,13 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
try {
|
try {
|
||||||
input.ref = FlakeRef::fromAttrs(attrs);
|
input.ref = FlakeRef::fromAttrs(attrs);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("in flake input"));
|
e.addTrace(state.positions[pos], hintfmt("in flake input"));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
attrs.erase("url");
|
attrs.erase("url");
|
||||||
if (!attrs.empty())
|
if (!attrs.empty())
|
||||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
|
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
|
||||||
if (url)
|
if (url)
|
||||||
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
|
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
|
||||||
}
|
}
|
||||||
|
@ -168,7 +170,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
EvalState & state, Value * value, const Pos & pos,
|
EvalState & state, Value * value, const PosIdx pos,
|
||||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||||
{
|
{
|
||||||
std::map<FlakeId, FlakeInput> inputs;
|
std::map<FlakeId, FlakeInput> inputs;
|
||||||
|
@ -176,11 +178,11 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
expectType(state, nAttrs, *value, pos);
|
expectType(state, nAttrs, *value, pos);
|
||||||
|
|
||||||
for (nix::Attr & inputAttr : *(*value).attrs) {
|
for (nix::Attr & inputAttr : *(*value).attrs) {
|
||||||
inputs.emplace(inputAttr.name,
|
inputs.emplace(state.symbols[inputAttr.name],
|
||||||
parseFlakeInput(state,
|
parseFlakeInput(state,
|
||||||
inputAttr.name,
|
state.symbols[inputAttr.name],
|
||||||
inputAttr.value,
|
inputAttr.value,
|
||||||
*inputAttr.pos,
|
inputAttr.pos,
|
||||||
baseDir,
|
baseDir,
|
||||||
lockRootPath));
|
lockRootPath));
|
||||||
}
|
}
|
||||||
|
@ -218,28 +220,28 @@ static Flake getFlake(
|
||||||
Value vInfo;
|
Value vInfo;
|
||||||
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
|
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
|
||||||
|
|
||||||
expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));
|
expectType(state, nAttrs, vInfo, state.positions.add({flakeFile, foFile}, 0, 0));
|
||||||
|
|
||||||
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||||
expectType(state, nString, *description->value, *description->pos);
|
expectType(state, nString, *description->value, description->pos);
|
||||||
flake.description = description->value->string.s;
|
flake.description = description->value->string.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sInputs = state.symbols.create("inputs");
|
auto sInputs = state.symbols.create("inputs");
|
||||||
|
|
||||||
if (auto inputs = vInfo.attrs->get(sInputs))
|
if (auto inputs = vInfo.attrs->get(sInputs))
|
||||||
flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir, lockRootPath);
|
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath);
|
||||||
|
|
||||||
auto sOutputs = state.symbols.create("outputs");
|
auto sOutputs = state.symbols.create("outputs");
|
||||||
|
|
||||||
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
||||||
expectType(state, nFunction, *outputs->value, *outputs->pos);
|
expectType(state, nFunction, *outputs->value, outputs->pos);
|
||||||
|
|
||||||
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
|
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
|
||||||
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
||||||
if (formal.name != state.sSelf)
|
if (formal.name != state.sSelf)
|
||||||
flake.inputs.emplace(formal.name, FlakeInput {
|
flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
|
||||||
.ref = parseFlakeRef(formal.name)
|
.ref = parseFlakeRef(state.symbols[formal.name])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,35 +252,41 @@ static Flake getFlake(
|
||||||
auto sNixConfig = state.symbols.create("nixConfig");
|
auto sNixConfig = state.symbols.create("nixConfig");
|
||||||
|
|
||||||
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
||||||
expectType(state, nAttrs, *nixConfig->value, *nixConfig->pos);
|
expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
|
||||||
|
|
||||||
for (auto & setting : *nixConfig->value->attrs) {
|
for (auto & setting : *nixConfig->value->attrs) {
|
||||||
forceTrivialValue(state, *setting.value, *setting.pos);
|
forceTrivialValue(state, *setting.value, setting.pos);
|
||||||
if (setting.value->type() == nString)
|
if (setting.value->type() == nString)
|
||||||
flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, *setting.pos))});
|
flake.config.settings.emplace(
|
||||||
|
state.symbols[setting.name],
|
||||||
|
std::string(state.forceStringNoCtx(*setting.value, setting.pos)));
|
||||||
else if (setting.value->type() == nPath) {
|
else if (setting.value->type() == nPath) {
|
||||||
PathSet emptyContext = {};
|
PathSet emptyContext = {};
|
||||||
flake.config.settings.emplace(
|
flake.config.settings.emplace(
|
||||||
setting.name,
|
state.symbols[setting.name],
|
||||||
state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
|
state.coerceToString(setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
|
||||||
}
|
}
|
||||||
else if (setting.value->type() == nInt)
|
else if (setting.value->type() == nInt)
|
||||||
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
|
flake.config.settings.emplace(
|
||||||
|
state.symbols[setting.name],
|
||||||
|
state.forceInt(*setting.value, setting.pos));
|
||||||
else if (setting.value->type() == nBool)
|
else if (setting.value->type() == nBool)
|
||||||
flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, *setting.pos) }});
|
flake.config.settings.emplace(
|
||||||
|
state.symbols[setting.name],
|
||||||
|
Explicit<bool> { state.forceBool(*setting.value, setting.pos) });
|
||||||
else if (setting.value->type() == nList) {
|
else if (setting.value->type() == nList) {
|
||||||
std::vector<std::string> ss;
|
std::vector<std::string> ss;
|
||||||
for (auto elem : setting.value->listItems()) {
|
for (auto elem : setting.value->listItems()) {
|
||||||
if (elem->type() != nString)
|
if (elem->type() != nString)
|
||||||
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||||
setting.name, showType(*setting.value));
|
state.symbols[setting.name], showType(*setting.value));
|
||||||
ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos));
|
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos));
|
||||||
}
|
}
|
||||||
flake.config.settings.insert({setting.name, ss});
|
flake.config.settings.emplace(state.symbols[setting.name], ss);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw TypeError("flake configuration setting '%s' is %s",
|
throw TypeError("flake configuration setting '%s' is %s",
|
||||||
setting.name, showType(*setting.value));
|
state.symbols[setting.name], showType(*setting.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +296,7 @@ static Flake getFlake(
|
||||||
attr.name != sOutputs &&
|
attr.name != sOutputs &&
|
||||||
attr.name != sNixConfig)
|
attr.name != sNixConfig)
|
||||||
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
|
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
|
||||||
lockedRef, attr.name, *attr.pos);
|
lockedRef, state.symbols[attr.name], state.positions[attr.pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return flake;
|
return flake;
|
||||||
|
@ -704,12 +712,12 @@ void callFlake(EvalState & state,
|
||||||
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
|
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
|
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
|
||||||
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
||||||
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
||||||
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
|
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
|
||||||
|
|
||||||
callFlake(state,
|
callFlake(state,
|
||||||
lockFlake(state, flakeRef,
|
lockFlake(state, flakeRef,
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace nix {
|
||||||
|
|
||||||
struct FunctionCallTrace
|
struct FunctionCallTrace
|
||||||
{
|
{
|
||||||
const Pos & pos;
|
const Pos pos;
|
||||||
FunctionCallTrace(const Pos & pos);
|
FunctionCallTrace(const Pos & pos);
|
||||||
~FunctionCallTrace();
|
~FunctionCallTrace();
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,7 +61,7 @@ std::string DrvInfo::querySystem() const
|
||||||
{
|
{
|
||||||
if (system == "" && attrs) {
|
if (system == "" && attrs) {
|
||||||
auto i = attrs->find(state->sSystem);
|
auto i = attrs->find(state->sSystem);
|
||||||
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos);
|
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos);
|
||||||
}
|
}
|
||||||
return system;
|
return system;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath() const
|
||||||
if (i == attrs->end())
|
if (i == attrs->end())
|
||||||
drvPath = {std::nullopt};
|
drvPath = {std::nullopt};
|
||||||
else
|
else
|
||||||
drvPath = {state->coerceToStorePath(*i->pos, *i->value, context)};
|
drvPath = {state->coerceToStorePath(i->pos, *i->value, context)};
|
||||||
}
|
}
|
||||||
return drvPath.value_or(std::nullopt);
|
return drvPath.value_or(std::nullopt);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ StorePath DrvInfo::queryOutPath() const
|
||||||
Bindings::iterator i = attrs->find(state->sOutPath);
|
Bindings::iterator i = attrs->find(state->sOutPath);
|
||||||
PathSet context;
|
PathSet context;
|
||||||
if (i != attrs->end())
|
if (i != attrs->end())
|
||||||
outPath = state->coerceToStorePath(*i->pos, *i->value, context);
|
outPath = state->coerceToStorePath(i->pos, *i->value, context);
|
||||||
}
|
}
|
||||||
if (!outPath)
|
if (!outPath)
|
||||||
throw UnimplementedError("CA derivations are not yet supported");
|
throw UnimplementedError("CA derivations are not yet supported");
|
||||||
|
@ -109,23 +109,23 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
|
||||||
/* Get the ‘outputs’ list. */
|
/* Get the ‘outputs’ list. */
|
||||||
Bindings::iterator i;
|
Bindings::iterator i;
|
||||||
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
|
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
|
||||||
state->forceList(*i->value, *i->pos);
|
state->forceList(*i->value, i->pos);
|
||||||
|
|
||||||
/* For each output... */
|
/* For each output... */
|
||||||
for (auto elem : i->value->listItems()) {
|
for (auto elem : i->value->listItems()) {
|
||||||
std::string output(state->forceStringNoCtx(*elem, *i->pos));
|
std::string output(state->forceStringNoCtx(*elem, i->pos));
|
||||||
|
|
||||||
if (withPaths) {
|
if (withPaths) {
|
||||||
/* Evaluate the corresponding set. */
|
/* Evaluate the corresponding set. */
|
||||||
Bindings::iterator out = attrs->find(state->symbols.create(output));
|
Bindings::iterator out = attrs->find(state->symbols.create(output));
|
||||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||||
state->forceAttrs(*out->value, *i->pos);
|
state->forceAttrs(*out->value, i->pos);
|
||||||
|
|
||||||
/* And evaluate its ‘outPath’ attribute. */
|
/* And evaluate its ‘outPath’ attribute. */
|
||||||
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
||||||
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
||||||
PathSet context;
|
PathSet context;
|
||||||
outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context));
|
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context));
|
||||||
} else
|
} else
|
||||||
outputs.emplace(output, std::nullopt);
|
outputs.emplace(output, std::nullopt);
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ Bindings * DrvInfo::getMeta()
|
||||||
if (!attrs) return 0;
|
if (!attrs) return 0;
|
||||||
Bindings::iterator a = attrs->find(state->sMeta);
|
Bindings::iterator a = attrs->find(state->sMeta);
|
||||||
if (a == attrs->end()) return 0;
|
if (a == attrs->end()) return 0;
|
||||||
state->forceAttrs(*a->value, *a->pos);
|
state->forceAttrs(*a->value, a->pos);
|
||||||
meta = a->value->attrs;
|
meta = a->value->attrs;
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ StringSet DrvInfo::queryMetaNames()
|
||||||
StringSet res;
|
StringSet res;
|
||||||
if (!getMeta()) return res;
|
if (!getMeta()) return res;
|
||||||
for (auto & i : *meta)
|
for (auto & i : *meta)
|
||||||
res.insert(i.name);
|
res.emplace(state->symbols[i.name]);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ void DrvInfo::setMeta(const std::string & name, Value * v)
|
||||||
{
|
{
|
||||||
getMeta();
|
getMeta();
|
||||||
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
|
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
|
||||||
Symbol sym = state->symbols.create(name);
|
auto sym = state->symbols.create(name);
|
||||||
if (meta)
|
if (meta)
|
||||||
for (auto i : *meta)
|
for (auto i : *meta)
|
||||||
if (i.name != sym)
|
if (i.name != sym)
|
||||||
|
@ -356,11 +356,11 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||||
there are names clashes between derivations, the derivation
|
there are names clashes between derivations, the derivation
|
||||||
bound to the attribute with the "lower" name should take
|
bound to the attribute with the "lower" name should take
|
||||||
precedence). */
|
precedence). */
|
||||||
for (auto & i : v.attrs->lexicographicOrder()) {
|
for (auto & i : v.attrs->lexicographicOrder(state.symbols)) {
|
||||||
debug("evaluating attribute '%1%'", i->name);
|
debug("evaluating attribute '%1%'", state.symbols[i->name]);
|
||||||
if (!std::regex_match(std::string(i->name), attrRegex))
|
if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex))
|
||||||
continue;
|
continue;
|
||||||
std::string pathPrefix2 = addToPath(pathPrefix, i->name);
|
std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
|
||||||
if (combineChannels)
|
if (combineChannels)
|
||||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||||
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
||||||
|
@ -369,7 +369,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||||
`recurseForDerivations = true' attribute. */
|
`recurseForDerivations = true' attribute. */
|
||||||
if (i->value->type() == nAttrs) {
|
if (i->value->type() == nAttrs) {
|
||||||
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
||||||
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos))
|
if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos))
|
||||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,9 @@ using namespace nix;
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||||
{
|
{
|
||||||
return Pos(data->origin, data->file, loc.first_line, loc.first_column);
|
return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CUR_POS makeCurPos(*yylloc, data)
|
#define CUR_POS makeCurPos(*yylloc, data)
|
||||||
|
@ -155,7 +155,7 @@ or { return OR_KW; }
|
||||||
} catch (const boost::bad_lexical_cast &) {
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("invalid integer '%1%'", yytext),
|
.msg = hintfmt("invalid integer '%1%'", yytext),
|
||||||
.errPos = CUR_POS,
|
.errPos = data->state.positions[CUR_POS],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return INT;
|
return INT;
|
||||||
|
@ -165,7 +165,7 @@ or { return OR_KW; }
|
||||||
if (errno != 0)
|
if (errno != 0)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("invalid float '%1%'", yytext),
|
.msg = hintfmt("invalid float '%1%'", yytext),
|
||||||
.errPos = CUR_POS,
|
.errPos = data->state.positions[CUR_POS],
|
||||||
});
|
});
|
||||||
return FLOAT;
|
return FLOAT;
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ or { return OR_KW; }
|
||||||
<INPATH_SLASH><<EOF>> {
|
<INPATH_SLASH><<EOF>> {
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("path has a trailing slash"),
|
.msg = hintfmt("path has a trailing slash"),
|
||||||
.errPos = CUR_POS,
|
.errPos = data->state.positions[CUR_POS],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
#include "eval.hh"
|
||||||
|
#include "symbol-table.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
@ -10,12 +12,6 @@ namespace nix {
|
||||||
|
|
||||||
/* Displaying abstract syntax trees. */
|
/* Displaying abstract syntax trees. */
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Expr & e)
|
|
||||||
{
|
|
||||||
e.show(str);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void showString(std::ostream & str, std::string_view s)
|
static void showString(std::ostream & str, std::string_view s)
|
||||||
{
|
{
|
||||||
str << '"';
|
str << '"';
|
||||||
|
@ -54,81 +50,101 @@ static void showId(std::ostream & str, std::string_view s)
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Symbol & sym)
|
std::ostream & operator << (std::ostream & str, const Symbol & sym)
|
||||||
{
|
{
|
||||||
showId(str, *sym.s);
|
showId(str, sym.s);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Expr::show(std::ostream & str) const
|
void Expr::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprInt::show(std::ostream & str) const
|
void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << n;
|
str << n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprFloat::show(std::ostream & str) const
|
void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << nf;
|
str << nf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprString::show(std::ostream & str) const
|
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
showString(str, s);
|
showString(str, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPath::show(std::ostream & str) const
|
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << s;
|
str << s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprVar::show(std::ostream & str) const
|
void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << name;
|
str << symbols[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprSelect::show(std::ostream & str) const
|
void ExprSelect::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(" << *e << ")." << showAttrPath(attrPath);
|
str << "(";
|
||||||
if (def) str << " or (" << *def << ")";
|
e->show(symbols, str);
|
||||||
|
str << ")." << showAttrPath(symbols, attrPath);
|
||||||
|
if (def) {
|
||||||
|
str << " or (";
|
||||||
|
def->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpHasAttr::show(std::ostream & str) const
|
void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "((" << *e << ") ? " << showAttrPath(attrPath) << ")";
|
str << "((";
|
||||||
|
e->show(symbols, str);
|
||||||
|
str << ") ? " << showAttrPath(symbols, attrPath) << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAttrs::show(std::ostream & str) const
|
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
if (recursive) str << "rec ";
|
if (recursive) str << "rec ";
|
||||||
str << "{ ";
|
str << "{ ";
|
||||||
typedef const decltype(attrs)::value_type * Attr;
|
typedef const decltype(attrs)::value_type * Attr;
|
||||||
std::vector<Attr> sorted;
|
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::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
|
||||||
return (const std::string &) a->first < (const std::string &) b->first;
|
std::string_view sa = symbols[a->first], sb = symbols[b->first];
|
||||||
});
|
return sa < sb;
|
||||||
|
});
|
||||||
for (auto & i : sorted) {
|
for (auto & i : sorted) {
|
||||||
if (i->second.inherited)
|
if (i->second.inherited)
|
||||||
str << "inherit " << i->first << " " << "; ";
|
str << "inherit " << symbols[i->first] << " " << "; ";
|
||||||
else
|
else {
|
||||||
str << i->first << " = " << *i->second.e << "; ";
|
str << symbols[i->first] << " = ";
|
||||||
|
i->second.e->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto & i : dynamicAttrs) {
|
||||||
|
str << "\"${";
|
||||||
|
i.nameExpr->show(symbols, str);
|
||||||
|
str << "}\" = ";
|
||||||
|
i.valueExpr->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
}
|
}
|
||||||
for (auto & i : dynamicAttrs)
|
|
||||||
str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
|
|
||||||
str << "}";
|
str << "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprList::show(std::ostream & str) const
|
void ExprList::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "[ ";
|
str << "[ ";
|
||||||
for (auto & i : elems)
|
for (auto & i : elems) {
|
||||||
str << "(" << *i << ") ";
|
str << "(";
|
||||||
|
i->show(symbols, str);
|
||||||
|
str << ") ";
|
||||||
|
}
|
||||||
str << "]";
|
str << "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprLambda::show(std::ostream & str) const
|
void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(";
|
str << "(";
|
||||||
if (hasFormals()) {
|
if (hasFormals()) {
|
||||||
|
@ -136,74 +152,100 @@ void ExprLambda::show(std::ostream & str) const
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto & i : formals->formals) {
|
for (auto & i : formals->formals) {
|
||||||
if (first) first = false; else str << ", ";
|
if (first) first = false; else str << ", ";
|
||||||
str << i.name;
|
str << symbols[i.name];
|
||||||
if (i.def) str << " ? " << *i.def;
|
if (i.def) {
|
||||||
|
str << " ? ";
|
||||||
|
i.def->show(symbols, str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (formals->ellipsis) {
|
if (formals->ellipsis) {
|
||||||
if (!first) str << ", ";
|
if (!first) str << ", ";
|
||||||
str << "...";
|
str << "...";
|
||||||
}
|
}
|
||||||
str << " }";
|
str << " }";
|
||||||
if (!arg.empty()) str << " @ ";
|
if (arg) str << " @ ";
|
||||||
}
|
}
|
||||||
if (!arg.empty()) str << arg;
|
if (arg) str << symbols[arg];
|
||||||
str << ": " << *body << ")";
|
str << ": ";
|
||||||
|
body->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprCall::show(std::ostream & str) const
|
void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << '(' << *fun;
|
str << '(';
|
||||||
|
fun->show(symbols, str);
|
||||||
for (auto e : args) {
|
for (auto e : args) {
|
||||||
str << ' ';
|
str << ' ';
|
||||||
str << *e;
|
e->show(symbols, str);
|
||||||
}
|
}
|
||||||
str << ')';
|
str << ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprLet::show(std::ostream & str) const
|
void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(let ";
|
str << "(let ";
|
||||||
for (auto & i : attrs->attrs)
|
for (auto & i : attrs->attrs)
|
||||||
if (i.second.inherited) {
|
if (i.second.inherited) {
|
||||||
str << "inherit " << i.first << "; ";
|
str << "inherit " << symbols[i.first] << "; ";
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
str << i.first << " = " << *i.second.e << "; ";
|
str << symbols[i.first] << " = ";
|
||||||
str << "in " << *body << ")";
|
i.second.e->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
|
}
|
||||||
|
str << "in ";
|
||||||
|
body->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprWith::show(std::ostream & str) const
|
void ExprWith::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(with " << *attrs << "; " << *body << ")";
|
str << "(with ";
|
||||||
|
attrs->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
|
body->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprIf::show(std::ostream & str) const
|
void ExprIf::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(if " << *cond << " then " << *then << " else " << *else_ << ")";
|
str << "(if ";
|
||||||
|
cond->show(symbols, str);
|
||||||
|
str << " then ";
|
||||||
|
then->show(symbols, str);
|
||||||
|
str << " else ";
|
||||||
|
else_->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAssert::show(std::ostream & str) const
|
void ExprAssert::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "assert " << *cond << "; " << *body;
|
str << "assert ";
|
||||||
|
cond->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
|
body->show(symbols, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpNot::show(std::ostream & str) const
|
void ExprOpNot::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(! " << *e << ")";
|
str << "(! ";
|
||||||
|
e->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprConcatStrings::show(std::ostream & str) const
|
void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
bool first = true;
|
bool first = true;
|
||||||
str << "(";
|
str << "(";
|
||||||
for (auto & i : *es) {
|
for (auto & i : *es) {
|
||||||
if (first) first = false; else str << " + ";
|
if (first) first = false; else str << " + ";
|
||||||
str << *i.second;
|
i.second->show(symbols, str);
|
||||||
}
|
}
|
||||||
str << ")";
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPos::show(std::ostream & str) const
|
void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "__curPos";
|
str << "__curPos";
|
||||||
}
|
}
|
||||||
|
@ -234,48 +276,49 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string showAttrPath(const AttrPath & attrPath)
|
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto & i : attrPath) {
|
for (auto & i : attrPath) {
|
||||||
if (!first) out << '.'; else first = false;
|
if (!first) out << '.'; else first = false;
|
||||||
if (i.symbol.set())
|
if (i.symbol)
|
||||||
out << i.symbol;
|
out << symbols[i.symbol];
|
||||||
else
|
else {
|
||||||
out << "\"${" << *i.expr << "}\"";
|
out << "\"${";
|
||||||
|
i.expr->show(symbols, out);
|
||||||
|
out << "}\"";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pos noPos;
|
|
||||||
|
|
||||||
|
|
||||||
/* Computing levels/displacements for variables. */
|
/* Computing levels/displacements for variables. */
|
||||||
|
|
||||||
void Expr::bindVars(const StaticEnv & env)
|
void Expr::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprInt::bindVars(const StaticEnv & env)
|
void ExprInt::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprFloat::bindVars(const StaticEnv & env)
|
void ExprFloat::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprString::bindVars(const StaticEnv & env)
|
void ExprString::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPath::bindVars(const StaticEnv & env)
|
void ExprPath::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprVar::bindVars(const StaticEnv & env)
|
void ExprVar::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
/* Check whether the variable appears in the environment. If so,
|
/* Check whether the variable appears in the environment. If so,
|
||||||
set its level and displacement. */
|
set its level and displacement. */
|
||||||
|
@ -301,31 +344,31 @@ void ExprVar::bindVars(const StaticEnv & env)
|
||||||
"undefined variable" error now. */
|
"undefined variable" error now. */
|
||||||
if (withLevel == -1)
|
if (withLevel == -1)
|
||||||
throw UndefinedVarError({
|
throw UndefinedVarError({
|
||||||
.msg = hintfmt("undefined variable '%1%'", name),
|
.msg = hintfmt("undefined variable '%1%'", es.symbols[name]),
|
||||||
.errPos = pos
|
.errPos = es.positions[pos]
|
||||||
});
|
});
|
||||||
fromWith = true;
|
fromWith = true;
|
||||||
this->level = withLevel;
|
this->level = withLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprSelect::bindVars(const StaticEnv & env)
|
void ExprSelect::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
e->bindVars(env);
|
e->bindVars(es, env);
|
||||||
if (def) def->bindVars(env);
|
if (def) def->bindVars(es, env);
|
||||||
for (auto & i : attrPath)
|
for (auto & i : attrPath)
|
||||||
if (!i.symbol.set())
|
if (!i.symbol)
|
||||||
i.expr->bindVars(env);
|
i.expr->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpHasAttr::bindVars(const StaticEnv & env)
|
void ExprOpHasAttr::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
e->bindVars(env);
|
e->bindVars(es, env);
|
||||||
for (auto & i : attrPath)
|
for (auto & i : attrPath)
|
||||||
if (!i.symbol.set())
|
if (!i.symbol)
|
||||||
i.expr->bindVars(env);
|
i.expr->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAttrs::bindVars(const StaticEnv & env)
|
void ExprAttrs::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
const StaticEnv * dynamicEnv = &env;
|
const StaticEnv * dynamicEnv = &env;
|
||||||
StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0);
|
StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0);
|
||||||
|
@ -340,35 +383,35 @@ void ExprAttrs::bindVars(const StaticEnv & env)
|
||||||
// No need to sort newEnv since attrs is in sorted order.
|
// No need to sort newEnv since attrs is in sorted order.
|
||||||
|
|
||||||
for (auto & i : attrs)
|
for (auto & i : attrs)
|
||||||
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
for (auto & i : attrs)
|
for (auto & i : attrs)
|
||||||
i.second.e->bindVars(env);
|
i.second.e->bindVars(es, env);
|
||||||
|
|
||||||
for (auto & i : dynamicAttrs) {
|
for (auto & i : dynamicAttrs) {
|
||||||
i.nameExpr->bindVars(*dynamicEnv);
|
i.nameExpr->bindVars(es, *dynamicEnv);
|
||||||
i.valueExpr->bindVars(*dynamicEnv);
|
i.valueExpr->bindVars(es, *dynamicEnv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprList::bindVars(const StaticEnv & env)
|
void ExprList::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
for (auto & i : elems)
|
for (auto & i : elems)
|
||||||
i->bindVars(env);
|
i->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprLambda::bindVars(const StaticEnv & env)
|
void ExprLambda::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
StaticEnv newEnv(
|
StaticEnv newEnv(
|
||||||
false, &env,
|
false, &env,
|
||||||
(hasFormals() ? formals->formals.size() : 0) +
|
(hasFormals() ? formals->formals.size() : 0) +
|
||||||
(arg.empty() ? 0 : 1));
|
(!arg ? 0 : 1));
|
||||||
|
|
||||||
Displacement displ = 0;
|
Displacement displ = 0;
|
||||||
|
|
||||||
if (!arg.empty()) newEnv.vars.emplace_back(arg, displ++);
|
if (arg) newEnv.vars.emplace_back(arg, displ++);
|
||||||
|
|
||||||
if (hasFormals()) {
|
if (hasFormals()) {
|
||||||
for (auto & i : formals->formals)
|
for (auto & i : formals->formals)
|
||||||
|
@ -377,20 +420,20 @@ void ExprLambda::bindVars(const StaticEnv & env)
|
||||||
newEnv.sort();
|
newEnv.sort();
|
||||||
|
|
||||||
for (auto & i : formals->formals)
|
for (auto & i : formals->formals)
|
||||||
if (i.def) i.def->bindVars(newEnv);
|
if (i.def) i.def->bindVars(es, newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
body->bindVars(newEnv);
|
body->bindVars(es, newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprCall::bindVars(const StaticEnv & env)
|
void ExprCall::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
fun->bindVars(env);
|
fun->bindVars(es, env);
|
||||||
for (auto e : args)
|
for (auto e : args)
|
||||||
e->bindVars(env);
|
e->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprLet::bindVars(const StaticEnv & env)
|
void ExprLet::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
StaticEnv newEnv(false, &env, attrs->attrs.size());
|
StaticEnv newEnv(false, &env, attrs->attrs.size());
|
||||||
|
|
||||||
|
@ -401,12 +444,12 @@ void ExprLet::bindVars(const StaticEnv & env)
|
||||||
// No need to sort newEnv since attrs->attrs is in sorted order.
|
// No need to sort newEnv since attrs->attrs is in sorted order.
|
||||||
|
|
||||||
for (auto & i : attrs->attrs)
|
for (auto & i : attrs->attrs)
|
||||||
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
|
||||||
|
|
||||||
body->bindVars(newEnv);
|
body->bindVars(es, newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprWith::bindVars(const StaticEnv & env)
|
void ExprWith::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
/* Does this `with' have an enclosing `with'? If so, record its
|
/* Does this `with' have an enclosing `with'? If so, record its
|
||||||
level so that `lookupVar' can look up variables in the previous
|
level so that `lookupVar' can look up variables in the previous
|
||||||
|
@ -420,57 +463,60 @@ void ExprWith::bindVars(const StaticEnv & env)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs->bindVars(env);
|
attrs->bindVars(es, env);
|
||||||
StaticEnv newEnv(true, &env);
|
StaticEnv newEnv(true, &env);
|
||||||
body->bindVars(newEnv);
|
body->bindVars(es, newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprIf::bindVars(const StaticEnv & env)
|
void ExprIf::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
cond->bindVars(env);
|
cond->bindVars(es, env);
|
||||||
then->bindVars(env);
|
then->bindVars(es, env);
|
||||||
else_->bindVars(env);
|
else_->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAssert::bindVars(const StaticEnv & env)
|
void ExprAssert::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
cond->bindVars(env);
|
cond->bindVars(es, env);
|
||||||
body->bindVars(env);
|
body->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpNot::bindVars(const StaticEnv & env)
|
void ExprOpNot::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
e->bindVars(env);
|
e->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprConcatStrings::bindVars(const StaticEnv & env)
|
void ExprConcatStrings::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
for (auto & i : *es)
|
for (auto & i : *this->es)
|
||||||
i.second->bindVars(env);
|
i.second->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPos::bindVars(const StaticEnv & env)
|
void ExprPos::bindVars(const EvalState & es, const StaticEnv & env)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Storing function names. */
|
/* Storing function names. */
|
||||||
|
|
||||||
void Expr::setName(Symbol & name)
|
void Expr::setName(SymbolIdx name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprLambda::setName(Symbol & name)
|
void ExprLambda::setName(SymbolIdx name)
|
||||||
{
|
{
|
||||||
this->name = name;
|
this->name = name;
|
||||||
body->setName(name);
|
body->setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string ExprLambda::showNamePos() const
|
std::string ExprLambda::showNamePos(const EvalState & state) const
|
||||||
{
|
{
|
||||||
return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pos);
|
std::string id(name
|
||||||
|
? concatStrings("'", state.symbols[name], "'")
|
||||||
|
: "anonymous function");
|
||||||
|
return fmt("%1% at %2%", id, state.positions[pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -480,8 +526,7 @@ std::string ExprLambda::showNamePos() const
|
||||||
size_t SymbolTable::totalSize() const
|
size_t SymbolTable::totalSize() const
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
for (auto & i : store)
|
dump([&] (const Symbol & s) { n += std::string_view(s).size(); });
|
||||||
n += i.size();
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "value.hh"
|
#include "value.hh"
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
#include "error.hh"
|
#include "error.hh"
|
||||||
|
#include "chunked-vector.hh"
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -23,32 +27,92 @@ MakeError(RestrictedPathError, Error);
|
||||||
|
|
||||||
struct Pos
|
struct Pos
|
||||||
{
|
{
|
||||||
Symbol file;
|
std::string file;
|
||||||
|
FileOrigin origin;
|
||||||
uint32_t line;
|
uint32_t line;
|
||||||
FileOrigin origin:2;
|
uint32_t column;
|
||||||
uint32_t column:30;
|
|
||||||
Pos() : line(0), origin(foString), column(0) { };
|
explicit operator bool() const { return line > 0; }
|
||||||
Pos(FileOrigin origin, const Symbol & file, uint32_t line, uint32_t column)
|
};
|
||||||
: file(file), line(line), origin(origin), column(column) { };
|
|
||||||
operator bool() const
|
class PosIdx {
|
||||||
|
friend class PosTable;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
|
explicit PosIdx(uint32_t id): id(id) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
PosIdx() : id(0) {}
|
||||||
|
|
||||||
|
explicit operator bool() const { return id > 0; }
|
||||||
|
|
||||||
|
bool operator<(const PosIdx other) const { return id < other.id; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class PosTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class Origin {
|
||||||
|
friend PosTable;
|
||||||
|
private:
|
||||||
|
// must always be invalid by default, add() replaces this with the actual value.
|
||||||
|
// subsequent add() calls use this index as a token to quickly check whether the
|
||||||
|
// current origins.back() can be reused or not.
|
||||||
|
mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
|
||||||
|
|
||||||
|
explicit Origin(uint32_t idx): idx(idx), file{}, origin{} {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const std::string file;
|
||||||
|
const FileOrigin origin;
|
||||||
|
|
||||||
|
Origin(std::string file, FileOrigin origin): file(std::move(file)), origin(origin) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Offset {
|
||||||
|
uint32_t line, column;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Origin> origins;
|
||||||
|
ChunkedVector<Offset, 8192> offsets;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PosTable(): offsets(1024)
|
||||||
{
|
{
|
||||||
return line != 0;
|
origins.reserve(1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator < (const Pos & p2) const
|
PosIdx add(const Origin & origin, uint32_t line, uint32_t column)
|
||||||
{
|
{
|
||||||
if (!line) return p2.line;
|
const auto idx = offsets.add({line, column}).second;
|
||||||
if (!p2.line) return false;
|
if (origins.empty() || origins.back().idx != origin.idx) {
|
||||||
int d = ((const std::string &) file).compare((const std::string &) p2.file);
|
origin.idx = idx;
|
||||||
if (d < 0) return true;
|
origins.push_back(origin);
|
||||||
if (d > 0) return false;
|
}
|
||||||
if (line < p2.line) return true;
|
return PosIdx(idx + 1);
|
||||||
if (line > p2.line) return false;
|
}
|
||||||
return column < p2.column;
|
|
||||||
|
Pos operator[](PosIdx p) const
|
||||||
|
{
|
||||||
|
if (p.id == 0 || p.id > offsets.size())
|
||||||
|
return {};
|
||||||
|
const auto idx = p.id - 1;
|
||||||
|
/* we want the last key <= idx, so we'll take prev(first key > idx).
|
||||||
|
this is guaranteed to never rewind origin.begin because the first
|
||||||
|
key is always 0. */
|
||||||
|
const auto pastOrigin = std::upper_bound(
|
||||||
|
origins.begin(), origins.end(), Origin(idx),
|
||||||
|
[] (const auto & a, const auto & b) { return a.idx < b.idx; });
|
||||||
|
const auto origin = *std::prev(pastOrigin);
|
||||||
|
const auto offset = offsets[idx];
|
||||||
|
return {origin.file, origin.origin, offset.line, offset.column};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Pos noPos;
|
inline PosIdx noPos = {};
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Pos & pos);
|
std::ostream & operator << (std::ostream & str, const Pos & pos);
|
||||||
|
|
||||||
|
@ -62,15 +126,15 @@ struct StaticEnv;
|
||||||
/* An attribute path is a sequence of attribute names. */
|
/* An attribute path is a sequence of attribute names. */
|
||||||
struct AttrName
|
struct AttrName
|
||||||
{
|
{
|
||||||
Symbol symbol;
|
SymbolIdx symbol;
|
||||||
Expr * expr;
|
Expr * expr;
|
||||||
AttrName(const Symbol & s) : symbol(s) {};
|
AttrName(const SymbolIdx & s) : symbol(s) {};
|
||||||
AttrName(Expr * e) : expr(e) {};
|
AttrName(Expr * e) : expr(e) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<AttrName> AttrPath;
|
typedef std::vector<AttrName> AttrPath;
|
||||||
|
|
||||||
std::string showAttrPath(const AttrPath & attrPath);
|
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath);
|
||||||
|
|
||||||
|
|
||||||
/* Abstract syntax of Nix expressions. */
|
/* Abstract syntax of Nix expressions. */
|
||||||
|
@ -78,19 +142,17 @@ std::string showAttrPath(const AttrPath & attrPath);
|
||||||
struct Expr
|
struct Expr
|
||||||
{
|
{
|
||||||
virtual ~Expr() { };
|
virtual ~Expr() { };
|
||||||
virtual void show(std::ostream & str) const;
|
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
|
||||||
virtual void bindVars(const StaticEnv & env);
|
virtual void bindVars(const EvalState & es, const StaticEnv & env);
|
||||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||||
virtual Value * maybeThunk(EvalState & state, Env & env);
|
virtual Value * maybeThunk(EvalState & state, Env & env);
|
||||||
virtual void setName(Symbol & name);
|
virtual void setName(SymbolIdx name);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Expr & e);
|
|
||||||
|
|
||||||
#define COMMON_METHODS \
|
#define COMMON_METHODS \
|
||||||
void show(std::ostream & str) const; \
|
void show(const SymbolTable & symbols, std::ostream & str) const; \
|
||||||
void eval(EvalState & state, Env & env, Value & v); \
|
void eval(EvalState & state, Env & env, Value & v); \
|
||||||
void bindVars(const StaticEnv & env);
|
void bindVars(const EvalState & es, const StaticEnv & env);
|
||||||
|
|
||||||
struct ExprInt : Expr
|
struct ExprInt : Expr
|
||||||
{
|
{
|
||||||
|
@ -133,8 +195,8 @@ typedef uint32_t Displacement;
|
||||||
|
|
||||||
struct ExprVar : Expr
|
struct ExprVar : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Symbol name;
|
SymbolIdx name;
|
||||||
|
|
||||||
/* Whether the variable comes from an environment (e.g. a rec, let
|
/* Whether the variable comes from an environment (e.g. a rec, let
|
||||||
or function argument) or from a "with". */
|
or function argument) or from a "with". */
|
||||||
|
@ -149,19 +211,19 @@ struct ExprVar : Expr
|
||||||
Level level;
|
Level level;
|
||||||
Displacement displ;
|
Displacement displ;
|
||||||
|
|
||||||
ExprVar(const Symbol & name) : name(name) { };
|
ExprVar(const SymbolIdx & name) : name(name) { };
|
||||||
ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { };
|
ExprVar(const PosIdx & pos, const SymbolIdx & name) : pos(pos), name(name) { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprSelect : Expr
|
struct ExprSelect : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Expr * e, * def;
|
Expr * e, * def;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprSelect(const Pos & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
|
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
|
||||||
ExprSelect(const Pos & pos, Expr * e, const Symbol & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
ExprSelect(const PosIdx & pos, Expr * e, const SymbolIdx & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,28 +238,28 @@ struct ExprOpHasAttr : Expr
|
||||||
struct ExprAttrs : Expr
|
struct ExprAttrs : Expr
|
||||||
{
|
{
|
||||||
bool recursive;
|
bool recursive;
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
struct AttrDef {
|
struct AttrDef {
|
||||||
bool inherited;
|
bool inherited;
|
||||||
Expr * e;
|
Expr * e;
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Displacement displ; // displacement
|
Displacement displ; // displacement
|
||||||
AttrDef(Expr * e, const Pos & pos, bool inherited=false)
|
AttrDef(Expr * e, const PosIdx & pos, bool inherited=false)
|
||||||
: inherited(inherited), e(e), pos(pos) { };
|
: inherited(inherited), e(e), pos(pos) { };
|
||||||
AttrDef() { };
|
AttrDef() { };
|
||||||
};
|
};
|
||||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
typedef std::map<SymbolIdx, AttrDef> AttrDefs;
|
||||||
AttrDefs attrs;
|
AttrDefs attrs;
|
||||||
struct DynamicAttrDef {
|
struct DynamicAttrDef {
|
||||||
Expr * nameExpr, * valueExpr;
|
Expr * nameExpr, * valueExpr;
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const Pos & 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;
|
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
||||||
DynamicAttrDefs dynamicAttrs;
|
DynamicAttrDefs dynamicAttrs;
|
||||||
ExprAttrs(const Pos &pos) : recursive(false), pos(pos) { };
|
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
|
||||||
ExprAttrs() : recursive(false), pos(noPos) { };
|
ExprAttrs() : recursive(false) { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -210,10 +272,9 @@ struct ExprList : Expr
|
||||||
|
|
||||||
struct Formal
|
struct Formal
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Symbol name;
|
SymbolIdx name;
|
||||||
Expr * def;
|
Expr * def;
|
||||||
Formal(const Pos & pos, const Symbol & name, Expr * def) : pos(pos), name(name), def(def) { };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Formals
|
struct Formals
|
||||||
|
@ -222,18 +283,19 @@ struct Formals
|
||||||
Formals_ formals;
|
Formals_ formals;
|
||||||
bool ellipsis;
|
bool ellipsis;
|
||||||
|
|
||||||
bool has(Symbol arg) const {
|
bool has(SymbolIdx arg) const {
|
||||||
auto it = std::lower_bound(formals.begin(), formals.end(), arg,
|
auto it = std::lower_bound(formals.begin(), formals.end(), arg,
|
||||||
[] (const Formal & f, const Symbol & sym) { return f.name < sym; });
|
[] (const Formal & f, const SymbolIdx & sym) { return f.name < sym; });
|
||||||
return it != formals.end() && it->name == arg;
|
return it != formals.end() && it->name == arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Formal> lexicographicOrder() const
|
std::vector<Formal> lexicographicOrder(const SymbolTable & symbols) const
|
||||||
{
|
{
|
||||||
std::vector<Formal> result(formals.begin(), formals.end());
|
std::vector<Formal> result(formals.begin(), formals.end());
|
||||||
std::sort(result.begin(), result.end(),
|
std::sort(result.begin(), result.end(),
|
||||||
[] (const Formal & a, const Formal & b) {
|
[&] (const Formal & a, const Formal & b) {
|
||||||
return std::string_view(a.name) < std::string_view(b.name);
|
std::string_view sa = symbols[a.name], sb = symbols[b.name];
|
||||||
|
return sa < sb;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -241,17 +303,21 @@ struct Formals
|
||||||
|
|
||||||
struct ExprLambda : Expr
|
struct ExprLambda : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Symbol name;
|
SymbolIdx name;
|
||||||
Symbol arg;
|
SymbolIdx arg;
|
||||||
Formals * formals;
|
Formals * formals;
|
||||||
Expr * body;
|
Expr * body;
|
||||||
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
|
ExprLambda(PosIdx pos, SymbolIdx arg, Formals * formals, Expr * body)
|
||||||
: pos(pos), arg(arg), formals(formals), body(body)
|
: pos(pos), arg(arg), formals(formals), body(body)
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
void setName(Symbol & name);
|
ExprLambda(PosIdx pos, Formals * formals, Expr * body)
|
||||||
std::string showNamePos() const;
|
: pos(pos), formals(formals), body(body)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void setName(SymbolIdx name);
|
||||||
|
std::string showNamePos(const EvalState & state) const;
|
||||||
inline bool hasFormals() const { return formals != nullptr; }
|
inline bool hasFormals() const { return formals != nullptr; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
@ -260,8 +326,8 @@ struct ExprCall : Expr
|
||||||
{
|
{
|
||||||
Expr * fun;
|
Expr * fun;
|
||||||
std::vector<Expr *> args;
|
std::vector<Expr *> args;
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
ExprCall(const Pos & pos, Expr * fun, std::vector<Expr *> && args)
|
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
|
||||||
: fun(fun), args(args), pos(pos)
|
: fun(fun), args(args), pos(pos)
|
||||||
{ }
|
{ }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
|
@ -277,26 +343,26 @@ struct ExprLet : Expr
|
||||||
|
|
||||||
struct ExprWith : Expr
|
struct ExprWith : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Expr * attrs, * body;
|
Expr * attrs, * body;
|
||||||
size_t prevWith;
|
size_t prevWith;
|
||||||
ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprIf : Expr
|
struct ExprIf : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Expr * cond, * then, * else_;
|
Expr * cond, * then, * else_;
|
||||||
ExprIf(const Pos & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
|
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprAssert : Expr
|
struct ExprAssert : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Expr * cond, * body;
|
Expr * cond, * body;
|
||||||
ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -310,17 +376,17 @@ struct ExprOpNot : Expr
|
||||||
#define MakeBinOp(name, s) \
|
#define MakeBinOp(name, s) \
|
||||||
struct name : Expr \
|
struct name : Expr \
|
||||||
{ \
|
{ \
|
||||||
Pos pos; \
|
PosIdx pos; \
|
||||||
Expr * e1, * e2; \
|
Expr * e1, * e2; \
|
||||||
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
|
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
|
||||||
name(const Pos & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
|
name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
|
||||||
void show(std::ostream & str) const \
|
void show(const SymbolTable & symbols, std::ostream & str) const \
|
||||||
{ \
|
{ \
|
||||||
str << "(" << *e1 << " " s " " << *e2 << ")"; \
|
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
|
||||||
} \
|
} \
|
||||||
void bindVars(const StaticEnv & env) \
|
void bindVars(const EvalState & es, const StaticEnv & env) \
|
||||||
{ \
|
{ \
|
||||||
e1->bindVars(env); e2->bindVars(env); \
|
e1->bindVars(es, env); e2->bindVars(es, env); \
|
||||||
} \
|
} \
|
||||||
void eval(EvalState & state, Env & env, Value & v); \
|
void eval(EvalState & state, Env & env, Value & v); \
|
||||||
};
|
};
|
||||||
|
@ -335,18 +401,18 @@ MakeBinOp(ExprOpConcatLists, "++")
|
||||||
|
|
||||||
struct ExprConcatStrings : Expr
|
struct ExprConcatStrings : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
bool forceString;
|
bool forceString;
|
||||||
std::vector<std::pair<Pos, Expr *> > * es;
|
std::vector<std::pair<PosIdx, Expr *> > * es;
|
||||||
ExprConcatStrings(const Pos & pos, bool forceString, std::vector<std::pair<Pos, Expr *> > * es)
|
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *> > * es)
|
||||||
: pos(pos), forceString(forceString), es(es) { };
|
: pos(pos), forceString(forceString), es(es) { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprPos : Expr
|
struct ExprPos : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
ExprPos(const Pos & pos) : pos(pos) { };
|
ExprPos(const PosIdx & pos) : pos(pos) { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -360,7 +426,7 @@ struct StaticEnv
|
||||||
const StaticEnv * up;
|
const StaticEnv * up;
|
||||||
|
|
||||||
// Note: these must be in sorted order.
|
// Note: these must be in sorted order.
|
||||||
typedef std::vector<std::pair<Symbol, Displacement>> Vars;
|
typedef std::vector<std::pair<SymbolIdx, Displacement>> Vars;
|
||||||
Vars vars;
|
Vars vars;
|
||||||
|
|
||||||
StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) {
|
StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) {
|
||||||
|
@ -384,7 +450,7 @@ struct StaticEnv
|
||||||
vars.erase(it, end);
|
vars.erase(it, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vars::const_iterator find(const Symbol & name) const
|
Vars::const_iterator find(const SymbolIdx & name) const
|
||||||
{
|
{
|
||||||
Vars::value_type key(name, 0);
|
Vars::value_type key(name, 0);
|
||||||
auto i = std::lower_bound(vars.begin(), vars.end(), key);
|
auto i = std::lower_bound(vars.begin(), vars.end(), key);
|
||||||
|
|
|
@ -32,12 +32,12 @@ namespace nix {
|
||||||
SymbolTable & symbols;
|
SymbolTable & symbols;
|
||||||
Expr * result;
|
Expr * result;
|
||||||
Path basePath;
|
Path basePath;
|
||||||
Symbol file;
|
PosTable::Origin origin;
|
||||||
FileOrigin origin;
|
|
||||||
std::optional<ErrorInfo> error;
|
std::optional<ErrorInfo> error;
|
||||||
ParseData(EvalState & state)
|
ParseData(EvalState & state, PosTable::Origin origin)
|
||||||
: state(state)
|
: state(state)
|
||||||
, symbols(state.symbols)
|
, symbols(state.symbols)
|
||||||
|
, origin(std::move(origin))
|
||||||
{ };
|
{ };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,26 +77,26 @@ using namespace nix;
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
|
static void dupAttr(const EvalState & state, const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
|
||||||
{
|
{
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("attribute '%1%' already defined at %2%",
|
.msg = hintfmt("attribute '%1%' already defined at %2%",
|
||||||
showAttrPath(attrPath), prevPos),
|
showAttrPath(state.symbols, attrPath), state.positions[prevPos]),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
static void dupAttr(const EvalState & state, SymbolIdx attr, const PosIdx pos, const PosIdx prevPos)
|
||||||
{
|
{
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos),
|
.msg = hintfmt("attribute '%1%' already defined at %2%", state.symbols[attr], state.positions[prevPos]),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
Expr * e, const Pos & pos)
|
Expr * e, const PosIdx pos, const nix::EvalState & state)
|
||||||
{
|
{
|
||||||
AttrPath::iterator i;
|
AttrPath::iterator i;
|
||||||
// All attrpaths have at least one attr
|
// All attrpaths have at least one attr
|
||||||
|
@ -104,15 +104,15 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
// Checking attrPath validity.
|
// Checking attrPath validity.
|
||||||
// ===========================
|
// ===========================
|
||||||
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
|
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
|
||||||
if (i->symbol.set()) {
|
if (i->symbol) {
|
||||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||||
if (j != attrs->attrs.end()) {
|
if (j != attrs->attrs.end()) {
|
||||||
if (!j->second.inherited) {
|
if (!j->second.inherited) {
|
||||||
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
|
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||||
if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
|
if (!attrs2) dupAttr(state, attrPath, pos, j->second.pos);
|
||||||
attrs = attrs2;
|
attrs = attrs2;
|
||||||
} else
|
} else
|
||||||
dupAttr(attrPath, pos, j->second.pos);
|
dupAttr(state, attrPath, pos, j->second.pos);
|
||||||
} else {
|
} else {
|
||||||
ExprAttrs * nested = new ExprAttrs;
|
ExprAttrs * nested = new ExprAttrs;
|
||||||
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
||||||
|
@ -126,7 +126,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
}
|
}
|
||||||
// Expr insertion.
|
// Expr insertion.
|
||||||
// ==========================
|
// ==========================
|
||||||
if (i->symbol.set()) {
|
if (i->symbol) {
|
||||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||||
if (j != attrs->attrs.end()) {
|
if (j != attrs->attrs.end()) {
|
||||||
// This attr path is already defined. However, if both
|
// This attr path is already defined. However, if both
|
||||||
|
@ -139,11 +139,11 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
for (auto & ad : ae->attrs) {
|
for (auto & ad : ae->attrs) {
|
||||||
auto j2 = jAttrs->attrs.find(ad.first);
|
auto j2 = jAttrs->attrs.find(ad.first);
|
||||||
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
|
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
|
||||||
dupAttr(ad.first, j2->second.pos, ad.second.pos);
|
dupAttr(state, ad.first, j2->second.pos, ad.second.pos);
|
||||||
jAttrs->attrs.emplace(ad.first, ad.second);
|
jAttrs->attrs.emplace(ad.first, ad.second);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dupAttr(attrPath, pos, j->second.pos);
|
dupAttr(state, attrPath, pos, j->second.pos);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This attr path is not defined. Let's create it.
|
// This attr path is not defined. Let's create it.
|
||||||
|
@ -157,14 +157,14 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
|
|
||||||
|
|
||||||
static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
||||||
Pos pos = noPos, Symbol arg = {})
|
PosIdx pos = noPos, SymbolIdx arg = {})
|
||||||
{
|
{
|
||||||
std::sort(formals->formals.begin(), formals->formals.end(),
|
std::sort(formals->formals.begin(), formals->formals.end(),
|
||||||
[] (const auto & a, const auto & b) {
|
[] (const auto & a, const auto & b) {
|
||||||
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
|
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::optional<std::pair<Symbol, Pos>> duplicate;
|
std::optional<std::pair<SymbolIdx, PosIdx>> duplicate;
|
||||||
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
|
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
|
||||||
if (formals->formals[i].name != formals->formals[i + 1].name)
|
if (formals->formals[i].name != formals->formals[i + 1].name)
|
||||||
continue;
|
continue;
|
||||||
|
@ -173,18 +173,18 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
||||||
}
|
}
|
||||||
if (duplicate)
|
if (duplicate)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first),
|
.msg = hintfmt("duplicate formal function argument '%1%'", data.symbols[duplicate->first]),
|
||||||
.errPos = duplicate->second
|
.errPos = data.state.positions[duplicate->second]
|
||||||
});
|
});
|
||||||
|
|
||||||
Formals result;
|
Formals result;
|
||||||
result.ellipsis = formals->ellipsis;
|
result.ellipsis = formals->ellipsis;
|
||||||
result.formals = std::move(formals->formals);
|
result.formals = std::move(formals->formals);
|
||||||
|
|
||||||
if (arg.set() && result.has(arg))
|
if (arg && result.has(arg))
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("duplicate formal function argument '%1%'", arg),
|
.msg = hintfmt("duplicate formal function argument '%1%'", data.symbols[arg]),
|
||||||
.errPos = pos
|
.errPos = data.state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
delete formals;
|
delete formals;
|
||||||
|
@ -192,8 +192,8 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
|
static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
||||||
std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > > & es)
|
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > > & es)
|
||||||
{
|
{
|
||||||
if (es.empty()) return new ExprString("");
|
if (es.empty()) return new ExprString("");
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Strip spaces from each line. */
|
/* Strip spaces from each line. */
|
||||||
std::vector<std::pair<Pos, Expr *> > * es2 = new std::vector<std::pair<Pos, Expr *> >;
|
auto * es2 = new std::vector<std::pair<PosIdx, Expr *> >;
|
||||||
atStartOfLine = true;
|
atStartOfLine = true;
|
||||||
size_t curDropped = 0;
|
size_t curDropped = 0;
|
||||||
size_t n = es.size();
|
size_t n = es.size();
|
||||||
|
@ -284,9 +284,9 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||||
{
|
{
|
||||||
return Pos(data->origin, data->file, loc.first_line, loc.first_column);
|
return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CUR_POS makeCurPos(*yylocp, data)
|
#define CUR_POS makeCurPos(*yylocp, data)
|
||||||
|
@ -299,7 +299,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
{
|
{
|
||||||
data->error = {
|
data->error = {
|
||||||
.msg = hintfmt(error),
|
.msg = hintfmt(error),
|
||||||
.errPos = makeCurPos(*loc, data)
|
.errPos = data->state.positions[makeCurPos(*loc, data)]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,8 +320,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
StringToken uri;
|
StringToken uri;
|
||||||
StringToken str;
|
StringToken str;
|
||||||
std::vector<nix::AttrName> * attrNames;
|
std::vector<nix::AttrName> * attrNames;
|
||||||
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
|
std::vector<std::pair<nix::PosIdx, nix::Expr *> > * string_parts;
|
||||||
std::vector<std::pair<nix::Pos, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
|
std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
%type <e> start expr expr_function expr_if expr_op
|
%type <e> start expr expr_function expr_if expr_op
|
||||||
|
@ -369,15 +369,15 @@ expr_function
|
||||||
: ID ':' expr_function
|
: ID ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
|
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
|
||||||
| '{' formals '}' ':' expr_function
|
| '{' formals '}' ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), toFormals(*data, $2), $5); }
|
{ $$ = new ExprLambda(CUR_POS, toFormals(*data, $2), $5); }
|
||||||
| '{' formals '}' '@' ID ':' expr_function
|
| '{' formals '}' '@' ID ':' expr_function
|
||||||
{
|
{
|
||||||
Symbol arg = data->symbols.create($5);
|
auto arg = data->symbols.create($5);
|
||||||
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
|
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
|
||||||
}
|
}
|
||||||
| ID '@' '{' formals '}' ':' expr_function
|
| ID '@' '{' formals '}' ':' expr_function
|
||||||
{
|
{
|
||||||
Symbol arg = data->symbols.create($1);
|
auto arg = data->symbols.create($1);
|
||||||
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
|
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
|
||||||
}
|
}
|
||||||
| ASSERT expr ';' expr_function
|
| ASSERT expr ';' expr_function
|
||||||
|
@ -388,7 +388,7 @@ expr_function
|
||||||
{ if (!$2->dynamicAttrs.empty())
|
{ if (!$2->dynamicAttrs.empty())
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("dynamic attributes not allowed in let"),
|
.msg = hintfmt("dynamic attributes not allowed in let"),
|
||||||
.errPos = CUR_POS
|
.errPos = data->state.positions[CUR_POS]
|
||||||
});
|
});
|
||||||
$$ = new ExprLet($2, $4);
|
$$ = new ExprLet($2, $4);
|
||||||
}
|
}
|
||||||
|
@ -415,7 +415,7 @@ expr_op
|
||||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
|
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
|
||||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
||||||
| expr_op '+' expr_op
|
| expr_op '+' expr_op
|
||||||
{ $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
{ $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
||||||
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
||||||
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
||||||
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
||||||
|
@ -477,7 +477,7 @@ expr_simple
|
||||||
if (noURLLiterals)
|
if (noURLLiterals)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("URL literals are disabled"),
|
.msg = hintfmt("URL literals are disabled"),
|
||||||
.errPos = CUR_POS
|
.errPos = data->state.positions[CUR_POS]
|
||||||
});
|
});
|
||||||
$$ = new ExprString(std::string($1));
|
$$ = new ExprString(std::string($1));
|
||||||
}
|
}
|
||||||
|
@ -503,9 +503,9 @@ string_parts_interpolated
|
||||||
: string_parts_interpolated STR
|
: string_parts_interpolated STR
|
||||||
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); }
|
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); }
|
||||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
||||||
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
|
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
|
||||||
| STR DOLLAR_CURLY expr '}' {
|
| STR DOLLAR_CURLY expr '}' {
|
||||||
$$ = new std::vector<std::pair<Pos, Expr *> >;
|
$$ = new std::vector<std::pair<PosIdx, Expr *> >;
|
||||||
$$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1)));
|
$$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1)));
|
||||||
$$->emplace_back(makeCurPos(@2, data), $3);
|
$$->emplace_back(makeCurPos(@2, data), $3);
|
||||||
}
|
}
|
||||||
|
@ -528,17 +528,17 @@ path_start
|
||||||
ind_string_parts
|
ind_string_parts
|
||||||
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
|
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
|
||||||
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
||||||
| { $$ = new std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > >; }
|
| { $$ = new std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > >; }
|
||||||
;
|
;
|
||||||
|
|
||||||
binds
|
binds
|
||||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
|
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state); }
|
||||||
| binds INHERIT attrs ';'
|
| binds INHERIT attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
for (auto & i : *$3) {
|
for (auto & i : *$3) {
|
||||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
|
dupAttr(data->state, i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
|
||||||
Pos pos = makeCurPos(@3, data);
|
auto pos = makeCurPos(@3, data);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -547,7 +547,7 @@ binds
|
||||||
/* !!! Should ensure sharing of the expression in $4. */
|
/* !!! Should ensure sharing of the expression in $4. */
|
||||||
for (auto & i : *$6) {
|
for (auto & i : *$6) {
|
||||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
dupAttr(i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -565,7 +565,7 @@ attrs
|
||||||
} else
|
} else
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("dynamic attributes not allowed in inherit"),
|
.msg = hintfmt("dynamic attributes not allowed in inherit"),
|
||||||
.errPos = makeCurPos(@2, data)
|
.errPos = data->state.positions[makeCurPos(@2, data)]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
| { $$ = new AttrPath; }
|
| { $$ = new AttrPath; }
|
||||||
|
@ -621,8 +621,8 @@ formals
|
||||||
;
|
;
|
||||||
|
|
||||||
formal
|
formal
|
||||||
: ID { $$ = new Formal(CUR_POS, data->symbols.create($1), 0); }
|
: ID { $$ = new Formal{CUR_POS, data->symbols.create($1), 0}; }
|
||||||
| ID '?' expr { $$ = new Formal(CUR_POS, data->symbols.create($1), $3); }
|
| ID '?' expr { $$ = new Formal{CUR_POS, data->symbols.create($1), $3}; }
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@ -646,19 +646,19 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
||||||
const PathView path, const PathView basePath, StaticEnv & staticEnv)
|
const PathView path, const PathView basePath, StaticEnv & staticEnv)
|
||||||
{
|
{
|
||||||
yyscan_t scanner;
|
yyscan_t scanner;
|
||||||
ParseData data(*this);
|
std::string file;
|
||||||
data.origin = origin;
|
|
||||||
switch (origin) {
|
switch (origin) {
|
||||||
case foFile:
|
case foFile:
|
||||||
data.file = data.symbols.create(path);
|
file = path;
|
||||||
break;
|
break;
|
||||||
case foStdin:
|
case foStdin:
|
||||||
case foString:
|
case foString:
|
||||||
data.file = data.symbols.create(text);
|
file = text;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
ParseData data(*this, {file, origin});
|
||||||
data.basePath = basePath;
|
data.basePath = basePath;
|
||||||
|
|
||||||
yylex_init(&scanner);
|
yylex_init(&scanner);
|
||||||
|
@ -668,7 +668,7 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
||||||
|
|
||||||
if (res) throw ParseError(data.error.value());
|
if (res) throw ParseError(data.error.value());
|
||||||
|
|
||||||
data.result->bindVars(staticEnv);
|
data.result->bindVars(*this, staticEnv);
|
||||||
|
|
||||||
return data.result;
|
return data.result;
|
||||||
}
|
}
|
||||||
|
@ -760,7 +760,7 @@ Path EvalState::findFile(const std::string_view path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
|
Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos)
|
||||||
{
|
{
|
||||||
for (auto & i : searchPath) {
|
for (auto & i : searchPath) {
|
||||||
std::string suffix;
|
std::string suffix;
|
||||||
|
@ -787,7 +787,7 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c
|
||||||
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
||||||
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
||||||
path),
|
path),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -38,9 +38,9 @@ struct RegisterPrimOp
|
||||||
them. */
|
them. */
|
||||||
|
|
||||||
/* Load a ValueInitializer from a DSO and return whatever it initializes */
|
/* Load a ValueInitializer from a DSO and return whatever it initializes */
|
||||||
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
|
|
||||||
/* Execute a program and parse its output */
|
/* Execute a program and parse its output */
|
||||||
void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto s = state.coerceToString(pos, *args[0], context);
|
auto s = state.coerceToString(pos, *args[0], context);
|
||||||
|
@ -15,7 +15,7 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos,
|
||||||
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||||
|
|
||||||
|
|
||||||
static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
state.forceString(*args[0], context, pos);
|
state.forceString(*args[0], context, pos);
|
||||||
|
@ -31,7 +31,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
|
||||||
source-only deployment). This primop marks the string context so
|
source-only deployment). This primop marks the string context so
|
||||||
that builtins.derivation adds the path to drv.inputSrcs rather than
|
that builtins.derivation adds the path to drv.inputSrcs rather than
|
||||||
drv.inputDrvs. */
|
drv.inputDrvs. */
|
||||||
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto s = state.coerceToString(pos, *args[0], context);
|
auto s = state.coerceToString(pos, *args[0], context);
|
||||||
|
@ -65,7 +65,7 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutpu
|
||||||
Note that for a given path any combination of the above attributes
|
Note that for a given path any combination of the above attributes
|
||||||
may be present.
|
may be present.
|
||||||
*/
|
*/
|
||||||
static void prim_getContext(EvalState & state, const Pos & 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 path = false;
|
||||||
|
@ -134,7 +134,7 @@ static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
|
||||||
See the commentary above unsafeGetContext for details of the
|
See the commentary above unsafeGetContext for details of the
|
||||||
context representation.
|
context representation.
|
||||||
*/
|
*/
|
||||||
static void prim_appendContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto orig = state.forceString(*args[0], context, pos);
|
auto orig = state.forceString(*args[0], context, pos);
|
||||||
|
@ -144,45 +144,46 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||||
auto sPath = state.symbols.create("path");
|
auto sPath = state.symbols.create("path");
|
||||||
auto sAllOutputs = state.symbols.create("allOutputs");
|
auto sAllOutputs = state.symbols.create("allOutputs");
|
||||||
for (auto & i : *args[1]->attrs) {
|
for (auto & i : *args[1]->attrs) {
|
||||||
if (!state.store->isStorePath(i.name))
|
const auto & name = state.symbols[i.name];
|
||||||
|
if (!state.store->isStorePath(name))
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("Context key '%s' is not a store path", i.name),
|
.msg = hintfmt("Context key '%s' is not a store path", name),
|
||||||
.errPos = *i.pos
|
.errPos = state.positions[i.pos]
|
||||||
});
|
});
|
||||||
if (!settings.readOnlyMode)
|
if (!settings.readOnlyMode)
|
||||||
state.store->ensurePath(state.store->parseStorePath(i.name));
|
state.store->ensurePath(state.store->parseStorePath(name));
|
||||||
state.forceAttrs(*i.value, *i.pos);
|
state.forceAttrs(*i.value, i.pos);
|
||||||
auto iter = i.value->attrs->find(sPath);
|
auto iter = i.value->attrs->find(sPath);
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
if (state.forceBool(*iter->value, *iter->pos))
|
if (state.forceBool(*iter->value, iter->pos))
|
||||||
context.insert(i.name);
|
context.emplace(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = i.value->attrs->find(sAllOutputs);
|
iter = i.value->attrs->find(sAllOutputs);
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
if (state.forceBool(*iter->value, *iter->pos)) {
|
if (state.forceBool(*iter->value, iter->pos)) {
|
||||||
if (!isDerivation(i.name)) {
|
if (!isDerivation(name)) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
|
.msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", name),
|
||||||
.errPos = *i.pos
|
.errPos = state.positions[i.pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
context.insert("=" + std::string(i.name));
|
context.insert(concatStrings("=", name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = i.value->attrs->find(state.sOutputs);
|
iter = i.value->attrs->find(state.sOutputs);
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
state.forceList(*iter->value, *iter->pos);
|
state.forceList(*iter->value, iter->pos);
|
||||||
if (iter->value->listSize() && !isDerivation(i.name)) {
|
if (iter->value->listSize() && !isDerivation(name)) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
|
.msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", name),
|
||||||
.errPos = *i.pos
|
.errPos = state.positions[i.pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (auto elem : iter->value->listItems()) {
|
for (auto elem : iter->value->listItems()) {
|
||||||
auto name = state.forceStringNoCtx(*elem, *iter->pos);
|
auto outputName = state.forceStringNoCtx(*elem, iter->pos);
|
||||||
context.insert(concatStrings("!", name, "!", i.name));
|
context.insert(concatStrings("!", outputName, "!", name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos);
|
||||||
|
|
||||||
|
@ -15,40 +15,42 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
std::optional<StorePath> toPath;
|
std::optional<StorePath> toPath;
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
if (attr.name == "fromPath") {
|
const auto & attrName = state.symbols[attr.name];
|
||||||
|
|
||||||
|
if (attrName == "fromPath") {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
fromPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
|
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (attr.name == "toPath") {
|
else if (attrName == "toPath") {
|
||||||
state.forceValue(*attr.value, *attr.pos);
|
state.forceValue(*attr.value, attr.pos);
|
||||||
toCA = true;
|
toCA = true;
|
||||||
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
|
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
toPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
|
toPath = state.coerceToStorePath(attr.pos, *attr.value, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (attr.name == "fromStore")
|
else if (attrName == "fromStore")
|
||||||
fromStoreUrl = state.forceStringNoCtx(*attr.value, *attr.pos);
|
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos);
|
||||||
|
|
||||||
else
|
else
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attr.name),
|
.msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fromPath)
|
if (!fromPath)
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
|
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!fromStoreUrl)
|
if (!fromStoreUrl)
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
|
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
auto parsedURL = parseURL(*fromStoreUrl);
|
auto parsedURL = parseURL(*fromStoreUrl);
|
||||||
|
@ -58,13 +60,13 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
|
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"),
|
.msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!parsedURL.query.empty())
|
if (!parsedURL.query.empty())
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
|
.msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
auto fromStore = openStore(parsedURL.to_string());
|
auto fromStore = openStore(parsedURL.to_string());
|
||||||
|
@ -80,7 +82,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
state.store->printStorePath(*fromPath),
|
state.store->printStorePath(*fromPath),
|
||||||
state.store->printStorePath(i->second),
|
state.store->printStorePath(i->second),
|
||||||
state.store->printStorePath(*toPath)),
|
state.store->printStorePath(*toPath)),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
if (!toPath)
|
if (!toPath)
|
||||||
throw Error({
|
throw Error({
|
||||||
|
@ -89,7 +91,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
"please set this in the 'toPath' attribute passed to 'fetchClosure'",
|
"please set this in the 'toPath' attribute passed to 'fetchClosure'",
|
||||||
state.store->printStorePath(*fromPath),
|
state.store->printStorePath(*fromPath),
|
||||||
state.store->printStorePath(i->second)),
|
state.store->printStorePath(i->second)),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -105,7 +107,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't",
|
.msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't",
|
||||||
state.store->printStorePath(*toPath)),
|
state.store->printStorePath(*toPath)),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
std::string url;
|
std::string url;
|
||||||
std::optional<Hash> rev;
|
std::optional<Hash> rev;
|
||||||
|
@ -22,31 +22,31 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos);
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
std::string_view n(attr.name);
|
std::string_view n(state.symbols[attr.name]);
|
||||||
if (n == "url")
|
if (n == "url")
|
||||||
url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
|
url = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
|
||||||
else if (n == "rev") {
|
else if (n == "rev") {
|
||||||
// Ugly: unlike fetchGit, here the "rev" attribute can
|
// Ugly: unlike fetchGit, here the "rev" attribute can
|
||||||
// be both a revision or a branch/tag name.
|
// be both a revision or a branch/tag name.
|
||||||
auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
|
auto value = state.forceStringNoCtx(*attr.value, attr.pos);
|
||||||
if (std::regex_match(value.begin(), value.end(), revRegex))
|
if (std::regex_match(value.begin(), value.end(), revRegex))
|
||||||
rev = Hash::parseAny(value, htSHA1);
|
rev = Hash::parseAny(value, htSHA1);
|
||||||
else
|
else
|
||||||
ref = value;
|
ref = value;
|
||||||
}
|
}
|
||||||
else if (n == "name")
|
else if (n == "name")
|
||||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
name = state.forceStringNoCtx(*attr.value, attr.pos);
|
||||||
else
|
else
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
|
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]),
|
||||||
.errPos = *attr.pos
|
.errPos = state.positions[attr.pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.empty())
|
if (url.empty())
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("'url' argument required"),
|
.msg = hintfmt("'url' argument required"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -90,7 +90,7 @@ struct FetchTreeParams {
|
||||||
|
|
||||||
static void fetchTree(
|
static void fetchTree(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const Pos & pos,
|
const PosIdx pos,
|
||||||
Value * * args,
|
Value * * args,
|
||||||
Value & v,
|
Value & v,
|
||||||
std::optional<std::string> type,
|
std::optional<std::string> type,
|
||||||
|
@ -110,43 +110,43 @@ static void fetchTree(
|
||||||
if (type)
|
if (type)
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("unexpected attribute 'type'"),
|
.msg = hintfmt("unexpected attribute 'type'"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
type = state.forceStringNoCtx(*aType->value, *aType->pos);
|
type = state.forceStringNoCtx(*aType->value, aType->pos);
|
||||||
} else if (!type)
|
} else if (!type)
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
attrs.emplace("type", type.value());
|
attrs.emplace("type", type.value());
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
if (attr.name == state.sType) continue;
|
if (attr.name == state.sType) continue;
|
||||||
state.forceValue(*attr.value, *attr.pos);
|
state.forceValue(*attr.value, attr.pos);
|
||||||
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
||||||
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
|
auto s = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
|
||||||
attrs.emplace(attr.name,
|
attrs.emplace(state.symbols[attr.name],
|
||||||
attr.name == "url"
|
state.symbols[attr.name] == "url"
|
||||||
? type == "git"
|
? type == "git"
|
||||||
? fixURIForGit(s, state)
|
? fixURIForGit(s, state)
|
||||||
: fixURI(s, state)
|
: fixURI(s, state)
|
||||||
: s);
|
: s);
|
||||||
}
|
}
|
||||||
else if (attr.value->type() == nBool)
|
else if (attr.value->type() == nBool)
|
||||||
attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean});
|
attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean});
|
||||||
else if (attr.value->type() == nInt)
|
else if (attr.value->type() == nInt)
|
||||||
attrs.emplace(attr.name, uint64_t(attr.value->integer));
|
attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer));
|
||||||
else
|
else
|
||||||
throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
|
throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
|
||||||
attr.name, showType(*attr.value));
|
state.symbols[attr.name], showType(*attr.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.allowNameArgument)
|
if (!params.allowNameArgument)
|
||||||
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("attribute 'name' isn't supported in call to 'fetchTree'"),
|
.msg = hintfmt("attribute 'name' isn't supported in call to 'fetchTree'"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
input = fetchers::Input::fromAttrs(std::move(attrs));
|
input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||||
|
@ -167,7 +167,7 @@ static void fetchTree(
|
||||||
input = lookupInRegistries(state.store, input).first;
|
input = lookupInRegistries(state.store, input).first;
|
||||||
|
|
||||||
if (evalSettings.pureEval && !input.isLocked())
|
if (evalSettings.pureEval && !input.isLocked())
|
||||||
throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", pos);
|
throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", state.positions[pos]);
|
||||||
|
|
||||||
auto [tree, input2] = input.fetch(state.store);
|
auto [tree, input2] = input.fetch(state.store);
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ static void fetchTree(
|
||||||
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
|
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature(Xp::Flakes);
|
settings.requireExperimentalFeature(Xp::Flakes);
|
||||||
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
|
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
|
||||||
|
@ -185,7 +185,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
|
||||||
// FIXME: document
|
// FIXME: document
|
||||||
static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree);
|
static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree);
|
||||||
|
|
||||||
static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
|
||||||
const std::string & who, bool unpack, std::string name)
|
const std::string & who, bool unpack, std::string name)
|
||||||
{
|
{
|
||||||
std::optional<std::string> url;
|
std::optional<std::string> url;
|
||||||
|
@ -198,24 +198,24 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos);
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
std::string n(attr.name);
|
std::string_view n(state.symbols[attr.name]);
|
||||||
if (n == "url")
|
if (n == "url")
|
||||||
url = state.forceStringNoCtx(*attr.value, *attr.pos);
|
url = state.forceStringNoCtx(*attr.value, attr.pos);
|
||||||
else if (n == "sha256")
|
else if (n == "sha256")
|
||||||
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256);
|
||||||
else if (n == "name")
|
else if (n == "name")
|
||||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
name = state.forceStringNoCtx(*attr.value, attr.pos);
|
||||||
else
|
else
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
|
.msg = hintfmt("unsupported argument '%s' to '%s'", n, who),
|
||||||
.errPos = *attr.pos
|
.errPos = state.positions[attr.pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!url)
|
if (!url)
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("'url' argument required"),
|
.msg = hintfmt("'url' argument required"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
} else
|
} else
|
||||||
url = state.forceStringNoCtx(*args[0], pos);
|
url = state.forceStringNoCtx(*args[0], pos);
|
||||||
|
@ -262,7 +262,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
state.allowAndSetStorePathString(storePath, v);
|
state.allowAndSetStorePathString(storePath, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_fetchurl(EvalState & state, const Pos & 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, "");
|
fetch(state, pos, args, v, "fetchurl", false, "");
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@ static RegisterPrimOp primop_fetchurl({
|
||||||
.fun = prim_fetchurl,
|
.fun = prim_fetchurl,
|
||||||
});
|
});
|
||||||
|
|
||||||
static void prim_fetchTarball(EvalState & state, const Pos & 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");
|
fetch(state, pos, args, v, "fetchTarball", true, "source");
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ static RegisterPrimOp primop_fetchTarball({
|
||||||
.fun = prim_fetchTarball,
|
.fun = prim_fetchTarball,
|
||||||
});
|
});
|
||||||
|
|
||||||
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
|
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void prim_fromTOML(EvalState & state, const Pos & 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);
|
auto toml = state.forceStringNoCtx(*args[0], pos);
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
} catch (std::exception & e) { // TODO: toml::syntax_error
|
} catch (std::exception & e) { // TODO: toml::syntax_error
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
|
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
#include "chunked-vector.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -16,90 +17,90 @@ namespace nix {
|
||||||
|
|
||||||
class Symbol
|
class Symbol
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
const std::string * s; // pointer into SymbolTable
|
|
||||||
Symbol(const std::string * s) : s(s) { };
|
|
||||||
friend class SymbolTable;
|
friend class SymbolTable;
|
||||||
|
private:
|
||||||
|
std::string s;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Symbol() : s(0) { };
|
Symbol(std::string_view s) : s(s) { }
|
||||||
|
|
||||||
bool operator == (const Symbol & s2) const
|
|
||||||
{
|
|
||||||
return s == s2.s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: remove
|
// FIXME: remove
|
||||||
bool operator == (std::string_view s2) const
|
bool operator == (std::string_view s2) const
|
||||||
{
|
{
|
||||||
return s->compare(s2) == 0;
|
return s == s2;
|
||||||
}
|
|
||||||
|
|
||||||
bool operator != (const Symbol & s2) const
|
|
||||||
{
|
|
||||||
return s != s2.s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator < (const Symbol & s2) const
|
|
||||||
{
|
|
||||||
return s < s2.s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operator const std::string & () const
|
operator const std::string & () const
|
||||||
{
|
{
|
||||||
return *s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator const std::string_view () const
|
operator const std::string_view () const
|
||||||
{
|
|
||||||
return *s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool set() const
|
|
||||||
{
|
{
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return s->empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend std::ostream & operator << (std::ostream & str, const Symbol & sym);
|
friend std::ostream & operator << (std::ostream & str, const Symbol & sym);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SymbolIdx
|
||||||
|
{
|
||||||
|
friend class SymbolTable;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
|
explicit SymbolIdx(uint32_t id): id(id) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
SymbolIdx() : id(0) {}
|
||||||
|
|
||||||
|
explicit operator bool() const { return id > 0; }
|
||||||
|
|
||||||
|
bool operator<(const SymbolIdx other) const { return id < other.id; }
|
||||||
|
bool operator==(const SymbolIdx other) const { return id == other.id; }
|
||||||
|
bool operator!=(const SymbolIdx other) const { return id != other.id; }
|
||||||
|
};
|
||||||
|
|
||||||
class SymbolTable
|
class SymbolTable
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string_view, Symbol> symbols;
|
std::unordered_map<std::string_view, std::pair<const Symbol *, uint32_t>> symbols;
|
||||||
std::list<std::string> store;
|
ChunkedVector<Symbol, 8192> store{16};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Symbol create(std::string_view s)
|
SymbolIdx create(std::string_view s)
|
||||||
{
|
{
|
||||||
// Most symbols are looked up more than once, so we trade off insertion performance
|
// Most symbols are looked up more than once, so we trade off insertion performance
|
||||||
// for lookup performance.
|
// for lookup performance.
|
||||||
// TODO: could probably be done more efficiently with transparent Hash and Equals
|
// TODO: could probably be done more efficiently with transparent Hash and Equals
|
||||||
// on the original implementation using unordered_set
|
// on the original implementation using unordered_set
|
||||||
auto it = symbols.find(s);
|
auto it = symbols.find(s);
|
||||||
if (it != symbols.end()) return it->second;
|
if (it != symbols.end()) return SymbolIdx(it->second.second + 1);
|
||||||
|
|
||||||
auto & rawSym = store.emplace_back(s);
|
const auto & [rawSym, idx] = store.add(s);
|
||||||
return symbols.emplace(rawSym, Symbol(&rawSym)).first->second;
|
symbols.emplace(rawSym, std::make_pair(&rawSym, idx));
|
||||||
|
return SymbolIdx(idx + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Symbol & operator[](SymbolIdx s) const
|
||||||
|
{
|
||||||
|
if (s.id == 0 || s.id > store.size())
|
||||||
|
abort();
|
||||||
|
return store[s.id - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size() const
|
size_t size() const
|
||||||
{
|
{
|
||||||
return symbols.size();
|
return store.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t totalSize() const;
|
size_t totalSize() const;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void dump(T callback)
|
void dump(T callback) const
|
||||||
{
|
{
|
||||||
for (auto & s : store)
|
store.forEach(callback);
|
||||||
callback(s);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
void printValueAsJSON(EvalState & state, bool strict,
|
void printValueAsJSON(EvalState & state, bool strict,
|
||||||
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context)
|
Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
|
@ -50,14 +50,14 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||||
auto obj(out.object());
|
auto obj(out.object());
|
||||||
StringSet names;
|
StringSet names;
|
||||||
for (auto & j : *v.attrs)
|
for (auto & j : *v.attrs)
|
||||||
names.insert(j.name);
|
names.emplace(state.symbols[j.name]);
|
||||||
for (auto & j : names) {
|
for (auto & j : names) {
|
||||||
Attr & a(*v.attrs->find(state.symbols.create(j)));
|
Attr & a(*v.attrs->find(state.symbols.create(j)));
|
||||||
auto placeholder(obj.placeholder(j));
|
auto placeholder(obj.placeholder(j));
|
||||||
printValueAsJSON(state, strict, *a.value, *a.pos, placeholder, context);
|
printValueAsJSON(state, strict, *a.value, a.pos, placeholder, context);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
printValueAsJSON(state, strict, *i->value, *i->pos, out, context);
|
printValueAsJSON(state, strict, *i->value, i->pos, out, context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,15 +82,15 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||||
case nFunction:
|
case nFunction:
|
||||||
auto e = TypeError({
|
auto e = TypeError({
|
||||||
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
|
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
|
||||||
.errPos = v.determinePos(pos)
|
.errPos = state.positions[v.determinePos(pos)]
|
||||||
});
|
});
|
||||||
e.addTrace(pos, hintfmt("message for the trace"));
|
e.addTrace(state.positions[pos], hintfmt("message for the trace"));
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void printValueAsJSON(EvalState & state, bool strict,
|
void printValueAsJSON(EvalState & state, bool strict,
|
||||||
Value & v, const Pos & pos, std::ostream & str, PathSet & context)
|
Value & v, const PosIdx pos, std::ostream & str, PathSet & context)
|
||||||
{
|
{
|
||||||
JSONPlaceholder out(str);
|
JSONPlaceholder out(str);
|
||||||
printValueAsJSON(state, strict, v, pos, out, context);
|
printValueAsJSON(state, strict, v, pos, out, context);
|
||||||
|
|
|
@ -11,9 +11,9 @@ namespace nix {
|
||||||
class JSONPlaceholder;
|
class JSONPlaceholder;
|
||||||
|
|
||||||
void printValueAsJSON(EvalState & state, bool strict,
|
void printValueAsJSON(EvalState & state, bool strict,
|
||||||
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context);
|
Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context);
|
||||||
|
|
||||||
void printValueAsJSON(EvalState & state, bool strict,
|
void printValueAsJSON(EvalState & state, bool strict,
|
||||||
Value & v, const Pos & pos, std::ostream & str, PathSet & context);
|
Value & v, const PosIdx pos, std::ostream & str, PathSet & context);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,10 @@ static XMLAttrs singletonAttrs(const std::string & name, const std::string & val
|
||||||
|
|
||||||
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||||
const Pos & pos);
|
const PosIdx pos);
|
||||||
|
|
||||||
|
|
||||||
static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos)
|
static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
|
||||||
{
|
{
|
||||||
xmlAttrs["path"] = pos.file;
|
xmlAttrs["path"] = pos.file;
|
||||||
xmlAttrs["line"] = (format("%1%") % pos.line).str();
|
xmlAttrs["line"] = (format("%1%") % pos.line).str();
|
||||||
|
@ -36,25 +36,25 @@ static void showAttrs(EvalState & state, bool strict, bool location,
|
||||||
StringSet names;
|
StringSet names;
|
||||||
|
|
||||||
for (auto & i : attrs)
|
for (auto & i : attrs)
|
||||||
names.insert(i.name);
|
names.emplace(state.symbols[i.name]);
|
||||||
|
|
||||||
for (auto & i : names) {
|
for (auto & i : names) {
|
||||||
Attr & a(*attrs.find(state.symbols.create(i)));
|
Attr & a(*attrs.find(state.symbols.create(i)));
|
||||||
|
|
||||||
XMLAttrs xmlAttrs;
|
XMLAttrs xmlAttrs;
|
||||||
xmlAttrs["name"] = i;
|
xmlAttrs["name"] = i;
|
||||||
if (location && a.pos != ptr(&noPos)) posToXML(xmlAttrs, *a.pos);
|
if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]);
|
||||||
|
|
||||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||||
printValueAsXML(state, strict, location,
|
printValueAsXML(state, strict, location,
|
||||||
*a.value, doc, context, drvsSeen, *a.pos);
|
*a.value, doc, context, drvsSeen, a.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||||
const Pos & pos)
|
const PosIdx pos)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
|
@ -93,14 +93,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Path drvPath;
|
Path drvPath;
|
||||||
a = v.attrs->find(state.sDrvPath);
|
a = v.attrs->find(state.sDrvPath);
|
||||||
if (a != v.attrs->end()) {
|
if (a != v.attrs->end()) {
|
||||||
if (strict) state.forceValue(*a->value, *a->pos);
|
if (strict) state.forceValue(*a->value, a->pos);
|
||||||
if (a->value->type() == nString)
|
if (a->value->type() == nString)
|
||||||
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
|
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
a = v.attrs->find(state.sOutPath);
|
a = v.attrs->find(state.sOutPath);
|
||||||
if (a != v.attrs->end()) {
|
if (a != v.attrs->end()) {
|
||||||
if (strict) state.forceValue(*a->value, *a->pos);
|
if (strict) state.forceValue(*a->value, a->pos);
|
||||||
if (a->value->type() == nString)
|
if (a->value->type() == nString)
|
||||||
xmlAttrs["outPath"] = a->value->string.s;
|
xmlAttrs["outPath"] = a->value->string.s;
|
||||||
}
|
}
|
||||||
|
@ -134,18 +134,18 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
XMLAttrs xmlAttrs;
|
XMLAttrs xmlAttrs;
|
||||||
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
|
if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]);
|
||||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||||
|
|
||||||
if (v.lambda.fun->hasFormals()) {
|
if (v.lambda.fun->hasFormals()) {
|
||||||
XMLAttrs attrs;
|
XMLAttrs attrs;
|
||||||
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg];
|
||||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||||
XMLOpenElement _(doc, "attrspat", attrs);
|
XMLOpenElement _(doc, "attrspat", attrs);
|
||||||
for (auto & i : v.lambda.fun->formals->lexicographicOrder())
|
for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols))
|
||||||
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
|
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
||||||
} else
|
} else
|
||||||
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
|
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg]));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -166,14 +166,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
|
|
||||||
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
|
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
|
||||||
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||||
const Pos & pos) const
|
const PosIdx pos) const
|
||||||
{
|
{
|
||||||
doc.writeEmptyElement("unevaluated");
|
doc.writeEmptyElement("unevaluated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void printValueAsXML(EvalState & state, bool strict, bool location,
|
void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Value & v, std::ostream & out, PathSet & context, const Pos & pos)
|
Value & v, std::ostream & out, PathSet & context, const PosIdx pos)
|
||||||
{
|
{
|
||||||
XMLWriter doc(true, out);
|
XMLWriter doc(true, out);
|
||||||
XMLOpenElement root(doc, "expr");
|
XMLOpenElement root(doc, "expr");
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
void printValueAsXML(EvalState & state, bool strict, bool location,
|
void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Value & v, std::ostream & out, PathSet & context, const Pos & pos);
|
Value & v, std::ostream & out, PathSet & context, const PosIdx pos);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,8 @@ struct Env;
|
||||||
struct Expr;
|
struct Expr;
|
||||||
struct ExprLambda;
|
struct ExprLambda;
|
||||||
struct PrimOp;
|
struct PrimOp;
|
||||||
class Symbol;
|
class SymbolIdx;
|
||||||
|
class PosIdx;
|
||||||
struct Pos;
|
struct Pos;
|
||||||
class StorePath;
|
class StorePath;
|
||||||
class Store;
|
class Store;
|
||||||
|
@ -103,7 +104,7 @@ class ExternalValueBase
|
||||||
/* Print the value as XML. Defaults to unevaluated */
|
/* Print the value as XML. Defaults to unevaluated */
|
||||||
virtual void printValueAsXML(EvalState & state, bool strict, bool location,
|
virtual void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||||
const Pos & pos) const;
|
const PosIdx pos) const;
|
||||||
|
|
||||||
virtual ~ExternalValueBase()
|
virtual ~ExternalValueBase()
|
||||||
{
|
{
|
||||||
|
@ -120,11 +121,11 @@ private:
|
||||||
|
|
||||||
friend std::string showType(const Value & v);
|
friend std::string showType(const Value & v);
|
||||||
|
|
||||||
void print(std::ostream & str, std::set<const void *> * seen) const;
|
void print(const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void print(std::ostream & str, bool showRepeated = false) const;
|
void print(const SymbolTable & symbols, std::ostream & str, bool showRepeated = false) const;
|
||||||
|
|
||||||
// Functions needed to distinguish the type
|
// Functions needed to distinguish the type
|
||||||
// These should be removed eventually, by putting the functionality that's
|
// These should be removed eventually, by putting the functionality that's
|
||||||
|
@ -252,7 +253,7 @@ public:
|
||||||
|
|
||||||
inline void mkString(const Symbol & s)
|
inline void mkString(const Symbol & s)
|
||||||
{
|
{
|
||||||
mkString(((const std::string &) s).c_str());
|
mkString(std::string_view(s).data());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void mkPath(const char * s)
|
inline void mkPath(const char * s)
|
||||||
|
@ -368,7 +369,7 @@ public:
|
||||||
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
|
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pos determinePos(const Pos & pos) const;
|
PosIdx determinePos(const PosIdx pos) const;
|
||||||
|
|
||||||
/* Check whether forcing this value requires a trivial amount of
|
/* Check whether forcing this value requires a trivial amount of
|
||||||
computation. In particular, function applications are
|
computation. In particular, function applications are
|
||||||
|
@ -409,12 +410,12 @@ public:
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
|
typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
|
||||||
typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *> > > ValueMap;
|
typedef std::map<SymbolIdx, Value *, std::less<SymbolIdx>, traceable_allocator<std::pair<const SymbolIdx, Value *> > > ValueMap;
|
||||||
typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector> > > ValueVectorMap;
|
typedef std::map<SymbolIdx, ValueVector, std::less<SymbolIdx>, traceable_allocator<std::pair<const SymbolIdx, ValueVector> > > ValueVectorMap;
|
||||||
#else
|
#else
|
||||||
typedef std::vector<Value *> ValueVector;
|
typedef std::vector<Value *> ValueVector;
|
||||||
typedef std::map<Symbol, Value *> ValueMap;
|
typedef std::map<SymbolIdx, Value *> ValueMap;
|
||||||
typedef std::map<Symbol, ValueVector> ValueVectorMap;
|
typedef std::map<SymbolIdx, ValueVector> ValueVectorMap;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ static std::string runHg(const Strings & args, const std::optional<std::string>
|
||||||
auto res = runProgram(std::move(opts));
|
auto res = runProgram(std::move(opts));
|
||||||
|
|
||||||
if (!statusOk(res.first))
|
if (!statusOk(res.first))
|
||||||
throw ExecError(res.first, fmt("hg %1%", statusToString(res.first)));
|
throw ExecError(res.first, "hg %1%", statusToString(res.first));
|
||||||
|
|
||||||
return res.second;
|
return res.second;
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
runHg({ "recover", "-R", cacheDir });
|
runHg({ "recover", "-R", cacheDir });
|
||||||
runHg({ "pull", "-R", cacheDir, "--", actualUrl });
|
runHg({ "pull", "-R", cacheDir, "--", actualUrl });
|
||||||
} else {
|
} else {
|
||||||
throw ExecError(e.status, fmt("'hg pull' %s", statusToString(e.status)));
|
throw ExecError(e.status, "'hg pull' %s", statusToString(e.status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -60,37 +60,37 @@ void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
||||||
{
|
{
|
||||||
if (!willBuild.empty()) {
|
if (!willBuild.empty()) {
|
||||||
if (willBuild.size() == 1)
|
if (willBuild.size() == 1)
|
||||||
printMsg(lvl, fmt("this derivation will be built:"));
|
printMsg(lvl, "this derivation will be built:");
|
||||||
else
|
else
|
||||||
printMsg(lvl, fmt("these %d derivations will be built:", willBuild.size()));
|
printMsg(lvl, "these %d derivations will be built:", willBuild.size());
|
||||||
auto sorted = store->topoSortPaths(willBuild);
|
auto sorted = store->topoSortPaths(willBuild);
|
||||||
reverse(sorted.begin(), sorted.end());
|
reverse(sorted.begin(), sorted.end());
|
||||||
for (auto & i : sorted)
|
for (auto & i : sorted)
|
||||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
printMsg(lvl, " %s", store->printStorePath(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!willSubstitute.empty()) {
|
if (!willSubstitute.empty()) {
|
||||||
const float downloadSizeMiB = downloadSize / (1024.f * 1024.f);
|
const float downloadSizeMiB = downloadSize / (1024.f * 1024.f);
|
||||||
const float narSizeMiB = narSize / (1024.f * 1024.f);
|
const float narSizeMiB = narSize / (1024.f * 1024.f);
|
||||||
if (willSubstitute.size() == 1) {
|
if (willSubstitute.size() == 1) {
|
||||||
printMsg(lvl, fmt("this path will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
printMsg(lvl, "this path will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||||
downloadSizeMiB,
|
downloadSizeMiB,
|
||||||
narSizeMiB));
|
narSizeMiB);
|
||||||
} else {
|
} else {
|
||||||
printMsg(lvl, fmt("these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
printMsg(lvl, "these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||||
willSubstitute.size(),
|
willSubstitute.size(),
|
||||||
downloadSizeMiB,
|
downloadSizeMiB,
|
||||||
narSizeMiB));
|
narSizeMiB);
|
||||||
}
|
}
|
||||||
for (auto & i : willSubstitute)
|
for (auto & i : willSubstitute)
|
||||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
printMsg(lvl, " %s", store->printStorePath(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unknown.empty()) {
|
if (!unknown.empty()) {
|
||||||
printMsg(lvl, fmt("don't know how to build these paths%s:",
|
printMsg(lvl, "don't know how to build these paths%s:",
|
||||||
(settings.readOnlyMode ? " (may be caused by read-only store access)" : "")));
|
(settings.readOnlyMode ? " (may be caused by read-only store access)" : ""));
|
||||||
for (auto & i : unknown)
|
for (auto & i : unknown)
|
||||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
printMsg(lvl, " %s", store->printStorePath(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,27 @@ create table if not exists Realisations (
|
||||||
|
|
||||||
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
||||||
|
|
||||||
|
-- We can end-up in a weird edge-case where a path depends on itself because
|
||||||
|
-- it’s an output of a CA derivation, that happens to be the same as one of its
|
||||||
|
-- dependencies.
|
||||||
|
-- In that case we have a dependency loop (path -> realisation1 -> realisation2
|
||||||
|
-- -> path) that we need to break by removing the dependencies between the
|
||||||
|
-- realisations
|
||||||
|
create trigger if not exists DeleteSelfRefsViaRealisations before delete on ValidPaths
|
||||||
|
begin
|
||||||
|
delete from RealisationsRefs where realisationReference in (
|
||||||
|
select id from Realisations where outputPath = old.id
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
|
||||||
create table if not exists RealisationsRefs (
|
create table if not exists RealisationsRefs (
|
||||||
referrer integer not null,
|
referrer integer not null,
|
||||||
realisationReference integer,
|
realisationReference integer,
|
||||||
foreign key (referrer) references Realisations(id) on delete cascade,
|
foreign key (referrer) references Realisations(id) on delete cascade,
|
||||||
foreign key (realisationReference) references Realisations(id) on delete restrict
|
foreign key (realisationReference) references Realisations(id) on delete restrict
|
||||||
);
|
);
|
||||||
|
-- used by deletion trigger
|
||||||
|
create index if not exists IndexRealisationsRefsRealisationReference on RealisationsRefs(realisationReference);
|
||||||
|
|
||||||
-- used by QueryRealisationReferences
|
-- used by QueryRealisationReferences
|
||||||
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
|
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
|
||||||
|
|
|
@ -443,14 +443,13 @@ struct curlFileTransfer : public FileTransfer
|
||||||
: httpStatus != 0
|
: httpStatus != 0
|
||||||
? FileTransferError(err,
|
? FileTransferError(err,
|
||||||
std::move(response),
|
std::move(response),
|
||||||
fmt("unable to %s '%s': HTTP error %d ('%s')",
|
"unable to %s '%s': HTTP error %d%s",
|
||||||
request.verb(), request.uri, httpStatus, statusMsg)
|
request.verb(), request.uri, httpStatus,
|
||||||
+ (code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
|
code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
|
||||||
)
|
|
||||||
: FileTransferError(err,
|
: FileTransferError(err,
|
||||||
std::move(response),
|
std::move(response),
|
||||||
fmt("unable to %s '%s': %s (%d)",
|
"unable to %s '%s': %s (%d)",
|
||||||
request.verb(), request.uri, curl_easy_strerror(code), code));
|
request.verb(), request.uri, curl_easy_strerror(code), code);
|
||||||
|
|
||||||
/* If this is a transient error, then maybe retry the
|
/* If this is a transient error, then maybe retry the
|
||||||
download after a while. If we're writing to a
|
download after a while. If we're writing to a
|
||||||
|
@ -704,7 +703,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
auto s3Res = s3Helper.getObject(bucketName, key);
|
auto s3Res = s3Helper.getObject(bucketName, key);
|
||||||
FileTransferResult res;
|
FileTransferResult res;
|
||||||
if (!s3Res.data)
|
if (!s3Res.data)
|
||||||
throw FileTransferError(NotFound, {}, "S3 object '%s' does not exist", request.uri);
|
throw FileTransferError(NotFound, "S3 object '%s' does not exist", request.uri);
|
||||||
res.data = std::move(*s3Res.data);
|
res.data = std::move(*s3Res.data);
|
||||||
callback(std::move(res));
|
callback(std::move(res));
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -81,7 +81,7 @@ int getSchema(Path schemaPath)
|
||||||
|
|
||||||
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||||
{
|
{
|
||||||
const int nixCASchemaVersion = 3;
|
const int nixCASchemaVersion = 4;
|
||||||
int curCASchema = getSchema(schemaPath);
|
int curCASchema = getSchema(schemaPath);
|
||||||
if (curCASchema != nixCASchemaVersion) {
|
if (curCASchema != nixCASchemaVersion) {
|
||||||
if (curCASchema > nixCASchemaVersion) {
|
if (curCASchema > nixCASchemaVersion) {
|
||||||
|
@ -143,6 +143,21 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||||
)");
|
)");
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
if (curCASchema < 4) {
|
||||||
|
SQLiteTxn txn(db);
|
||||||
|
db.exec(R"(
|
||||||
|
create trigger if not exists DeleteSelfRefsViaRealisations before delete on ValidPaths
|
||||||
|
begin
|
||||||
|
delete from RealisationsRefs where realisationReference in (
|
||||||
|
select id from Realisations where outputPath = old.id
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
-- used by deletion trigger
|
||||||
|
create index if not exists IndexRealisationsRefsRealisationReference on RealisationsRefs(realisationReference);
|
||||||
|
)");
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
||||||
lockFile(lockFd.get(), ltRead, true);
|
lockFile(lockFd.get(), ltRead, true);
|
||||||
}
|
}
|
||||||
|
@ -482,18 +497,18 @@ void LocalStore::openDB(State & state, bool create)
|
||||||
SQLiteStmt stmt;
|
SQLiteStmt stmt;
|
||||||
stmt.create(db, "pragma main.journal_mode;");
|
stmt.create(db, "pragma main.journal_mode;");
|
||||||
if (sqlite3_step(stmt) != SQLITE_ROW)
|
if (sqlite3_step(stmt) != SQLITE_ROW)
|
||||||
throwSQLiteError(db, "querying journal mode");
|
SQLiteError::throw_(db, "querying journal mode");
|
||||||
prevMode = std::string((const char *) sqlite3_column_text(stmt, 0));
|
prevMode = std::string((const char *) sqlite3_column_text(stmt, 0));
|
||||||
}
|
}
|
||||||
if (prevMode != mode &&
|
if (prevMode != mode &&
|
||||||
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "setting journal mode");
|
SQLiteError::throw_(db, "setting journal mode");
|
||||||
|
|
||||||
/* Increase the auto-checkpoint interval to 40000 pages. This
|
/* Increase the auto-checkpoint interval to 40000 pages. This
|
||||||
seems enough to ensure that instantiating the NixOS system
|
seems enough to ensure that instantiating the NixOS system
|
||||||
derivation is done in a single fsync(). */
|
derivation is done in a single fsync(). */
|
||||||
if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;", 0, 0, 0) != SQLITE_OK)
|
if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "setting autocheckpoint interval");
|
SQLiteError::throw_(db, "setting autocheckpoint interval");
|
||||||
|
|
||||||
/* Initialise the database schema, if necessary. */
|
/* Initialise the database schema, if necessary. */
|
||||||
if (create) {
|
if (create) {
|
||||||
|
|
|
@ -8,22 +8,32 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
[[noreturn]] void throwSQLiteError(sqlite3 * db, const FormatOrString & fs)
|
SQLiteError::SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf)
|
||||||
|
: Error(""), path(path), errNo(errNo), extendedErrNo(extendedErrNo)
|
||||||
|
{
|
||||||
|
err.msg = hintfmt("%s: %s (in '%s')",
|
||||||
|
normaltxt(hf.str()),
|
||||||
|
sqlite3_errstr(extendedErrNo),
|
||||||
|
path ? path : "(in-memory)");
|
||||||
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void SQLiteError::throw_(sqlite3 * db, hintformat && hf)
|
||||||
{
|
{
|
||||||
int err = sqlite3_errcode(db);
|
int err = sqlite3_errcode(db);
|
||||||
int exterr = sqlite3_extended_errcode(db);
|
int exterr = sqlite3_extended_errcode(db);
|
||||||
|
|
||||||
auto path = sqlite3_db_filename(db, nullptr);
|
auto path = sqlite3_db_filename(db, nullptr);
|
||||||
if (!path) path = "(in-memory)";
|
|
||||||
|
|
||||||
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
|
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
|
||||||
throw SQLiteBusy(
|
auto exp = SQLiteBusy(path, err, exterr, std::move(hf));
|
||||||
|
exp.err.msg = hintfmt(
|
||||||
err == SQLITE_PROTOCOL
|
err == SQLITE_PROTOCOL
|
||||||
? fmt("SQLite database '%s' is busy (SQLITE_PROTOCOL)", path)
|
? "SQLite database '%s' is busy (SQLITE_PROTOCOL)"
|
||||||
: fmt("SQLite database '%s' is busy", path));
|
: "SQLite database '%s' is busy",
|
||||||
}
|
path ? path : "(in-memory)");
|
||||||
else
|
throw exp;
|
||||||
throw SQLiteError("%s: %s (in '%s')", fs.s, sqlite3_errstr(exterr), path);
|
} else
|
||||||
|
throw SQLiteError(path, err, exterr, std::move(hf));
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLite::SQLite(const Path & path, bool create)
|
SQLite::SQLite(const Path & path, bool create)
|
||||||
|
@ -37,7 +47,7 @@ SQLite::SQLite(const Path & path, bool create)
|
||||||
throw Error("cannot open SQLite database '%s'", path);
|
throw Error("cannot open SQLite database '%s'", path);
|
||||||
|
|
||||||
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "setting timeout");
|
SQLiteError::throw_(db, "setting timeout");
|
||||||
|
|
||||||
exec("pragma foreign_keys = 1");
|
exec("pragma foreign_keys = 1");
|
||||||
}
|
}
|
||||||
|
@ -46,7 +56,7 @@ SQLite::~SQLite()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (db && sqlite3_close(db) != SQLITE_OK)
|
if (db && sqlite3_close(db) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "closing database");
|
SQLiteError::throw_(db, "closing database");
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -62,7 +72,7 @@ void SQLite::exec(const std::string & stmt)
|
||||||
{
|
{
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
if (sqlite3_exec(db, stmt.c_str(), 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, stmt.c_str(), 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, format("executing SQLite statement '%s'") % stmt);
|
SQLiteError::throw_(db, "executing SQLite statement '%s'", stmt);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +86,7 @@ void SQLiteStmt::create(sqlite3 * db, const std::string & sql)
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
assert(!stmt);
|
assert(!stmt);
|
||||||
if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0) != SQLITE_OK)
|
if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, fmt("creating statement '%s'", sql));
|
SQLiteError::throw_(db, "creating statement '%s'", sql);
|
||||||
this->db = db;
|
this->db = db;
|
||||||
this->sql = sql;
|
this->sql = sql;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +95,7 @@ SQLiteStmt::~SQLiteStmt()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
|
if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
|
||||||
throwSQLiteError(db, fmt("finalizing statement '%s'", sql));
|
SQLiteError::throw_(db, "finalizing statement '%s'", sql);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -109,7 +119,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (std::string_view value, bool not
|
||||||
{
|
{
|
||||||
if (notNull) {
|
if (notNull) {
|
||||||
if (sqlite3_bind_text(stmt, curArg++, value.data(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
if (sqlite3_bind_text(stmt, curArg++, value.data(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
throwSQLiteError(stmt.db, "binding argument");
|
SQLiteError::throw_(stmt.db, "binding argument");
|
||||||
} else
|
} else
|
||||||
bind();
|
bind();
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -119,7 +129,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (const unsigned char * data, size
|
||||||
{
|
{
|
||||||
if (notNull) {
|
if (notNull) {
|
||||||
if (sqlite3_bind_blob(stmt, curArg++, data, len, SQLITE_TRANSIENT) != SQLITE_OK)
|
if (sqlite3_bind_blob(stmt, curArg++, data, len, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
throwSQLiteError(stmt.db, "binding argument");
|
SQLiteError::throw_(stmt.db, "binding argument");
|
||||||
} else
|
} else
|
||||||
bind();
|
bind();
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -129,7 +139,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
|
||||||
{
|
{
|
||||||
if (notNull) {
|
if (notNull) {
|
||||||
if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
|
if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
|
||||||
throwSQLiteError(stmt.db, "binding argument");
|
SQLiteError::throw_(stmt.db, "binding argument");
|
||||||
} else
|
} else
|
||||||
bind();
|
bind();
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -138,7 +148,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
|
||||||
SQLiteStmt::Use & SQLiteStmt::Use::bind()
|
SQLiteStmt::Use & SQLiteStmt::Use::bind()
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
|
if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
|
||||||
throwSQLiteError(stmt.db, "binding argument");
|
SQLiteError::throw_(stmt.db, "binding argument");
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,14 +162,14 @@ void SQLiteStmt::Use::exec()
|
||||||
int r = step();
|
int r = step();
|
||||||
assert(r != SQLITE_ROW);
|
assert(r != SQLITE_ROW);
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
throwSQLiteError(stmt.db, fmt("executing SQLite statement '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
SQLiteError::throw_(stmt.db, fmt("executing SQLite statement '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SQLiteStmt::Use::next()
|
bool SQLiteStmt::Use::next()
|
||||||
{
|
{
|
||||||
int r = step();
|
int r = step();
|
||||||
if (r != SQLITE_DONE && r != SQLITE_ROW)
|
if (r != SQLITE_DONE && r != SQLITE_ROW)
|
||||||
throwSQLiteError(stmt.db, fmt("executing SQLite query '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
SQLiteError::throw_(stmt.db, fmt("executing SQLite query '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
||||||
return r == SQLITE_ROW;
|
return r == SQLITE_ROW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,14 +195,14 @@ SQLiteTxn::SQLiteTxn(sqlite3 * db)
|
||||||
{
|
{
|
||||||
this->db = db;
|
this->db = db;
|
||||||
if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "starting transaction");
|
SQLiteError::throw_(db, "starting transaction");
|
||||||
active = true;
|
active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SQLiteTxn::commit()
|
void SQLiteTxn::commit()
|
||||||
{
|
{
|
||||||
if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "committing transaction");
|
SQLiteError::throw_(db, "committing transaction");
|
||||||
active = false;
|
active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +210,7 @@ SQLiteTxn::~SQLiteTxn()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
|
if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "aborting transaction");
|
SQLiteError::throw_(db, "aborting transaction");
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,10 +96,30 @@ struct SQLiteTxn
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
MakeError(SQLiteError, Error);
|
struct SQLiteError : Error
|
||||||
MakeError(SQLiteBusy, SQLiteError);
|
{
|
||||||
|
const char *path;
|
||||||
|
int errNo, extendedErrNo;
|
||||||
|
|
||||||
[[noreturn]] void throwSQLiteError(sqlite3 * db, const FormatOrString & fs);
|
template<typename... Args>
|
||||||
|
[[noreturn]] static void throw_(sqlite3 * db, const std::string & fs, const Args & ... args) {
|
||||||
|
throw_(db, hintfmt(fs, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
SQLiteError(const char *path, int errNo, int extendedErrNo, const std::string & fs, const Args & ... args)
|
||||||
|
: SQLiteError(path, errNo, extendedErrNo, hintfmt(fs, args...))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
[[noreturn]] static void throw_(sqlite3 * db, hintformat && hf);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
MakeError(SQLiteBusy, SQLiteError);
|
||||||
|
|
||||||
void handleSQLiteBusy(const SQLiteBusy & e);
|
void handleSQLiteBusy(const SQLiteBusy & e);
|
||||||
|
|
||||||
|
|
68
src/libutil/chunked-vector.hh
Normal file
68
src/libutil/chunked-vector.hh
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bits/stdint-uintn.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <vector>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* Provides an indexable container like vector<> with memory overhead
|
||||||
|
guarantees like list<> by allocating storage in chunks of ChunkSize
|
||||||
|
elements instead of using a contiguous memory allocation like vector<>
|
||||||
|
does. Not using a single vector that is resized reduces memory overhead
|
||||||
|
on large data sets by on average (growth factor)/2, mostly
|
||||||
|
eliminates copies within the vector during resizing, and provides stable
|
||||||
|
references to its elements. */
|
||||||
|
template<typename T, size_t ChunkSize>
|
||||||
|
class ChunkedVector {
|
||||||
|
private:
|
||||||
|
uint32_t size_ = 0;
|
||||||
|
std::vector<std::vector<T>> chunks;
|
||||||
|
|
||||||
|
/* keep this out of the ::add hot path */
|
||||||
|
[[gnu::noinline]]
|
||||||
|
auto & addChunk()
|
||||||
|
{
|
||||||
|
if (size_ >= std::numeric_limits<uint32_t>::max() - ChunkSize)
|
||||||
|
abort();
|
||||||
|
chunks.emplace_back();
|
||||||
|
chunks.back().reserve(ChunkSize);
|
||||||
|
return chunks.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChunkedVector(uint32_t reserve)
|
||||||
|
{
|
||||||
|
chunks.reserve(reserve);
|
||||||
|
addChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t size() const { return size_; }
|
||||||
|
|
||||||
|
std::pair<T &, uint32_t> add(T value)
|
||||||
|
{
|
||||||
|
const auto idx = size_++;
|
||||||
|
auto & chunk = [&] () -> auto & {
|
||||||
|
if (auto & back = chunks.back(); back.size() < ChunkSize)
|
||||||
|
return back;
|
||||||
|
return addChunk();
|
||||||
|
}();
|
||||||
|
auto & result = chunk.emplace_back(std::move(value));
|
||||||
|
return {result, idx};
|
||||||
|
}
|
||||||
|
|
||||||
|
const T & operator[](uint32_t idx) const
|
||||||
|
{
|
||||||
|
return chunks[idx / ChunkSize][idx % ChunkSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Fn>
|
||||||
|
void forEach(Fn fn) const
|
||||||
|
{
|
||||||
|
for (const auto & c : chunks)
|
||||||
|
for (const auto & e : c)
|
||||||
|
fn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -87,11 +87,7 @@ struct ErrPos {
|
||||||
origin = pos.origin;
|
origin = pos.origin;
|
||||||
line = pos.line;
|
line = pos.line;
|
||||||
column = pos.column;
|
column = pos.column;
|
||||||
// is file symbol null?
|
file = pos.file;
|
||||||
if (pos.file.set())
|
|
||||||
file = pos.file;
|
|
||||||
else
|
|
||||||
file = "";
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <regex>
|
|
||||||
#include "ansicolor.hh"
|
#include "ansicolor.hh"
|
||||||
|
|
||||||
|
|
||||||
|
@ -155,15 +154,4 @@ inline hintformat hintfmt(std::string plain_string)
|
||||||
return hintfmt("%s", normaltxt(plain_string));
|
return hintfmt("%s", normaltxt(plain_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Highlight all the given matches in the given string `s` by wrapping
|
|
||||||
them between `prefix` and `postfix`.
|
|
||||||
|
|
||||||
If some matches overlap, then their union will be wrapped rather
|
|
||||||
than the individual matches. */
|
|
||||||
std::string hiliteMatches(
|
|
||||||
std::string_view s,
|
|
||||||
std::vector<std::smatch> matches,
|
|
||||||
std::string_view prefix,
|
|
||||||
std::string_view postfix);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#include "fmt.hh"
|
#include "hilite.hh"
|
||||||
|
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
20
src/libutil/hilite.hh
Normal file
20
src/libutil/hilite.hh
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* Highlight all the given matches in the given string `s` by wrapping
|
||||||
|
them between `prefix` and `postfix`.
|
||||||
|
|
||||||
|
If some matches overlap, then their union will be wrapped rather
|
||||||
|
than the individual matches. */
|
||||||
|
std::string hiliteMatches(
|
||||||
|
std::string_view s,
|
||||||
|
std::vector<std::smatch> matches,
|
||||||
|
std::string_view prefix,
|
||||||
|
std::string_view postfix);
|
||||||
|
|
||||||
|
}
|
|
@ -99,47 +99,4 @@ make_ref(Args&&... args)
|
||||||
return ref<T>(p);
|
return ref<T>(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* A non-nullable pointer.
|
|
||||||
This is similar to a C++ "& reference", but mutable.
|
|
||||||
This is similar to ref<T> but backed by a regular pointer instead of a smart pointer.
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
class ptr {
|
|
||||||
private:
|
|
||||||
T * p;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ptr<T>(const ptr<T> & r)
|
|
||||||
: p(r.p)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
explicit ptr<T>(T * p)
|
|
||||||
: p(p)
|
|
||||||
{
|
|
||||||
if (!p)
|
|
||||||
throw std::invalid_argument("null pointer cast to ptr");
|
|
||||||
}
|
|
||||||
|
|
||||||
T* operator ->() const
|
|
||||||
{
|
|
||||||
return &*p;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator *() const
|
|
||||||
{
|
|
||||||
return *p;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator == (const ptr<T> & other) const
|
|
||||||
{
|
|
||||||
return p == other.p;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator != (const ptr<T> & other) const
|
|
||||||
{
|
|
||||||
return p != other.p;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
54
src/libutil/tests/chunked-vector.cc
Normal file
54
src/libutil/tests/chunked-vector.cc
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include "chunked-vector.hh"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
TEST(ChunkedVector, InitEmpty) {
|
||||||
|
auto v = ChunkedVector<int, 2>(100);
|
||||||
|
ASSERT_EQ(v.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChunkedVector, GrowsCorrectly) {
|
||||||
|
auto v = ChunkedVector<int, 2>(100);
|
||||||
|
for (auto i = 1; i < 20; i++) {
|
||||||
|
v.add(i);
|
||||||
|
ASSERT_EQ(v.size(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChunkedVector, AddAndGet) {
|
||||||
|
auto v = ChunkedVector<int, 2>(100);
|
||||||
|
for (auto i = 1; i < 20; i++) {
|
||||||
|
auto [i2, idx] = v.add(i);
|
||||||
|
auto & i3 = v[idx];
|
||||||
|
ASSERT_EQ(i, i2);
|
||||||
|
ASSERT_EQ(&i2, &i3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChunkedVector, ForEach) {
|
||||||
|
auto v = ChunkedVector<int, 2>(100);
|
||||||
|
for (auto i = 1; i < 20; i++) {
|
||||||
|
v.add(i);
|
||||||
|
}
|
||||||
|
int count = 0;
|
||||||
|
v.forEach([&count](int elt) {
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
ASSERT_EQ(count, v.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChunkedVector, OverflowOK) {
|
||||||
|
// Similar to the AddAndGet, but intentionnally use a small
|
||||||
|
// initial ChunkedVector to force it to overflow
|
||||||
|
auto v = ChunkedVector<int, 2>(2);
|
||||||
|
for (auto i = 1; i < 20; i++) {
|
||||||
|
auto [i2, idx] = v.add(i);
|
||||||
|
auto & i3 = v[idx];
|
||||||
|
ASSERT_EQ(i, i2);
|
||||||
|
ASSERT_EQ(&i2, &i3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#include "fmt.hh"
|
#include "hilite.hh"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
/* ----------- tests for fmt.hh -------------------------------------------------*/
|
/* ----------- tests for fmt.hh -------------------------------------------------*/
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
|
@ -1082,7 +1082,7 @@ std::string runProgram(Path program, bool searchPath, const Strings & args,
|
||||||
auto res = runProgram(RunOptions {.program = program, .searchPath = searchPath, .args = args, .input = input});
|
auto res = runProgram(RunOptions {.program = program, .searchPath = searchPath, .args = args, .input = input});
|
||||||
|
|
||||||
if (!statusOk(res.first))
|
if (!statusOk(res.first))
|
||||||
throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first)));
|
throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));
|
||||||
|
|
||||||
return res.second;
|
return res.second;
|
||||||
}
|
}
|
||||||
|
@ -1210,7 +1210,7 @@ void runProgram2(const RunOptions & options)
|
||||||
if (source) promise.get_future().get();
|
if (source) promise.get_future().get();
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
throw ExecError(status, fmt("program '%1%' %2%", options.program, statusToString(status)));
|
throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1241,7 +1241,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
Attr & a(*attrs.find(i.name));
|
Attr & a(*attrs.find(i.name));
|
||||||
if(a.value->type() != nString) continue;
|
if(a.value->type() != nString) continue;
|
||||||
XMLAttrs attrs3;
|
XMLAttrs attrs3;
|
||||||
attrs3["type"] = i.name;
|
attrs3["type"] = globals.state->symbols[i.name];
|
||||||
attrs3["value"] = a.value->string.s;
|
attrs3["value"] = a.value->string.s;
|
||||||
xml.writeEmptyElement("string", attrs3);
|
xml.writeEmptyElement("string", attrs3);
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
the store; we need it for future modifications of the
|
the store; we need it for future modifications of the
|
||||||
environment. */
|
environment. */
|
||||||
std::ostringstream str;
|
std::ostringstream str;
|
||||||
manifest.print(str, true);
|
manifest.print(state.symbols, str, true);
|
||||||
auto manifestFile = state.store->addTextToStore("env-manifest.nix",
|
auto manifestFile = state.store->addTextToStore("env-manifest.nix",
|
||||||
str.str(), references);
|
str.str(), references);
|
||||||
|
|
||||||
|
@ -134,9 +134,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
|
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
|
||||||
PathSet context;
|
PathSet context;
|
||||||
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
|
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
|
||||||
auto topLevelDrv = state.coerceToStorePath(*aDrvPath.pos, *aDrvPath.value, context);
|
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context);
|
||||||
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
|
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
|
||||||
auto topLevelOut = state.coerceToStorePath(*aOutPath.pos, *aOutPath.value, context);
|
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context);
|
||||||
|
|
||||||
/* Realise the resulting store expression. */
|
/* Realise the resulting store expression. */
|
||||||
debug("building user environment");
|
debug("building user environment");
|
||||||
|
|
|
@ -31,7 +31,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||||
bool evalOnly, OutputKind output, bool location, Expr * e)
|
bool evalOnly, OutputKind output, bool location, Expr * e)
|
||||||
{
|
{
|
||||||
if (parseOnly) {
|
if (parseOnly) {
|
||||||
std::cout << format("%1%\n") % *e;
|
e->show(state.symbols, std::cout);
|
||||||
|
std::cout << "\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +56,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||||
printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context);
|
printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context);
|
||||||
else {
|
else {
|
||||||
if (strict) state.forceValueDeep(vRes);
|
if (strict) state.forceValueDeep(vRes);
|
||||||
std::cout << vRes << std::endl;
|
vRes.print(state.symbols, std::cout);
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DrvInfos drvs;
|
DrvInfos drvs;
|
||||||
|
|
|
@ -85,9 +85,9 @@ UnresolvedApp Installable::toApp(EvalState & state)
|
||||||
|
|
||||||
else if (type == "derivation") {
|
else if (type == "derivation") {
|
||||||
auto drvPath = cursor->forceDerivation();
|
auto drvPath = cursor->forceDerivation();
|
||||||
auto outPath = cursor->getAttr(state.sOutPath)->getString();
|
auto outPath = cursor->getAttr("outPath")->getString();
|
||||||
auto outputName = cursor->getAttr(state.sOutputName)->getString();
|
auto outputName = cursor->getAttr("outputName")->getString();
|
||||||
auto name = cursor->getAttr(state.sName)->getString();
|
auto name = cursor->getAttr("name")->getString();
|
||||||
auto aPname = cursor->maybeGetAttr("pname");
|
auto aPname = cursor->maybeGetAttr("pname");
|
||||||
auto aMeta = cursor->maybeGetAttr("meta");
|
auto aMeta = cursor->maybeGetAttr("meta");
|
||||||
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;
|
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;
|
||||||
|
|
|
@ -97,21 +97,23 @@ struct CmdBundle : InstallableCommand
|
||||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||||
|
|
||||||
PathSet context2;
|
PathSet context2;
|
||||||
auto drvPath = evalState->coerceToStorePath(*attr1->pos, *attr1->value, context2);
|
auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2);
|
||||||
|
|
||||||
auto attr2 = vRes->attrs->get(evalState->sOutPath);
|
auto attr2 = vRes->attrs->get(evalState->sOutPath);
|
||||||
if (!attr2)
|
if (!attr2)
|
||||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||||
|
|
||||||
auto outPath = evalState->coerceToStorePath(*attr2->pos, *attr2->value, context2);
|
auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2);
|
||||||
|
|
||||||
store->buildPaths({ DerivedPath::Built { drvPath } });
|
store->buildPaths({ DerivedPath::Built { drvPath } });
|
||||||
|
|
||||||
auto outPathS = store->printStorePath(outPath);
|
auto outPathS = store->printStorePath(outPath);
|
||||||
|
|
||||||
if (!outLink) {
|
if (!outLink) {
|
||||||
auto &attr = vRes->attrs->need(evalState->sName);
|
auto * attr = vRes->attrs->get(evalState->sName);
|
||||||
outLink = evalState->forceStringNoCtx(*attr.value,*attr.pos);
|
if (!attr)
|
||||||
|
throw Error("attribute 'name' missing");
|
||||||
|
outLink = evalState->forceStringNoCtx(*attr->value, attr->pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: will crash if not a localFSStore?
|
// TODO: will crash if not a localFSStore?
|
||||||
|
|
|
@ -28,19 +28,19 @@ struct CmdEdit : InstallableCommand
|
||||||
{
|
{
|
||||||
auto state = getEvalState();
|
auto state = getEvalState();
|
||||||
|
|
||||||
auto [v, pos] = installable->toValue(*state);
|
const auto [file, line] = [&] {
|
||||||
|
auto [v, pos] = installable->toValue(*state);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pos = findPackageFilename(*state, *v, installable->what());
|
return findPackageFilename(*state, *v, installable->what());
|
||||||
} catch (NoPositionInfo &) {
|
} catch (NoPositionInfo &) {
|
||||||
}
|
throw Error("cannot find position information for '%s", installable->what());
|
||||||
|
}
|
||||||
if (pos == noPos)
|
}();
|
||||||
throw Error("cannot find position information for '%s", installable->what());
|
|
||||||
|
|
||||||
stopProgressBar();
|
stopProgressBar();
|
||||||
|
|
||||||
auto args = editorFor(pos);
|
auto args = editorFor(file, line);
|
||||||
|
|
||||||
restoreProcessContext();
|
restoreProcessContext();
|
||||||
|
|
||||||
|
|
|
@ -77,9 +77,9 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
if (pathExists(*writeTo))
|
if (pathExists(*writeTo))
|
||||||
throw Error("path '%s' already exists", *writeTo);
|
throw Error("path '%s' already exists", *writeTo);
|
||||||
|
|
||||||
std::function<void(Value & v, const Pos & pos, const Path & path)> recurse;
|
std::function<void(Value & v, const PosIdx pos, const Path & path)> recurse;
|
||||||
|
|
||||||
recurse = [&](Value & v, const Pos & pos, const Path & path)
|
recurse = [&](Value & v, const PosIdx pos, const Path & path)
|
||||||
{
|
{
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (v.type() == nString)
|
if (v.type() == nString)
|
||||||
|
@ -88,18 +88,22 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
else if (v.type() == nAttrs) {
|
else if (v.type() == nAttrs) {
|
||||||
if (mkdir(path.c_str(), 0777) == -1)
|
if (mkdir(path.c_str(), 0777) == -1)
|
||||||
throw SysError("creating directory '%s'", path);
|
throw SysError("creating directory '%s'", path);
|
||||||
for (auto & attr : *v.attrs)
|
for (auto & attr : *v.attrs) {
|
||||||
|
std::string_view name = state->symbols[attr.name];
|
||||||
try {
|
try {
|
||||||
if (attr.name == "." || attr.name == "..")
|
if (name == "." || name == "..")
|
||||||
throw Error("invalid file name '%s'", attr.name);
|
throw Error("invalid file name '%s'", name);
|
||||||
recurse(*attr.value, *attr.pos, path + "/" + std::string(attr.name));
|
recurse(*attr.value, attr.pos, concatStrings(path, "/", name));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(*attr.pos, hintfmt("while evaluating the attribute '%s'", attr.name));
|
e.addTrace(
|
||||||
|
state->positions[attr.pos],
|
||||||
|
hintfmt("while evaluating the attribute '%s'", name));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw TypeError("value at '%s' is not a string or an attribute set", pos);
|
throw TypeError("value at '%s' is not a string or an attribute set", state->positions[pos]);
|
||||||
};
|
};
|
||||||
|
|
||||||
recurse(*v, pos, *writeTo);
|
recurse(*v, pos, *writeTo);
|
||||||
|
@ -117,7 +121,7 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
|
|
||||||
else {
|
else {
|
||||||
state->forceValueDeep(*v);
|
state->forceValueDeep(*v);
|
||||||
logger->cout("%s", *v);
|
logger->cout("%s", printValue(*state, *v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
178
src/nix/flake.cc
178
src/nix/flake.cc
|
@ -123,7 +123,7 @@ struct CmdFlakeLock : FlakeCommand
|
||||||
};
|
};
|
||||||
|
|
||||||
static void enumerateOutputs(EvalState & state, Value & vFlake,
|
static void enumerateOutputs(EvalState & state, Value & vFlake,
|
||||||
std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback)
|
std::function<void(const std::string & name, Value & vProvide, const PosIdx pos)> callback)
|
||||||
{
|
{
|
||||||
auto pos = vFlake.determinePos(noPos);
|
auto pos = vFlake.determinePos(noPos);
|
||||||
state.forceAttrs(vFlake, pos);
|
state.forceAttrs(vFlake, pos);
|
||||||
|
@ -139,11 +139,11 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
|
||||||
else. This way we can disable IFD for hydraJobs and then enable
|
else. This way we can disable IFD for hydraJobs and then enable
|
||||||
it for other outputs. */
|
it for other outputs. */
|
||||||
if (auto attr = aOutputs->value->attrs->get(sHydraJobs))
|
if (auto attr = aOutputs->value->attrs->get(sHydraJobs))
|
||||||
callback(attr->name, *attr->value, *attr->pos);
|
callback(state.symbols[attr->name], *attr->value, attr->pos);
|
||||||
|
|
||||||
for (auto & attr : *aOutputs->value->attrs) {
|
for (auto & attr : *aOutputs->value->attrs) {
|
||||||
if (attr.name != sHydraJobs)
|
if (attr.name != sHydraJobs)
|
||||||
callback(attr.name, *attr.value, *attr.pos);
|
callback(state.symbols[attr.name], *attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,14 +254,6 @@ struct CmdFlakeInfo : CmdFlakeMetadata
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool argHasName(std::string_view arg, std::string_view expected)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
arg == expected
|
|
||||||
|| arg == "_"
|
|
||||||
|| (hasPrefix(arg, "_") && arg.substr(1) == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CmdFlakeCheck : FlakeCommand
|
struct CmdFlakeCheck : FlakeCommand
|
||||||
{
|
{
|
||||||
bool build = true;
|
bool build = true;
|
||||||
|
@ -315,13 +307,25 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
|
|
||||||
// FIXME: rewrite to use EvalCache.
|
// FIXME: rewrite to use EvalCache.
|
||||||
|
|
||||||
auto checkSystemName = [&](const std::string & system, const Pos & pos) {
|
auto resolve = [&] (PosIdx p) {
|
||||||
// FIXME: what's the format of "system"?
|
return state->positions[p];
|
||||||
if (system.find('-') == std::string::npos)
|
|
||||||
reportError(Error("'%s' is not a valid system type, at %s", system, pos));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) -> std::optional<StorePath> {
|
auto argHasName = [&] (SymbolIdx arg, std::string_view expected) {
|
||||||
|
std::string_view name = state->symbols[arg];
|
||||||
|
return
|
||||||
|
name == expected
|
||||||
|
|| name == "_"
|
||||||
|
|| (hasPrefix(name, "_") && name.substr(1) == expected);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto checkSystemName = [&](const std::string & system, const PosIdx pos) {
|
||||||
|
// FIXME: what's the format of "system"?
|
||||||
|
if (system.find('-') == std::string::npos)
|
||||||
|
reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos)));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto checkDerivation = [&](const std::string & attrPath, Value & v, const PosIdx pos) -> std::optional<StorePath> {
|
||||||
try {
|
try {
|
||||||
auto drvInfo = getDerivation(*state, v, false);
|
auto drvInfo = getDerivation(*state, v, false);
|
||||||
if (!drvInfo)
|
if (!drvInfo)
|
||||||
|
@ -329,7 +333,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
// FIXME: check meta attributes
|
// FIXME: check meta attributes
|
||||||
return drvInfo->queryDrvPath();
|
return drvInfo->queryDrvPath();
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the derivation '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the derivation '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -337,7 +341,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
|
|
||||||
std::vector<DerivedPath> drvPaths;
|
std::vector<DerivedPath> drvPaths;
|
||||||
|
|
||||||
auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
#if 0
|
#if 0
|
||||||
// FIXME
|
// FIXME
|
||||||
|
@ -348,12 +352,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the app definition '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the app definition '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkOverlay = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (!v.isLambda()
|
if (!v.isLambda()
|
||||||
|
@ -368,12 +372,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
// FIXME: if we have a 'nixpkgs' input, use it to
|
// FIXME: if we have a 'nixpkgs' input, use it to
|
||||||
// evaluate the overlay.
|
// evaluate the overlay.
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the overlay '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the overlay '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkModule = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkModule = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (v.isLambda()) {
|
if (v.isLambda()) {
|
||||||
|
@ -382,9 +386,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
} else if (v.type() == nAttrs) {
|
} else if (v.type() == nAttrs) {
|
||||||
for (auto & attr : *v.attrs)
|
for (auto & attr : *v.attrs)
|
||||||
try {
|
try {
|
||||||
state->forceValue(*attr.value, *attr.pos);
|
state->forceValue(*attr.value, attr.pos);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(*attr.pos, hintfmt("while evaluating the option '%s'", attr.name));
|
e.addTrace(
|
||||||
|
state->positions[attr.pos],
|
||||||
|
hintfmt("while evaluating the option '%s'", state->symbols[attr.name]));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -392,14 +398,14 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
// FIXME: if we have a 'nixpkgs' input, use it to
|
// FIXME: if we have a 'nixpkgs' input, use it to
|
||||||
// check the module.
|
// check the module.
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the NixOS module '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the NixOS module '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<void(const std::string & attrPath, Value & v, const Pos & pos)> checkHydraJobs;
|
std::function<void(const std::string & attrPath, Value & v, const PosIdx pos)> checkHydraJobs;
|
||||||
|
|
||||||
checkHydraJobs = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
state->forceAttrs(v, pos);
|
state->forceAttrs(v, pos);
|
||||||
|
|
||||||
|
@ -407,23 +413,23 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
throw Error("jobset should not be a derivation at top-level");
|
throw Error("jobset should not be a derivation at top-level");
|
||||||
|
|
||||||
for (auto & attr : *v.attrs) {
|
for (auto & attr : *v.attrs) {
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
auto attrPath2 = attrPath + "." + (std::string) attr.name;
|
auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]);
|
||||||
if (state->isDerivation(*attr.value)) {
|
if (state->isDerivation(*attr.value)) {
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("checking Hydra job '%s'", attrPath2));
|
fmt("checking Hydra job '%s'", attrPath2));
|
||||||
checkDerivation(attrPath2, *attr.value, *attr.pos);
|
checkDerivation(attrPath2, *attr.value, attr.pos);
|
||||||
} else
|
} else
|
||||||
checkHydraJobs(attrPath2, *attr.value, *attr.pos);
|
checkHydraJobs(attrPath2, *attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the Hydra jobset '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the Hydra jobset '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("checking NixOS configuration '%s'", attrPath));
|
fmt("checking NixOS configuration '%s'", attrPath));
|
||||||
|
@ -433,12 +439,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
if (!state->isDerivation(*vToplevel))
|
if (!state->isDerivation(*vToplevel))
|
||||||
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
|
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the NixOS configuration '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the NixOS configuration '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkTemplate = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkTemplate = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("checking template '%s'", attrPath));
|
fmt("checking template '%s'", attrPath));
|
||||||
|
@ -448,7 +454,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
if (auto attr = v.attrs->get(state->symbols.create("path"))) {
|
if (auto attr = v.attrs->get(state->symbols.create("path"))) {
|
||||||
if (attr->name == state->symbols.create("path")) {
|
if (attr->name == state->symbols.create("path")) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto path = state->coerceToPath(*attr->pos, *attr->value, context);
|
auto path = state->coerceToPath(attr->pos, *attr->value, context);
|
||||||
if (!store->isInStore(path))
|
if (!store->isInStore(path))
|
||||||
throw Error("template '%s' has a bad 'path' attribute");
|
throw Error("template '%s' has a bad 'path' attribute");
|
||||||
// TODO: recursively check the flake in 'path'.
|
// TODO: recursively check the flake in 'path'.
|
||||||
|
@ -457,29 +463,29 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
throw Error("template '%s' lacks attribute 'path'", attrPath);
|
throw Error("template '%s' lacks attribute 'path'", attrPath);
|
||||||
|
|
||||||
if (auto attr = v.attrs->get(state->symbols.create("description")))
|
if (auto attr = v.attrs->get(state->symbols.create("description")))
|
||||||
state->forceStringNoCtx(*attr->value, *attr->pos);
|
state->forceStringNoCtx(*attr->value, attr->pos);
|
||||||
else
|
else
|
||||||
throw Error("template '%s' lacks attribute 'description'", attrPath);
|
throw Error("template '%s' lacks attribute 'description'", attrPath);
|
||||||
|
|
||||||
for (auto & attr : *v.attrs) {
|
for (auto & attr : *v.attrs) {
|
||||||
std::string name(attr.name);
|
std::string_view name(state->symbols[attr.name]);
|
||||||
if (name != "path" && name != "description" && name != "welcomeText")
|
if (name != "path" && name != "description" && name != "welcomeText")
|
||||||
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
|
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
|
||||||
}
|
}
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkBundler = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkBundler = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (!v.isLambda())
|
if (!v.isLambda())
|
||||||
throw Error("bundler must be a function");
|
throw Error("bundler must be a function");
|
||||||
// TODO: check types of inputs/outputs?
|
// TODO: check types of inputs/outputs?
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -492,7 +498,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
|
|
||||||
enumerateOutputs(*state,
|
enumerateOutputs(*state,
|
||||||
*vFlake,
|
*vFlake,
|
||||||
[&](const std::string & name, Value & vOutput, const Pos & pos) {
|
[&](const std::string & name, Value & vOutput, const PosIdx pos) {
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("checking flake output '%s'", name));
|
fmt("checking flake output '%s'", name));
|
||||||
|
|
||||||
|
@ -516,13 +522,14 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
if (name == "checks") {
|
if (name == "checks") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
for (auto & attr2 : *attr.value->attrs) {
|
for (auto & attr2 : *attr.value->attrs) {
|
||||||
auto drvPath = checkDerivation(
|
auto drvPath = checkDerivation(
|
||||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
*attr2.value, *attr2.pos);
|
*attr2.value, attr2.pos);
|
||||||
if (drvPath && (std::string) attr.name == settings.thisSystem.get())
|
if (drvPath && attr_name == settings.thisSystem.get())
|
||||||
drvPaths.push_back(DerivedPath::Built{*drvPath});
|
drvPaths.push_back(DerivedPath::Built{*drvPath});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -531,61 +538,66 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
else if (name == "formatter") {
|
else if (name == "formatter") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
|
checkSystemName(attr_name, attr.pos);
|
||||||
checkApp(
|
checkApp(
|
||||||
fmt("%s.%s", name, attr.name),
|
fmt("%s.%s", name, attr_name),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "packages" || name == "devShells") {
|
else if (name == "packages" || name == "devShells") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
for (auto & attr2 : *attr.value->attrs)
|
for (auto & attr2 : *attr.value->attrs)
|
||||||
checkDerivation(
|
checkDerivation(
|
||||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
*attr2.value, *attr2.pos);
|
*attr2.value, attr2.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "apps") {
|
else if (name == "apps") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
for (auto & attr2 : *attr.value->attrs)
|
for (auto & attr2 : *attr.value->attrs)
|
||||||
checkApp(
|
checkApp(
|
||||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
*attr2.value, *attr2.pos);
|
*attr2.value, attr2.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultPackage" || name == "devShell") {
|
else if (name == "defaultPackage" || name == "devShell") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
|
checkSystemName(attr_name, attr.pos);
|
||||||
checkDerivation(
|
checkDerivation(
|
||||||
fmt("%s.%s", name, attr.name),
|
fmt("%s.%s", name, attr_name),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultApp") {
|
else if (name == "defaultApp") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
|
checkSystemName(attr_name, attr.pos);
|
||||||
checkApp(
|
checkApp(
|
||||||
fmt("%s.%s", name, attr.name),
|
fmt("%s.%s", name, attr_name),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "legacyPackages") {
|
else if (name == "legacyPackages") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
checkSystemName(state->symbols[attr.name], attr.pos);
|
||||||
// FIXME: do getDerivations?
|
// FIXME: do getDerivations?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,8 +608,8 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
else if (name == "overlays") {
|
else if (name == "overlays") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkOverlay(fmt("%s.%s", name, attr.name),
|
checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "nixosModule")
|
else if (name == "nixosModule")
|
||||||
|
@ -606,15 +618,15 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
else if (name == "nixosModules") {
|
else if (name == "nixosModules") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkModule(fmt("%s.%s", name, attr.name),
|
checkModule(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "nixosConfigurations") {
|
else if (name == "nixosConfigurations") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkNixOSConfiguration(fmt("%s.%s", name, attr.name),
|
checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "hydraJobs")
|
else if (name == "hydraJobs")
|
||||||
|
@ -626,29 +638,31 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
else if (name == "templates") {
|
else if (name == "templates") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkTemplate(fmt("%s.%s", name, attr.name),
|
checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultBundler") {
|
else if (name == "defaultBundler") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
|
checkSystemName(attr_name, attr.pos);
|
||||||
checkBundler(
|
checkBundler(
|
||||||
fmt("%s.%s", name, attr.name),
|
fmt("%s.%s", name, attr_name),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "bundlers") {
|
else if (name == "bundlers") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
for (auto & attr2 : *attr.value->attrs) {
|
for (auto & attr2 : *attr.value->attrs) {
|
||||||
checkBundler(
|
checkBundler(
|
||||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
*attr2.value, *attr2.pos);
|
*attr2.value, attr2.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -657,7 +671,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
warn("unknown flake output '%s'", name);
|
warn("unknown flake output '%s'", name);
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking flake output '%s'", name));
|
e.addTrace(resolve(pos), hintfmt("while checking flake output '%s'", name));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -994,7 +1008,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
|
|
||||||
auto showDerivation = [&]()
|
auto showDerivation = [&]()
|
||||||
{
|
{
|
||||||
auto name = visitor.getAttr(state->sName)->getString();
|
auto name = visitor.getAttr("name")->getString();
|
||||||
if (json) {
|
if (json) {
|
||||||
std::optional<std::string> description;
|
std::optional<std::string> description;
|
||||||
if (auto aMeta = visitor.maybeGetAttr("meta")) {
|
if (auto aMeta = visitor.maybeGetAttr("meta")) {
|
||||||
|
|
|
@ -302,7 +302,7 @@ void mainWrapped(int argc, char * * argv)
|
||||||
b["arity"] = primOp->arity;
|
b["arity"] = primOp->arity;
|
||||||
b["args"] = primOp->args;
|
b["args"] = primOp->args;
|
||||||
b["doc"] = trim(stripIndentation(primOp->doc));
|
b["doc"] = trim(stripIndentation(primOp->doc));
|
||||||
res[(std::string) builtin.name] = std::move(b);
|
res[state.symbols[builtin.name]] = std::move(b);
|
||||||
}
|
}
|
||||||
std::cout << res.dump() << "\n";
|
std::cout << res.dump() << "\n";
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -199,11 +199,13 @@ static int main_nix_prefetch_url(int argc, char * * argv)
|
||||||
state->forceAttrs(v, noPos);
|
state->forceAttrs(v, noPos);
|
||||||
|
|
||||||
/* Extract the URL. */
|
/* Extract the URL. */
|
||||||
auto & attr = v.attrs->need(state->symbols.create("urls"));
|
auto * attr = v.attrs->get(state->symbols.create("urls"));
|
||||||
state->forceList(*attr.value, noPos);
|
if (!attr)
|
||||||
if (attr.value->listSize() < 1)
|
throw Error("attribute 'urls' missing");
|
||||||
|
state->forceList(*attr->value, noPos);
|
||||||
|
if (attr->value->listSize() < 1)
|
||||||
throw Error("'urls' list is empty");
|
throw Error("'urls' list is empty");
|
||||||
url = state->forceString(*attr.value->listElems()[0]);
|
url = state->forceString(*attr->value->listElems()[0]);
|
||||||
|
|
||||||
/* Extract the hash mode. */
|
/* Extract the hash mode. */
|
||||||
auto attr2 = v.attrs->get(state->symbols.create("outputHashMode"));
|
auto attr2 = v.attrs->get(state->symbols.create("outputHashMode"));
|
||||||
|
|
|
@ -73,7 +73,7 @@ struct NixRepl
|
||||||
void initEnv();
|
void initEnv();
|
||||||
void reloadFiles();
|
void reloadFiles();
|
||||||
void addAttrsToScope(Value & attrs);
|
void addAttrsToScope(Value & attrs);
|
||||||
void addVarToScope(const Symbol & name, Value & v);
|
void addVarToScope(const SymbolIdx name, Value & v);
|
||||||
Expr * parseString(std::string s);
|
Expr * parseString(std::string s);
|
||||||
void evalString(std::string s, Value & v);
|
void evalString(std::string s, Value & v);
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ std::string runNix(Path program, const Strings & args,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!statusOk(res.first))
|
if (!statusOk(res.first))
|
||||||
throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first)));
|
throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));
|
||||||
|
|
||||||
return res.second;
|
return res.second;
|
||||||
}
|
}
|
||||||
|
@ -347,9 +347,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||||
state->forceAttrs(v, noPos);
|
state->forceAttrs(v, noPos);
|
||||||
|
|
||||||
for (auto & i : *v.attrs) {
|
for (auto & i : *v.attrs) {
|
||||||
std::string name = i.name;
|
std::string_view name = state->symbols[i.name];
|
||||||
if (name.substr(0, cur2.size()) != cur2) continue;
|
if (name.substr(0, cur2.size()) != cur2) continue;
|
||||||
completions.insert(prev + expr + "." + name);
|
completions.insert(concatStrings(prev, expr, ".", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (ParseError & e) {
|
} catch (ParseError & e) {
|
||||||
|
@ -461,21 +461,23 @@ bool NixRepl::processLine(std::string line)
|
||||||
Value v;
|
Value v;
|
||||||
evalString(arg, v);
|
evalString(arg, v);
|
||||||
|
|
||||||
Pos pos;
|
const auto [file, line] = [&] () -> std::pair<std::string, uint32_t> {
|
||||||
|
if (v.type() == nPath || v.type() == nString) {
|
||||||
if (v.type() == nPath || v.type() == nString) {
|
PathSet context;
|
||||||
PathSet context;
|
auto filename = state->coerceToString(noPos, v, context).toOwned();
|
||||||
auto filename = state->coerceToString(noPos, v, context);
|
state->symbols.create(filename);
|
||||||
pos.file = state->symbols.create(*filename);
|
return {filename, 0};
|
||||||
} else if (v.isLambda()) {
|
} else if (v.isLambda()) {
|
||||||
pos = v.lambda.fun->pos;
|
auto pos = state->positions[v.lambda.fun->pos];
|
||||||
} else {
|
return {pos.file, pos.line};
|
||||||
// assume it's a derivation
|
} else {
|
||||||
pos = findPackageFilename(*state, v, arg);
|
// assume it's a derivation
|
||||||
}
|
return findPackageFilename(*state, v, arg);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
// Open in EDITOR
|
// Open in EDITOR
|
||||||
auto args = editorFor(pos);
|
auto args = editorFor(file, line);
|
||||||
auto editor = args.front();
|
auto editor = args.front();
|
||||||
args.pop_front();
|
args.pop_front();
|
||||||
|
|
||||||
|
@ -498,7 +500,7 @@ bool NixRepl::processLine(std::string line)
|
||||||
Value v, f, result;
|
Value v, f, result;
|
||||||
evalString(arg, v);
|
evalString(arg, v);
|
||||||
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
|
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
|
||||||
state->callFunction(f, v, result, Pos());
|
state->callFunction(f, v, result, PosIdx());
|
||||||
|
|
||||||
StorePath drvPath = getDerivationPath(result);
|
StorePath drvPath = getDerivationPath(result);
|
||||||
runNix("nix-shell", {state->store->printStorePath(drvPath)});
|
runNix("nix-shell", {state->store->printStorePath(drvPath)});
|
||||||
|
@ -671,7 +673,7 @@ void NixRepl::initEnv()
|
||||||
|
|
||||||
varNames.clear();
|
varNames.clear();
|
||||||
for (auto & i : state->staticBaseEnv.vars)
|
for (auto & i : state->staticBaseEnv.vars)
|
||||||
varNames.insert(i.first);
|
varNames.emplace(state->symbols[i.first]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -701,7 +703,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
||||||
for (auto & i : *attrs.attrs) {
|
for (auto & i : *attrs.attrs) {
|
||||||
staticEnv.vars.emplace_back(i.name, displ);
|
staticEnv.vars.emplace_back(i.name, displ);
|
||||||
env->values[displ++] = i.value;
|
env->values[displ++] = i.value;
|
||||||
varNames.insert((std::string) i.name);
|
varNames.emplace(state->symbols[i.name]);
|
||||||
}
|
}
|
||||||
staticEnv.sort();
|
staticEnv.sort();
|
||||||
staticEnv.deduplicate();
|
staticEnv.deduplicate();
|
||||||
|
@ -709,7 +711,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void NixRepl::addVarToScope(const Symbol & name, Value & v)
|
void NixRepl::addVarToScope(const SymbolIdx name, Value & v)
|
||||||
{
|
{
|
||||||
if (displ >= envSize)
|
if (displ >= envSize)
|
||||||
throw Error("environment full; cannot add more variables");
|
throw Error("environment full; cannot add more variables");
|
||||||
|
@ -718,7 +720,7 @@ void NixRepl::addVarToScope(const Symbol & name, Value & v)
|
||||||
staticEnv.vars.emplace_back(name, displ);
|
staticEnv.vars.emplace_back(name, displ);
|
||||||
staticEnv.sort();
|
staticEnv.sort();
|
||||||
env->values[displ++] = &v;
|
env->values[displ++] = &v;
|
||||||
varNames.insert((std::string) name);
|
varNames.emplace(state->symbols[name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -799,7 +801,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
||||||
PathSet context;
|
PathSet context;
|
||||||
if (i != v.attrs->end())
|
if (i != v.attrs->end())
|
||||||
str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context));
|
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context));
|
||||||
else
|
else
|
||||||
str << "???";
|
str << "???";
|
||||||
str << "»";
|
str << "»";
|
||||||
|
@ -811,7 +813,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
typedef std::map<std::string, Value *> Sorted;
|
typedef std::map<std::string, Value *> Sorted;
|
||||||
Sorted sorted;
|
Sorted sorted;
|
||||||
for (auto & i : *v.attrs)
|
for (auto & i : *v.attrs)
|
||||||
sorted[i.name] = i.value;
|
sorted.emplace(state->symbols[i.name], i.value);
|
||||||
|
|
||||||
for (auto & i : sorted) {
|
for (auto & i : sorted) {
|
||||||
if (isVarName(i.first))
|
if (isVarName(i.first))
|
||||||
|
@ -861,7 +863,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
case nFunction:
|
case nFunction:
|
||||||
if (v.isLambda()) {
|
if (v.isLambda()) {
|
||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << v.lambda.fun->pos;
|
s << state->positions[v.lambda.fun->pos];
|
||||||
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
|
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
|
||||||
} else if (v.isPrimOp()) {
|
} else if (v.isPrimOp()) {
|
||||||
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
|
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "eval-cache.hh"
|
#include "eval-cache.hh"
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "fmt.hh"
|
#include "hilite.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -154,7 +154,7 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
recurse();
|
recurse();
|
||||||
|
|
||||||
else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) {
|
else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) {
|
||||||
auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations);
|
auto attr = cursor.maybeGetAttr("recurseForDerivations");
|
||||||
if (attr && attr->getBool())
|
if (attr && attr->getBool())
|
||||||
recurse();
|
recurse();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ source common.sh
|
||||||
|
|
||||||
file=build-hook-ca-floating.nix
|
file=build-hook-ca-floating.nix
|
||||||
|
|
||||||
enableFeatures "ca-derivations ca-references"
|
enableFeatures "ca-derivations"
|
||||||
|
|
||||||
CONTENT_ADDRESSED=true
|
CONTENT_ADDRESSED=true
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
source ../common.sh
|
source ../common.sh
|
||||||
|
|
||||||
enableFeatures "ca-derivations ca-references"
|
enableFeatures "ca-derivations"
|
||||||
|
|
||||||
restartDaemon
|
restartDaemon
|
||||||
|
|
11
tests/ca/selfref-gc.sh
Executable file
11
tests/ca/selfref-gc.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
requireDaemonNewerThan "2.4pre20210626"
|
||||||
|
|
||||||
|
enableFeatures "ca-derivations nix-command flakes"
|
||||||
|
|
||||||
|
export NIX_TESTS_CA_BY_DEFAULT=1
|
||||||
|
cd ..
|
||||||
|
source ./selfref-gc.sh
|
|
@ -20,6 +20,8 @@ nix eval --expr 'assert 1 + 2 == 3; true'
|
||||||
[[ $(nix eval attr --json -f "./eval.nix") == '{"foo":"bar"}' ]]
|
[[ $(nix eval attr --json -f "./eval.nix") == '{"foo":"bar"}' ]]
|
||||||
[[ $(nix eval int -f - < "./eval.nix") == 123 ]]
|
[[ $(nix eval int -f - < "./eval.nix") == 123 ]]
|
||||||
|
|
||||||
|
# Check if toFile can be utilized during restricted eval
|
||||||
|
[[ $(nix eval --restrict-eval --expr 'import (builtins.toFile "source" "42")') == 42 ]]
|
||||||
|
|
||||||
nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
|
nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
|
||||||
[[ $(nix-instantiate -A int --eval "./eval.nix") == 123 ]]
|
[[ $(nix-instantiate -A int --eval "./eval.nix") == 123 ]]
|
||||||
|
|
|
@ -92,6 +92,7 @@ nix_tests = \
|
||||||
plugins.sh \
|
plugins.sh \
|
||||||
build.sh \
|
build.sh \
|
||||||
ca/nix-run.sh \
|
ca/nix-run.sh \
|
||||||
|
selfref-gc.sh ca/selfref-gc.sh \
|
||||||
db-migration.sh \
|
db-migration.sh \
|
||||||
bash-profile.sh \
|
bash-profile.sh \
|
||||||
pass-as-file.sh \
|
pass-as-file.sh \
|
||||||
|
|
|
@ -3,7 +3,7 @@ source common.sh
|
||||||
clearStore
|
clearStore
|
||||||
clearProfiles
|
clearProfiles
|
||||||
|
|
||||||
enableFeatures "ca-derivations ca-references"
|
enableFeatures "ca-derivations"
|
||||||
restartDaemon
|
restartDaemon
|
||||||
|
|
||||||
# Make a flake.
|
# Make a flake.
|
||||||
|
|
|
@ -13,7 +13,7 @@ MySettings mySettings;
|
||||||
|
|
||||||
static GlobalConfig::Register rs(&mySettings);
|
static GlobalConfig::Register rs(&mySettings);
|
||||||
|
|
||||||
static void prim_anotherNull (EvalState & state, const Pos & pos, Value ** args, Value & v)
|
static void prim_anotherNull (EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||||
{
|
{
|
||||||
if (mySettings.settingSet)
|
if (mySettings.settingSet)
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
|
|
30
tests/selfref-gc.sh
Normal file
30
tests/selfref-gc.sh
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
requireDaemonNewerThan "2.6.0pre20211215"
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
|
||||||
|
nix-build --no-out-link -E '
|
||||||
|
with import ./config.nix;
|
||||||
|
|
||||||
|
let d1 = mkDerivation {
|
||||||
|
name = "selfref-gc";
|
||||||
|
outputs = [ "out" ];
|
||||||
|
buildCommand = "
|
||||||
|
echo SELF_REF: $out > $out
|
||||||
|
";
|
||||||
|
}; in
|
||||||
|
|
||||||
|
# the only change from d1 is d1 as an (unused) build input
|
||||||
|
# to get identical store path in CA.
|
||||||
|
mkDerivation {
|
||||||
|
name = "selfref-gc";
|
||||||
|
outputs = [ "out" ];
|
||||||
|
buildCommand = "
|
||||||
|
echo UNUSED: ${d1}
|
||||||
|
echo SELF_REF: $out > $out
|
||||||
|
";
|
||||||
|
}
|
||||||
|
'
|
||||||
|
|
||||||
|
nix-collect-garbage
|
Loading…
Reference in a new issue