forked from lix-project/lix
Revive 'nix search'
It uses the evaluation cache now rather than the ad hoc JSON cache.
This commit is contained in:
parent
42a12f9232
commit
b69323f8c9
|
@ -695,7 +695,7 @@ struct CmdFlakeShow : FlakeCommand
|
||||||
void run(nix::ref<nix::Store> store) override
|
void run(nix::ref<nix::Store> store) override
|
||||||
{
|
{
|
||||||
auto state = getEvalState();
|
auto state = getEvalState();
|
||||||
auto flake = lockFlake();
|
auto flake = std::make_shared<LockedFlake>(lockFlake());
|
||||||
|
|
||||||
std::function<void(eval_cache::AttrCursor & visitor, const std::vector<Symbol> & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit;
|
std::function<void(eval_cache::AttrCursor & visitor, const std::vector<Symbol> & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit;
|
||||||
|
|
||||||
|
@ -815,7 +815,7 @@ struct CmdFlakeShow : FlakeCommand
|
||||||
|
|
||||||
auto cache = openEvalCache(*state, flake, useEvalCache);
|
auto cache = openEvalCache(*state, flake, useEvalCache);
|
||||||
|
|
||||||
visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake.flake.lockedRef), "");
|
visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake->flake.lockedRef), "");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,15 @@ App Installable::toApp(EvalState & state)
|
||||||
return App(state, *toValue(state).first);
|
return App(state, *toValue(state).first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||||
|
Installable::getCursor(EvalState & state, bool useEvalCache)
|
||||||
|
{
|
||||||
|
auto evalCache =
|
||||||
|
std::make_shared<nix::eval_cache::EvalCache>(false, Hash(), state,
|
||||||
|
[&]() { return toValue(state).first; });
|
||||||
|
return {{evalCache->getRoot(), ""}};
|
||||||
|
}
|
||||||
|
|
||||||
struct InstallableStorePath : Installable
|
struct InstallableStorePath : Installable
|
||||||
{
|
{
|
||||||
ref<Store> store;
|
ref<Store> store;
|
||||||
|
@ -285,14 +294,14 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked
|
||||||
|
|
||||||
ref<eval_cache::EvalCache> openEvalCache(
|
ref<eval_cache::EvalCache> openEvalCache(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const flake::LockedFlake & lockedFlake,
|
std::shared_ptr<flake::LockedFlake> lockedFlake,
|
||||||
bool useEvalCache)
|
bool useEvalCache)
|
||||||
{
|
{
|
||||||
return ref(std::make_shared<nix::eval_cache::EvalCache>(
|
return ref(std::make_shared<nix::eval_cache::EvalCache>(
|
||||||
useEvalCache,
|
useEvalCache,
|
||||||
lockedFlake.getFingerprint(),
|
lockedFlake->getFingerprint(),
|
||||||
state,
|
state,
|
||||||
[&]()
|
[&state, lockedFlake]()
|
||||||
{
|
{
|
||||||
/* For testing whether the evaluation cache is
|
/* For testing whether the evaluation cache is
|
||||||
complete. */
|
complete. */
|
||||||
|
@ -300,7 +309,7 @@ ref<eval_cache::EvalCache> openEvalCache(
|
||||||
throw Error("not everything is cached, but evaluation is not allowed");
|
throw Error("not everything is cached, but evaluation is not allowed");
|
||||||
|
|
||||||
auto vFlake = state.allocValue();
|
auto vFlake = state.allocValue();
|
||||||
flake::callFlake(state, lockedFlake, *vFlake);
|
flake::callFlake(state, *lockedFlake, *vFlake);
|
||||||
|
|
||||||
state.forceAttrs(*vFlake);
|
state.forceAttrs(*vFlake);
|
||||||
|
|
||||||
|
@ -315,7 +324,8 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
|
||||||
{
|
{
|
||||||
auto state = cmd.getEvalState();
|
auto state = cmd.getEvalState();
|
||||||
|
|
||||||
auto lockedFlake = lockFlake(*state, flakeRef, cmd.lockFlags);
|
auto lockedFlake = std::make_shared<flake::LockedFlake>(
|
||||||
|
lockFlake(*state, flakeRef, cmd.lockFlags));
|
||||||
|
|
||||||
auto cache = openEvalCache(*state, lockedFlake, true);
|
auto cache = openEvalCache(*state, lockedFlake, true);
|
||||||
auto root = cache->getRoot();
|
auto root = cache->getRoot();
|
||||||
|
@ -343,7 +353,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
|
||||||
attr->getAttr(state->sOutputName)->getString()
|
attr->getAttr(state->sOutputName)->getString()
|
||||||
};
|
};
|
||||||
|
|
||||||
return {attrPath, lockedFlake.flake.lockedRef, std::move(drvInfo)};
|
return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Error("flake '%s' does not provide attribute %s",
|
throw Error("flake '%s' does not provide attribute %s",
|
||||||
|
@ -378,6 +388,25 @@ std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
||||||
flakeRef, concatStringsSep(", ", quoteStrings(attrPaths)));
|
flakeRef, concatStringsSep(", ", quoteStrings(attrPaths)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||||
|
InstallableFlake::getCursor(EvalState & state, bool useEvalCache)
|
||||||
|
{
|
||||||
|
auto evalCache = openEvalCache(state,
|
||||||
|
std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, cmd.lockFlags)),
|
||||||
|
useEvalCache);
|
||||||
|
|
||||||
|
auto root = evalCache->getRoot();
|
||||||
|
|
||||||
|
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> res;
|
||||||
|
|
||||||
|
for (auto & attrPath : getActualAttrPaths()) {
|
||||||
|
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
||||||
|
if (attr) res.push_back({attr, attrPath});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: extend
|
// FIXME: extend
|
||||||
std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)";
|
std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)";
|
||||||
static std::regex attrPathRegex(fmt(R"(%1%(\.%1%)*)", attrRegex));
|
static std::regex attrPathRegex(fmt(R"(%1%(\.%1%)*)", attrRegex));
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace nix {
|
||||||
struct DrvInfo;
|
struct DrvInfo;
|
||||||
struct SourceExprCommand;
|
struct SourceExprCommand;
|
||||||
|
|
||||||
namespace eval_cache { class EvalCache; }
|
namespace eval_cache { class EvalCache; class AttrCursor; }
|
||||||
|
|
||||||
struct Buildable
|
struct Buildable
|
||||||
{
|
{
|
||||||
|
@ -57,6 +57,9 @@ struct Installable
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||||
|
getCursor(EvalState & state, bool useEvalCache);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InstallableValue : Installable
|
struct InstallableValue : Installable
|
||||||
|
@ -100,11 +103,14 @@ struct InstallableFlake : InstallableValue
|
||||||
std::vector<DerivationInfo> toDerivations() override;
|
std::vector<DerivationInfo> toDerivations() override;
|
||||||
|
|
||||||
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>>
|
||||||
|
getCursor(EvalState & state, bool useEvalCache) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<eval_cache::EvalCache> openEvalCache(
|
ref<eval_cache::EvalCache> openEvalCache(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const flake::LockedFlake & lockedFlake,
|
std::shared_ptr<flake::LockedFlake> lockedFlake,
|
||||||
bool useEvalCache);
|
bool useEvalCache);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "json.hh"
|
#include "json.hh"
|
||||||
#include "json-to-value.hh"
|
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
|
#include "eval-cache.hh"
|
||||||
|
#include "attr-path.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -25,7 +26,7 @@ std::string hilite(const std::string & s, const std::smatch & m, std::string pos
|
||||||
m.empty()
|
m.empty()
|
||||||
? s
|
? s
|
||||||
: std::string(m.prefix())
|
: std::string(m.prefix())
|
||||||
+ ANSI_RED + std::string(m.str()) + postfix
|
+ ANSI_GREEN + std::string(m.str()) + postfix
|
||||||
+ std::string(m.suffix());
|
+ std::string(m.suffix());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,23 +34,9 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
{
|
{
|
||||||
std::vector<std::string> res;
|
std::vector<std::string> res;
|
||||||
|
|
||||||
bool writeCache = true;
|
|
||||||
bool useCache = true;
|
|
||||||
|
|
||||||
CmdSearch()
|
CmdSearch()
|
||||||
{
|
{
|
||||||
expectArgs("regex", &res);
|
expectArgs("regex", &res);
|
||||||
|
|
||||||
mkFlag()
|
|
||||||
.longName("update-cache")
|
|
||||||
.shortName('u')
|
|
||||||
.description("update the package search cache")
|
|
||||||
.handler([&]() { writeCache = true; useCache = false; });
|
|
||||||
|
|
||||||
mkFlag()
|
|
||||||
.longName("no-cache")
|
|
||||||
.description("do not use or update the package search cache")
|
|
||||||
.handler([&]() { writeCache = false; useCache = false; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
@ -61,27 +48,30 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
Example{
|
Example{
|
||||||
"To show all available packages:",
|
"To show all packages in the flake in the current directory:",
|
||||||
"nix search"
|
"nix search"
|
||||||
},
|
},
|
||||||
Example{
|
Example{
|
||||||
"To show any packages containing 'blender' in its name or description:",
|
"To show packages in the 'nixpkgs' flake containing 'blender' in its name or description:",
|
||||||
"nix search blender"
|
"nix search nixpkgs blender"
|
||||||
},
|
},
|
||||||
Example{
|
Example{
|
||||||
"To search for Firefox or Chromium:",
|
"To search for Firefox or Chromium:",
|
||||||
"nix search 'firefox|chromium'"
|
"nix search nixpkgs 'firefox|chromium'"
|
||||||
},
|
},
|
||||||
Example{
|
Example{
|
||||||
"To search for git and frontend or gui:",
|
"To search for packages containing 'git' and either 'frontend' or 'gui':",
|
||||||
"nix search git 'frontend|gui'"
|
"nix search nixpkgs git 'frontend|gui'"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings getDefaultFlakeAttrPaths() override
|
Strings getDefaultFlakeAttrPaths() override
|
||||||
{
|
{
|
||||||
return {""};
|
return {
|
||||||
|
"packages." + settings.thisSystem.get() + ".",
|
||||||
|
"legacyPackages." + settings.thisSystem.get() + "."
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(ref<Store> store) override
|
void run(ref<Store> store) override
|
||||||
|
@ -91,9 +81,8 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
// Empty search string should match all packages
|
// Empty search string should match all packages
|
||||||
// Use "^" here instead of ".*" due to differences in resulting highlighting
|
// Use "^" here instead of ".*" due to differences in resulting highlighting
|
||||||
// (see #1893 -- libc++ claims empty search string is not in POSIX grammar)
|
// (see #1893 -- libc++ claims empty search string is not in POSIX grammar)
|
||||||
if (res.empty()) {
|
if (res.empty())
|
||||||
res.push_back("^");
|
res.push_back("^");
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::regex> regexes;
|
std::vector<std::regex> regexes;
|
||||||
regexes.reserve(res.size());
|
regexes.reserve(res.size());
|
||||||
|
@ -103,181 +92,91 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
|
|
||||||
auto state = getEvalState();
|
auto state = getEvalState();
|
||||||
|
|
||||||
//auto [value, pos] = installable->toValue(*state);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr;
|
auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr;
|
||||||
|
|
||||||
auto sToplevel = state->symbols.create("_toplevel");
|
uint64_t results = 0;
|
||||||
auto sRecurse = state->symbols.create("recurseForDerivations");
|
|
||||||
|
|
||||||
bool fromCache = false;
|
std::function<void(eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath)> visit;
|
||||||
|
|
||||||
std::map<std::string, std::string> results;
|
visit = [&](eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath)
|
||||||
|
|
||||||
std::function<void(Value *, std::string, bool, JSONObject *)> doExpr;
|
|
||||||
|
|
||||||
doExpr = [&](Value * v, std::string attrPath, bool toplevel, JSONObject * cache) {
|
|
||||||
debug("at attribute '%s'", attrPath);
|
|
||||||
|
|
||||||
try {
|
|
||||||
uint found = 0;
|
|
||||||
|
|
||||||
state->forceValue(*v);
|
|
||||||
|
|
||||||
if (v->type == tLambda && toplevel) {
|
|
||||||
Value * v2 = state->allocValue();
|
|
||||||
state->autoCallFunction(*state->allocBindings(1), *v, *v2);
|
|
||||||
v = v2;
|
|
||||||
state->forceValue(*v);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->isDerivation(*v)) {
|
|
||||||
|
|
||||||
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', ' ');
|
|
||||||
std::regex_search(description, descriptionMatch, regex);
|
|
||||||
|
|
||||||
if (!attrPathMatch.empty()
|
|
||||||
|| !nameMatch.empty()
|
|
||||||
|| !descriptionMatch.empty())
|
|
||||||
{
|
{
|
||||||
found++;
|
Activity act(*logger, lvlInfo, actUnknown,
|
||||||
}
|
fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
|
||||||
}
|
try {
|
||||||
|
auto recurse = [&]()
|
||||||
if (found == res.size()) {
|
{
|
||||||
if (json) {
|
for (const auto & attr : cursor.getAttrs()) {
|
||||||
|
auto cursor2 = cursor.getAttr(attr);
|
||||||
auto jsonElem = jsonOut->object(attrPath);
|
auto attrPath2(attrPath);
|
||||||
|
attrPath2.push_back(attr);
|
||||||
jsonElem.attr("pkgName", parsed.name);
|
visit(*cursor2, attrPath2);
|
||||||
jsonElem.attr("version", parsed.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (v->type == tAttrs) {
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (AssertionError & e) {
|
|
||||||
} catch (Error & e) {
|
|
||||||
if (!toplevel) {
|
|
||||||
e.addPrefix(fmt("While evaluating the attribute '%s':\n", attrPath));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json";
|
if (cursor.isDerivation()) {
|
||||||
|
size_t found = 0;
|
||||||
|
|
||||||
if (useCache && pathExists(jsonCacheFileName)) {
|
DrvName name(cursor.getAttr("name")->getString());
|
||||||
|
|
||||||
warn("using cached results; pass '-u' to update the cache");
|
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);
|
||||||
|
|
||||||
Value vRoot;
|
std::smatch attrPathMatch;
|
||||||
parseJSON(*state, readFile(jsonCacheFileName), vRoot);
|
std::smatch descriptionMatch;
|
||||||
|
std::smatch nameMatch;
|
||||||
|
|
||||||
fromCache = true;
|
for (auto & regex : regexes) {
|
||||||
|
std::regex_search(attrPath2, attrPathMatch, regex);
|
||||||
doExpr(&vRoot, "", true, nullptr);
|
std::regex_search(name.name, nameMatch, regex);
|
||||||
|
std::regex_search(description, descriptionMatch, regex);
|
||||||
|
if (!attrPathMatch.empty()
|
||||||
|
|| !nameMatch.empty()
|
||||||
|
|| !descriptionMatch.empty())
|
||||||
|
found++;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
if (found == res.size()) {
|
||||||
createDirs(dirOf(jsonCacheFileName));
|
results++;
|
||||||
|
if (json) {
|
||||||
|
auto jsonElem = jsonOut->object(attrPath2);
|
||||||
|
jsonElem.attr("pkgName", name.name);
|
||||||
|
jsonElem.attr("version", name.version);
|
||||||
|
jsonElem.attr("description", description);
|
||||||
|
} else {
|
||||||
|
auto name2 = hilite(name.name, nameMatch, "\e[0;2m")
|
||||||
|
+ std::string(name.fullName, name.name.length());
|
||||||
|
if (results > 1) logger->stdout("");
|
||||||
|
logger->stdout(
|
||||||
|
"* %s (%s)",
|
||||||
|
wrap("\e[0;1m", hilite(attrPath2, attrPathMatch, "\e[0;1m")),
|
||||||
|
wrap("\e[0;2m", hilite(name2, nameMatch, "\e[0;2m")));
|
||||||
|
if (description != "")
|
||||||
|
logger->stdout(
|
||||||
|
" %s", hilite(description, descriptionMatch, ANSI_NORMAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid());
|
else if (
|
||||||
|
attrPath.size() == 0
|
||||||
|
|| (attrPath[0] == "legacyPackages" && attrPath.size() <= 2)
|
||||||
|
|| (attrPath[0] == "packages" && attrPath.size() <= 2))
|
||||||
|
recurse();
|
||||||
|
|
||||||
std::ofstream jsonCacheFile;
|
} catch (EvalError & e) {
|
||||||
|
if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages"))
|
||||||
try {
|
|
||||||
// iostream considered harmful
|
|
||||||
jsonCacheFile.exceptions(std::ofstream::failbit);
|
|
||||||
jsonCacheFile.open(tmpFile);
|
|
||||||
|
|
||||||
auto cache = writeCache ? std::make_unique<JSONObject>(jsonCacheFile, false) : nullptr;
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
throw Error("NOT IMPLEMENTED");
|
|
||||||
//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;
|
throw;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (writeCache && rename(tmpFile.c_str(), jsonCacheFileName.c_str()) == -1)
|
for (auto & [cursor, prefix] : installable->getCursor(*state, true))
|
||||||
throw SysError("cannot rename '%s' to '%s'", tmpFile, jsonCacheFileName);
|
visit(*cursor, parseAttrPath(*state, prefix));
|
||||||
}
|
|
||||||
|
|
||||||
if (results.size() == 0)
|
if (!results)
|
||||||
throw Error("no results for the given search term(s)!");
|
throw Error("no results for the given search term(s)!");
|
||||||
|
|
||||||
RunPager pager;
|
|
||||||
for (auto el : results) std::cout << el.second << "\n";
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,43 +3,23 @@ source common.sh
|
||||||
clearStore
|
clearStore
|
||||||
clearCache
|
clearCache
|
||||||
|
|
||||||
exit 0 # FIXME
|
(( $(nix search -f search.nix '' hello | wc -l) > 0 ))
|
||||||
|
|
||||||
# No packages
|
|
||||||
(( $(NIX_PATH= nix search -u|wc -l) == 0 ))
|
|
||||||
|
|
||||||
# Haven't updated cache, still nothing
|
|
||||||
(( $(nix search -f search.nix hello|wc -l) == 0 ))
|
|
||||||
(( $(nix search -f search.nix |wc -l) == 0 ))
|
|
||||||
|
|
||||||
# Update cache, search should work
|
|
||||||
(( $(nix search -f search.nix -u hello|wc -l) > 0 ))
|
|
||||||
|
|
||||||
# Use cache
|
|
||||||
(( $(nix search -f search.nix foo|wc -l) > 0 ))
|
|
||||||
(( $(nix search foo|wc -l) > 0 ))
|
|
||||||
|
|
||||||
# Test --no-cache works
|
|
||||||
# No results from cache
|
|
||||||
(( $(nix search --no-cache foo |wc -l) == 0 ))
|
|
||||||
# Does find results from file pointed at
|
|
||||||
(( $(nix search -f search.nix --no-cache foo |wc -l) > 0 ))
|
|
||||||
|
|
||||||
# Check descriptions are searched
|
# Check descriptions are searched
|
||||||
(( $(nix search broken | wc -l) > 0 ))
|
(( $(nix search -f search.nix '' broken | wc -l) > 0 ))
|
||||||
|
|
||||||
# Check search that matches nothing
|
# Check search that matches nothing
|
||||||
(( $(nix search nosuchpackageexists | wc -l) == 0 ))
|
(( $(nix search -f search.nix '' nosuchpackageexists | wc -l) == 0 ))
|
||||||
|
|
||||||
# Search for multiple arguments
|
# Search for multiple arguments
|
||||||
(( $(nix search hello empty | wc -l) == 3 ))
|
(( $(nix search -f search.nix '' hello empty | wc -l) == 2 ))
|
||||||
|
|
||||||
# Multiple arguments will not exist
|
# Multiple arguments will not exist
|
||||||
(( $(nix search hello broken | wc -l) == 0 ))
|
(( $(nix search -f search.nix '' hello broken | wc -l) == 0 ))
|
||||||
|
|
||||||
## Search expressions
|
## Search expressions
|
||||||
|
|
||||||
# Check that empty search string matches all
|
# Check that empty search string matches all
|
||||||
nix search|grep -q foo
|
nix search -f search.nix '' |grep -q foo
|
||||||
nix search|grep -q bar
|
nix search -f search.nix '' |grep -q bar
|
||||||
nix search|grep -q hello
|
nix search -f search.nix '' |grep -q hello
|
||||||
|
|
Loading…
Reference in a new issue