Make InstallableFlake::toValue() and toDerivation() behave consistently

In particular, this means that 'nix eval` (which uses toValue()) no
longer auto-calls functions or functors (because
AttrCursor::findAlongAttrPath() doesn't).

Fixes #6152.

Also use ref<> in a few places, and don't return attrpaths from
getCursor() because cursors already have a getAttrPath() method.
This commit is contained in:
Eelco Dolstra 2022-04-14 14:04:19 +02:00
parent 0e58affd39
commit d89840b103
7 changed files with 72 additions and 75 deletions

View file

@ -334,16 +334,16 @@ DerivedPath Installable::toDerivedPath()
return std::move(buildables[0]); return std::move(buildables[0]);
} }
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> std::vector<ref<eval_cache::AttrCursor>>
Installable::getCursors(EvalState & state) Installable::getCursors(EvalState & state)
{ {
auto evalCache = auto evalCache =
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state, std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state,
[&]() { return toValue(state).first; }); [&]() { return toValue(state).first; });
return {{evalCache->getRoot(), ""}}; return {evalCache->getRoot()};
} }
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string> ref<eval_cache::AttrCursor>
Installable::getCursor(EvalState & state) Installable::getCursor(EvalState & state)
{ {
auto cursors = getCursors(state); auto cursors = getCursors(state);
@ -566,43 +566,21 @@ InstallableFlake::InstallableFlake(
std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation() std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation()
{ {
auto lockedFlake = getLockedFlake(); auto attr = getCursor(*state);
auto cache = openEvalCache(*state, lockedFlake); auto attrPath = attr->getAttrPathStr();
auto root = cache->getRoot();
Suggestions suggestions; if (!attr->isDerivation())
throw Error("flake output attribute '%s' is not a derivation", attrPath);
for (auto & attrPath : getActualAttrPaths()) { auto drvPath = attr->forceDerivation();
debug("trying flake output attribute '%s'", attrPath);
auto attrOrSuggestions = root->findAlongAttrPath( auto drvInfo = DerivationInfo {
parseAttrPath(*state, attrPath), std::move(drvPath),
true attr->getAttr(state->sOutputName)->getString()
); };
if (!attrOrSuggestions) { return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
suggestions += attrOrSuggestions.getSuggestions();
continue;
}
auto attr = *attrOrSuggestions;
if (!attr->isDerivation())
throw Error("flake output attribute '%s' is not a derivation", attrPath);
auto drvPath = attr->forceDerivation();
auto drvInfo = DerivationInfo {
std::move(drvPath),
attr->getAttr(state->sOutputName)->getString()
};
return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
}
throw Error(suggestions, "flake '%s' does not provide attribute %s",
flakeRef, showAttrPaths(getActualAttrPaths()));
} }
std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations() std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
@ -614,33 +592,10 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state) std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
{ {
auto lockedFlake = getLockedFlake(); return {&getCursor(state)->forceValue(), noPos};
auto vOutputs = getFlakeOutputs(state, *lockedFlake);
auto emptyArgs = state.allocBindings(0);
Suggestions suggestions;
for (auto & attrPath : getActualAttrPaths()) {
try {
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
state.forceValue(*v, pos);
return {v, pos};
} catch (AttrPathNotFound & e) {
suggestions += e.info().suggestions;
}
}
throw Error(
suggestions,
"flake '%s' does not provide attribute %s",
flakeRef,
showAttrPaths(getActualAttrPaths())
);
} }
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> std::vector<ref<eval_cache::AttrCursor>>
InstallableFlake::getCursors(EvalState & state) InstallableFlake::getCursors(EvalState & state)
{ {
auto evalCache = openEvalCache(state, auto evalCache = openEvalCache(state,
@ -648,21 +603,55 @@ InstallableFlake::getCursors(EvalState & state)
auto root = evalCache->getRoot(); auto root = evalCache->getRoot();
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> res; std::vector<ref<eval_cache::AttrCursor>> res;
for (auto & attrPath : getActualAttrPaths()) { for (auto & attrPath : getActualAttrPaths()) {
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
if (attr) res.push_back({*attr, attrPath}); if (attr) res.push_back(ref(*attr));
} }
return res; return res;
} }
ref<eval_cache::AttrCursor> InstallableFlake::getCursor(EvalState & state)
{
auto lockedFlake = getLockedFlake();
auto cache = openEvalCache(state, lockedFlake);
auto root = cache->getRoot();
Suggestions suggestions;
auto attrPaths = getActualAttrPaths();
for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath);
auto attrOrSuggestions = root->findAlongAttrPath(
parseAttrPath(state, attrPath),
true
);
if (!attrOrSuggestions) {
suggestions += attrOrSuggestions.getSuggestions();
continue;
}
return *attrOrSuggestions;
}
throw Error(
suggestions,
"flake '%s' does not provide attribute %s",
flakeRef,
showAttrPaths(attrPaths));
}
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
{ {
flake::LockFlags lockFlagsApplyConfig = lockFlags;
lockFlagsApplyConfig.applyNixConfig = true;
if (!_lockedFlake) { if (!_lockedFlake) {
flake::LockFlags lockFlagsApplyConfig = lockFlags;
lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig)); _lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
} }
return _lockedFlake; return _lockedFlake;

View file

@ -80,10 +80,10 @@ struct Installable
return {}; return {};
} }
virtual std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> virtual std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state); getCursors(EvalState & state);
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string> virtual ref<eval_cache::AttrCursor>
getCursor(EvalState & state); getCursor(EvalState & state);
virtual FlakeRef nixpkgsFlakeRef() const virtual FlakeRef nixpkgsFlakeRef() const
@ -180,9 +180,15 @@ struct InstallableFlake : InstallableValue
std::pair<Value *, Pos> toValue(EvalState & state) override; std::pair<Value *, Pos> toValue(EvalState & state) override;
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> /* Get a cursor to every attrpath in getActualAttrPaths() that
exists. */
std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state) override; getCursors(EvalState & state) override;
/* Get a cursor to the first attrpath in getActualAttrPaths() that
exists, or throw an exception with suggestions if none exists. */
ref<eval_cache::AttrCursor> getCursor(EvalState & state) override;
std::shared_ptr<flake::LockedFlake> getLockedFlake() const; std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
FlakeRef nixpkgsFlakeRef() const override; FlakeRef nixpkgsFlakeRef() const override;

View file

@ -306,9 +306,9 @@ Value * EvalCache::getRootValue()
return *value; return *value;
} }
std::shared_ptr<AttrCursor> EvalCache::getRoot() ref<AttrCursor> EvalCache::getRoot()
{ {
return std::make_shared<AttrCursor>(ref(shared_from_this()), std::nullopt); return make_ref<AttrCursor>(ref(shared_from_this()), std::nullopt);
} }
AttrCursor::AttrCursor( AttrCursor::AttrCursor(

View file

@ -33,7 +33,7 @@ public:
EvalState & state, EvalState & state,
RootLoader rootLoader); RootLoader rootLoader);
std::shared_ptr<AttrCursor> getRoot(); ref<AttrCursor> getRoot();
}; };
enum AttrType { enum AttrType {
@ -104,6 +104,8 @@ public:
ref<AttrCursor> getAttr(std::string_view name); ref<AttrCursor> getAttr(std::string_view name);
/* Get an attribute along a chain of attrsets. Note that this does
not auto-call functors or functions. */
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false); OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
std::string getString(); std::string getString();

View file

@ -61,7 +61,7 @@ std::string resolveString(Store & store, const std::string & toResolve, const Bu
UnresolvedApp Installable::toApp(EvalState & state) UnresolvedApp Installable::toApp(EvalState & state)
{ {
auto [cursor, attrPath] = getCursor(state); auto cursor = getCursor(state);
auto type = cursor->getAttr("type")->getString(); auto type = cursor->getAttr("type")->getString();
@ -101,7 +101,7 @@ UnresolvedApp Installable::toApp(EvalState & state)
} }
else else
throw Error("attribute '%s' has unsupported type '%s'", attrPath, type); throw Error("attribute '%s' has unsupported type '%s'", cursor->getAttrPathStr(), type);
} }
// FIXME: move to libcmd // FIXME: move to libcmd

View file

@ -705,7 +705,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
defaultTemplateAttrPathsPrefixes, defaultTemplateAttrPathsPrefixes,
lockFlags); lockFlags);
auto [cursor, attrPath] = installable.getCursor(*evalState); auto cursor = installable.getCursor(*evalState);
auto templateDirAttr = cursor->getAttr("path"); auto templateDirAttr = cursor->getAttr("path");
auto templateDir = templateDirAttr->getString(); auto templateDir = templateDirAttr->getString();

View file

@ -165,8 +165,8 @@ struct CmdSearch : InstallableCommand, MixJSON
} }
}; };
for (auto & [cursor, prefix] : installable->getCursors(*state)) for (auto & cursor : installable->getCursors(*state))
visit(*cursor, parseAttrPath(*state, prefix), true); visit(*cursor, cursor->getAttrPath(), true);
if (!json && !results) if (!json && !results)
throw Error("no results for the given search term(s)!"); throw Error("no results for the given search term(s)!");