mirror of
https://github.com/NixOS/nix.git
synced 2025-11-20 09:19:36 +01:00
Use the cache in nix search
Not optimal atm, possibly because we don’t cache the evaluation failures
This commit is contained in:
parent
a6aaf81103
commit
9102508f33
5 changed files with 161 additions and 17 deletions
|
|
@ -302,6 +302,14 @@ Installable::getCursors(EvalState & state)
|
||||||
return {{evalCache->getRoot(), ""}};
|
return {{evalCache->getRoot(), ""}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<Value*, Pos> Installable::toValue(EvalState & state)
|
||||||
|
{
|
||||||
|
auto values = toValues(state);
|
||||||
|
if (values.empty())
|
||||||
|
throw Error("cannot find flake attribute '%s'", what());
|
||||||
|
return {std::get<0>(values[0]), std::get<1>(values[0])};
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
|
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
|
||||||
Installable::getCursor(EvalState & state)
|
Installable::getCursor(EvalState & state)
|
||||||
{
|
{
|
||||||
|
|
@ -378,11 +386,11 @@ struct InstallableAttrPath : InstallableValue
|
||||||
|
|
||||||
std::string what() override { return attrPath; }
|
std::string what() override { return attrPath; }
|
||||||
|
|
||||||
std::pair<Value *, Pos> toValue(EvalState & state) override
|
std::vector<std::tuple<Value *, Pos, std::string>> toValues(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);
|
state.forceValue(*vRes);
|
||||||
return {vRes, pos};
|
return {{vRes, pos, attrPath}};
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::vector<InstallableValue::DerivationInfo> toDerivations() override;
|
virtual std::vector<InstallableValue::DerivationInfo> toDerivations() override;
|
||||||
|
|
@ -539,25 +547,22 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
std::vector<std::tuple<Value *, Pos, std::string>>
|
||||||
|
InstallableFlake::toValues(EvalState & state)
|
||||||
{
|
{
|
||||||
auto lockedFlake = getLockedFlake();
|
auto vOutputs = getFlakeOutputs(state, *getLockedFlake());
|
||||||
|
|
||||||
auto vOutputs = getFlakeOutputs(state, *lockedFlake);
|
|
||||||
|
|
||||||
auto emptyArgs = state.allocBindings(0);
|
auto emptyArgs = state.allocBindings(0);
|
||||||
|
|
||||||
|
std::vector<std::tuple<Value *, Pos, std::string>> res;
|
||||||
|
|
||||||
for (auto & attrPath : getActualAttrPaths()) {
|
for (auto & attrPath : getActualAttrPaths()) {
|
||||||
try {
|
try {
|
||||||
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
|
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
|
||||||
state.forceValue(*v);
|
res.push_back({v, pos, attrPath});
|
||||||
return {v, pos};
|
} catch (Error &) {}
|
||||||
} catch (AttrPathNotFound & e) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Error("flake '%s' does not provide attribute %s",
|
return res;
|
||||||
flakeRef, showAttrPaths(getActualAttrPaths()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,11 @@ struct Installable
|
||||||
|
|
||||||
UnresolvedApp toApp(EvalState & state);
|
UnresolvedApp toApp(EvalState & state);
|
||||||
|
|
||||||
virtual std::pair<Value *, Pos> toValue(EvalState & state)
|
virtual std::vector<std::tuple<Value *, Pos, std::string>> toValues(EvalState & state)
|
||||||
{
|
{
|
||||||
throw Error("argument '%s' cannot be evaluated", what());
|
throw Error("argument '%s' cannot be evaluated", what());
|
||||||
}
|
}
|
||||||
|
std::pair<Value *, Pos> toValue(EvalState & state);
|
||||||
|
|
||||||
/* Return a value only if this installable is a store path or a
|
/* Return a value only if this installable is a store path or a
|
||||||
symlink to it. */
|
symlink to it. */
|
||||||
|
|
@ -109,7 +110,7 @@ struct InstallableFlake : InstallableValue
|
||||||
|
|
||||||
std::vector<DerivationInfo> toDerivations() override;
|
std::vector<DerivationInfo> toDerivations() override;
|
||||||
|
|
||||||
std::pair<Value *, Pos> toValue(EvalState & state) override;
|
std::vector<std::tuple<Value *, Pos, std::string>> toValues(EvalState & state) override;
|
||||||
|
|
||||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||||
getCursors(EvalState & state) override;
|
getCursors(EvalState & state) override;
|
||||||
|
|
|
||||||
|
|
@ -1284,6 +1284,43 @@ bool EvalState::getAttrField(Value & attrs, const std::vector<Symbol> & selector
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EvalState::getAttrFieldThrow(Value & attrs, const std::vector<Symbol> & selector, const Pos & pos, Value & dest)
|
||||||
|
{
|
||||||
|
if (!getAttrField(attrs, selector, pos, dest))
|
||||||
|
throw Error("Missing attribute path '%s'", "ImTooLazyToImplementThisRightNow");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Attr> EvalState::getFields(Value & attrs, const Pos & pos)
|
||||||
|
{
|
||||||
|
auto eval_cache = attrs.getEvalCache();
|
||||||
|
if (eval_cache.isEmpty()) {
|
||||||
|
forceValue(attrs, pos);
|
||||||
|
eval_cache = attrs.getEvalCache();
|
||||||
|
}
|
||||||
|
if (auto attrNames = eval_cache.listChildren(symbols)) {
|
||||||
|
bool everythingCached = true;
|
||||||
|
std::vector<Attr> res;
|
||||||
|
for (auto & attrName : *attrNames) {
|
||||||
|
auto newValue = allocValue();
|
||||||
|
try {
|
||||||
|
if (lazyGetAttrField(attrs, {attrName}, pos, *newValue)) {
|
||||||
|
res.push_back(Attr(attrName, newValue));
|
||||||
|
} else {
|
||||||
|
everythingCached = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (Error &) {
|
||||||
|
everythingCached = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (everythingCached) return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
forceValue(attrs);
|
||||||
|
auto attrsStart = attrs.attrs->attrs;
|
||||||
|
return std::vector<Attr>(attrsStart, attrsStart + attrs.attrs->size());
|
||||||
|
}
|
||||||
|
|
||||||
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
|
|
@ -1493,6 +1530,20 @@ std::optional<tree_cache::AttrValue> ValueCache::getRawValue()
|
||||||
return rawCache->getCachedValue();
|
return rawCache->getCachedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<Symbol>> ValueCache::listChildren(SymbolTable& symbols)
|
||||||
|
{
|
||||||
|
auto ret = std::vector<Symbol>();
|
||||||
|
if (rawCache) {
|
||||||
|
auto cachedValue = rawCache->getCachedValue();
|
||||||
|
if (std::get_if<tree_cache::attributeSet_t>(&cachedValue)) {
|
||||||
|
for (auto & fieldStr : rawCache->getChildren())
|
||||||
|
ret.push_back(symbols.create(fieldStr));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||||
{
|
{
|
||||||
if (auto evalCache = fun.getEvalCache(); !evalCache.isEmpty()) {
|
if (auto evalCache = fun.getEvalCache(); !evalCache.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -359,6 +359,12 @@ public:
|
||||||
// Similar to `getAttrField`, but if the cache says that the result is an
|
// Similar to `getAttrField`, but if the cache says that the result is an
|
||||||
// attribute set, just return a thunk to it rather than forcing it.
|
// attribute set, just return a thunk to it rather than forcing it.
|
||||||
bool lazyGetAttrField(Value & attrs, const std::vector<Symbol> & selector, const Pos & pos, Value & dest);
|
bool lazyGetAttrField(Value & attrs, const std::vector<Symbol> & selector, const Pos & pos, Value & dest);
|
||||||
|
|
||||||
|
// Similar to `getAttrField`, but throws an `Error` if the field can’t be
|
||||||
|
// found
|
||||||
|
void getAttrFieldThrow(Value & attrs, const std::vector<Symbol> & selector, const Pos & pos, Value & dest);
|
||||||
|
|
||||||
|
std::vector<Attr> getFields(Value & attrs, const Pos & pos);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,87 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
uint64_t results = 0;
|
uint64_t results = 0;
|
||||||
|
|
||||||
std::function<void(eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse)> visit;
|
std::function<void(eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse)> visit;
|
||||||
|
std::function<void(Value & current, const std::vector<Symbol> & attrPath, bool initialRecurse)> visit2;
|
||||||
|
|
||||||
|
Value * vTmp = state->allocValue();
|
||||||
|
|
||||||
|
visit2 = [&](Value & current, const std::vector<Symbol> & attrPath, bool initialRecurse)
|
||||||
|
{
|
||||||
|
Activity act(*logger, lvlInfo, actUnknown,
|
||||||
|
fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
|
||||||
|
auto recurse = [&]()
|
||||||
|
{
|
||||||
|
for (auto & attr : state->getFields(current, noPos)) {
|
||||||
|
auto attrPath2(attrPath);
|
||||||
|
attrPath2.push_back(attr.name);
|
||||||
|
visit2(*attr.value, attrPath2, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (state->isDerivation(current)) {
|
||||||
|
size_t found = 0;
|
||||||
|
|
||||||
|
state->getAttrFieldThrow(current, {state->sName}, noPos, *vTmp);
|
||||||
|
DrvName name(state->forceString(*vTmp));
|
||||||
|
|
||||||
|
auto hasDescription = state->getAttrField(current, {state->sMeta, state->sDescription}, noPos, *vTmp);
|
||||||
|
auto description = hasDescription ? state->forceString(*vTmp) : "";
|
||||||
|
std::replace(description.begin(), description.end(), '\n', ' ');
|
||||||
|
auto attrPath2 = concatStringsSep(".", attrPath);
|
||||||
|
|
||||||
|
std::smatch attrPathMatch;
|
||||||
|
std::smatch descriptionMatch;
|
||||||
|
std::smatch nameMatch;
|
||||||
|
|
||||||
|
for (auto & regex : regexes) {
|
||||||
|
std::regex_search(attrPath2, attrPathMatch, regex);
|
||||||
|
std::regex_search(name.name, nameMatch, regex);
|
||||||
|
std::regex_search(description, descriptionMatch, regex);
|
||||||
|
if (!attrPathMatch.empty()
|
||||||
|
|| !nameMatch.empty()
|
||||||
|
|| !descriptionMatch.empty())
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found == res.size()) {
|
||||||
|
results++;
|
||||||
|
if (json) {
|
||||||
|
auto jsonElem = jsonOut->object(attrPath2);
|
||||||
|
jsonElem.attr("pname", name.name);
|
||||||
|
jsonElem.attr("version", name.version);
|
||||||
|
jsonElem.attr("description", description);
|
||||||
|
} else {
|
||||||
|
auto name2 = hilite(name.name, nameMatch, "\e[0;2m");
|
||||||
|
if (results > 1) logger->cout("");
|
||||||
|
logger->cout(
|
||||||
|
"* %s%s",
|
||||||
|
wrap("\e[0;1m", hilite(attrPath2, attrPathMatch, "\e[0;1m")),
|
||||||
|
name.version != "" ? " (" + name.version + ")" : "");
|
||||||
|
if (description != "")
|
||||||
|
logger->cout(
|
||||||
|
" %s", hilite(description, descriptionMatch, ANSI_NORMAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
attrPath.size() == 0
|
||||||
|
|| (attrPath[0] == "legacyPackages" && attrPath.size() <= 2)
|
||||||
|
|| (attrPath[0] == "packages" && attrPath.size() <= 2))
|
||||||
|
recurse();
|
||||||
|
|
||||||
|
else if (initialRecurse)
|
||||||
|
recurse();
|
||||||
|
|
||||||
|
else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) {
|
||||||
|
auto hasRecurse = state->getAttrField(current, {state->sRecurseForDerivations}, noPos, *vTmp);
|
||||||
|
if (hasRecurse && state->forceBool(*vTmp, noPos))
|
||||||
|
recurse();
|
||||||
|
}
|
||||||
|
} catch (EvalError & e) {
|
||||||
|
if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages"))
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
visit = [&](eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse)
|
visit = [&](eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse)
|
||||||
{
|
{
|
||||||
|
|
@ -165,8 +246,8 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto & [cursor, prefix] : installable->getCursors(*state))
|
for (auto & [value, pos, prefix] : installable->toValues(*state))
|
||||||
visit(*cursor, parseAttrPath(*state, prefix), true);
|
visit2(*value, parseAttrPath(*state, prefix), 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)!");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue