diff options
Diffstat (limited to 'src/nix/search.cc')
-rw-r--r-- | src/nix/search.cc | 235 |
1 files changed, 73 insertions, 162 deletions
diff --git a/src/nix/search.cc b/src/nix/search.cc index ba72c1e79..65a1e1818 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -6,8 +6,9 @@ #include "get-drvs.hh" #include "common-args.hh" #include "json.hh" -#include "json-to-value.hh" #include "shared.hh" +#include "eval-cache.hh" +#include "attr-path.hh" #include <regex> #include <fstream> @@ -25,33 +26,17 @@ std::string hilite(const std::string & s, const std::smatch & m, std::string pos m.empty() ? s : std::string(m.prefix()) - + ANSI_RED + std::string(m.str()) + postfix + + ANSI_GREEN + std::string(m.str()) + postfix + std::string(m.suffix()); } -struct CmdSearch : SourceExprCommand, MixJSON +struct CmdSearch : InstallableCommand, MixJSON { std::vector<std::string> res; - bool writeCache = true; - bool useCache = true; - CmdSearch() { expectArgs("regex", &res); - - addFlag({ - .longName = "update-cache", - .shortName = 'u', - .description = "update the package search cache", - .handler = {[&]() { writeCache = true; useCache = false; }} - }); - - addFlag({ - .longName = "no-cache", - .description = "do not use or update the package search cache", - .handler = {[&]() { writeCache = false; useCache = false; }} - }); } std::string description() override @@ -63,24 +48,32 @@ struct CmdSearch : SourceExprCommand, MixJSON { return { Example{ - "To show all available packages:", + "To show all packages in the flake in the current directory:", "nix search" }, Example{ - "To show any packages containing 'blender' in its name or description:", - "nix search blender" + "To show packages in the 'nixpkgs' flake containing 'blender' in its name or description:", + "nix search nixpkgs blender" }, Example{ "To search for Firefox or Chromium:", - "nix search 'firefox|chromium'" + "nix search nixpkgs 'firefox|chromium'" }, Example{ - "To search for git and frontend or gui:", - "nix search git 'frontend|gui'" + "To search for packages containing 'git' and either 'frontend' or 'gui':", + "nix search nixpkgs git 'frontend|gui'" } }; } + Strings getDefaultFlakeAttrPaths() override + { + return { + "packages." + settings.thisSystem.get() + ".", + "legacyPackages." + settings.thisSystem.get() + "." + }; + } + void run(ref<Store> store) override { settings.readOnlyMode = true; @@ -88,189 +81,107 @@ struct CmdSearch : SourceExprCommand, MixJSON // Empty search string should match all packages // Use "^" here instead of ".*" due to differences in resulting highlighting // (see #1893 -- libc++ claims empty search string is not in POSIX grammar) - if (res.empty()) { + if (res.empty()) res.push_back("^"); - } std::vector<std::regex> regexes; regexes.reserve(res.size()); - for (auto &re : res) { + for (auto & re : res) regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase)); - } auto state = getEvalState(); auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr; - auto sToplevel = state->symbols.create("_toplevel"); - auto sRecurse = state->symbols.create("recurseForDerivations"); - - bool fromCache = false; + uint64_t results = 0; - std::map<std::string, std::string> results; - - std::function<void(Value *, std::string, bool, JSONObject *)> doExpr; - - doExpr = [&](Value * v, std::string attrPath, bool toplevel, JSONObject * cache) { - debug("at attribute '%s'", attrPath); + std::function<void(eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath)> visit; + visit = [&](eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath) + { + Activity act(*logger, lvlInfo, actUnknown, + fmt("evaluating '%s'", concatStringsSep(".", attrPath))); try { - uint found = 0; + auto recurse = [&]() + { + for (const auto & attr : cursor.getAttrs()) { + auto cursor2 = cursor.getAttr(attr); + auto attrPath2(attrPath); + attrPath2.push_back(attr); + visit(*cursor2, attrPath2); + } + }; - state->forceValue(*v); + if (cursor.isDerivation()) { + size_t found = 0; - if (v->type == tLambda && toplevel) { - Value * v2 = state->allocValue(); - state->autoCallFunction(*state->allocBindings(1), *v, *v2); - v = v2; - state->forceValue(*v); - } + DrvName name(cursor.getAttr("name")->getString()); - if (state->isDerivation(*v)) { + auto aMeta = cursor.maybeGetAttr("meta"); + auto aDescription = aMeta ? aMeta->maybeGetAttr("description") : nullptr; + auto description = aDescription ? aDescription->getString() : ""; + std::replace(description.begin(), description.end(), '\n', ' '); + auto attrPath2 = concatStringsSep(".", attrPath); - DrvInfo drv(*state, attrPath, v->attrs); - std::string description; std::smatch attrPathMatch; std::smatch descriptionMatch; std::smatch nameMatch; - std::string name; - - DrvName parsed(drv.queryName()); - - for (auto ®ex : regexes) { - std::regex_search(attrPath, attrPathMatch, regex); - name = parsed.name; - std::regex_search(name, nameMatch, regex); - - description = drv.queryMetaString("description"); - std::replace(description.begin(), description.end(), '\n', ' '); + 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(attrPath); - - jsonElem.attr("pkgName", parsed.name); - jsonElem.attr("version", parsed.version); + auto jsonElem = jsonOut->object(attrPath2); + jsonElem.attr("pname", name.name); + jsonElem.attr("version", name.version); jsonElem.attr("description", description); - } else { - auto name = hilite(parsed.name, nameMatch, "\e[0;2m") - + std::string(parsed.fullName, parsed.name.length()); - results[attrPath] = fmt( - "* %s (%s)\n %s\n", - wrap("\e[0;1m", hilite(attrPath, attrPathMatch, "\e[0;1m")), - wrap("\e[0;2m", hilite(name, nameMatch, "\e[0;2m")), - hilite(description, descriptionMatch, ANSI_NORMAL)); - } - } - - if (cache) { - cache->attr("type", "derivation"); - cache->attr("name", drv.queryName()); - cache->attr("system", drv.querySystem()); - if (description != "") { - auto meta(cache->object("meta")); - meta.attr("description", description); + auto name2 = hilite(name.name, nameMatch, "\e[0;2m"); + if (results > 1) logger->stdout(""); + logger->stdout( + "* %s%s", + wrap("\e[0;1m", hilite(attrPath2, attrPathMatch, "\e[0;1m")), + name.version != "" ? " (" + name.version + ")" : ""); + if (description != "") + logger->stdout( + " %s", hilite(description, descriptionMatch, ANSI_NORMAL)); } } } - else if (v->type == tAttrs) { + else if ( + attrPath.size() == 0 + || (attrPath[0] == "legacyPackages" && attrPath.size() <= 2) + || (attrPath[0] == "packages" && attrPath.size() <= 2)) + recurse(); - if (!toplevel) { - auto attrs = v->attrs; - Bindings::iterator j = attrs->find(sRecurse); - if (j == attrs->end() || !state->forceBool(*j->value, *j->pos)) { - debug("skip attribute '%s'", attrPath); - return; - } - } - - bool toplevel2 = false; - if (!fromCache) { - Bindings::iterator j = v->attrs->find(sToplevel); - toplevel2 = j != v->attrs->end() && state->forceBool(*j->value, *j->pos); - } - - for (auto & i : *v->attrs) { - auto cache2 = - cache ? std::make_unique<JSONObject>(cache->object(i.name)) : nullptr; - doExpr(i.value, - attrPath == "" ? (std::string) i.name : attrPath + "." + (std::string) i.name, - toplevel2 || fromCache, cache2 ? cache2.get() : nullptr); - } + else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) { + auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations); + if (attr && attr->getBool()) + recurse(); } - } catch (AssertionError & e) { - } catch (Error & e) { - if (!toplevel) { - e.addPrefix(fmt("While evaluating the attribute '%s':\n", attrPath)); + } catch (EvalError & e) { + if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages")) throw; - } } }; - Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json"; - - if (useCache && pathExists(jsonCacheFileName)) { - - warn("using cached results; pass '-u' to update the cache"); - - Value vRoot; - parseJSON(*state, readFile(jsonCacheFileName), vRoot); - - fromCache = true; - - doExpr(&vRoot, "", true, nullptr); - } + for (auto & [cursor, prefix] : installable->getCursors(*state, true)) + visit(*cursor, parseAttrPath(*state, prefix)); - else { - createDirs(dirOf(jsonCacheFileName)); - - Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid()); - - std::ofstream jsonCacheFile; - - try { - // iostream considered harmful - jsonCacheFile.exceptions(std::ofstream::failbit); - jsonCacheFile.open(tmpFile); - - auto cache = writeCache ? std::make_unique<JSONObject>(jsonCacheFile, false) : nullptr; - - doExpr(getSourceExpr(*state), "", true, cache.get()); - - } catch (std::exception &) { - /* Fun fact: catching std::ios::failure does not work - due to C++11 ABI shenanigans. - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145 */ - if (!jsonCacheFile) - throw Error("error writing to %s", tmpFile); - throw; - } - - if (writeCache && rename(tmpFile.c_str(), jsonCacheFileName.c_str()) == -1) - throw SysError("cannot rename '%s' to '%s'", tmpFile, jsonCacheFileName); - } - - if (!json && results.size() == 0) + if (!json && !results) throw Error("no results for the given search term(s)!"); - - RunPager pager; - for (auto el : results) std::cout << el.second << "\n"; - } }; |