diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build-remote/local.mk | 2 | ||||
-rw-r--r-- | src/libexpr/nix-expr.pc.in | 2 | ||||
-rw-r--r-- | src/libexpr/primops.cc | 17 | ||||
-rw-r--r-- | src/libexpr/primops.hh | 8 | ||||
-rw-r--r-- | src/libmain/nix-main.pc.in | 2 | ||||
-rw-r--r-- | src/libstore/build.cc | 31 | ||||
-rw-r--r-- | src/libstore/builtins/buildenv.cc | 87 | ||||
-rw-r--r-- | src/libstore/download.cc | 10 | ||||
-rw-r--r-- | src/libstore/globals.cc | 2 | ||||
-rw-r--r-- | src/libstore/globals.hh | 8 | ||||
-rw-r--r-- | src/libstore/local-store.cc | 12 | ||||
-rw-r--r-- | src/libstore/local-store.hh | 4 | ||||
-rw-r--r-- | src/libstore/nar-info-disk-cache.cc | 13 | ||||
-rw-r--r-- | src/libstore/nix-store.pc.in | 4 | ||||
-rwxr-xr-x | src/nix-build/nix-build.cc | 2 | ||||
-rw-r--r-- | src/nix-channel/local.mk | 2 | ||||
-rw-r--r-- | src/nix-copy-closure/local.mk | 2 | ||||
-rw-r--r-- | src/nix-daemon/nix-daemon.cc | 2 | ||||
-rw-r--r-- | src/nix/edit.cc | 4 | ||||
-rw-r--r-- | src/nix/repl.cc | 2 | ||||
-rw-r--r-- | src/nix/search.cc | 56 |
21 files changed, 173 insertions, 99 deletions
diff --git a/src/build-remote/local.mk b/src/build-remote/local.mk index 64368a43ff73..50b0409d1886 100644 --- a/src/build-remote/local.mk +++ b/src/build-remote/local.mk @@ -4,6 +4,6 @@ build-remote_DIR := $(d) build-remote_INSTALL_DIR := $(libexecdir)/nix -build-remote_LIBS = libmain libutil libformat libstore +build-remote_LIBS = libmain libformat libstore libutil build-remote_SOURCES := $(d)/build-remote.cc diff --git a/src/libexpr/nix-expr.pc.in b/src/libexpr/nix-expr.pc.in index 21b6c38dd133..79f3e2f4506e 100644 --- a/src/libexpr/nix-expr.pc.in +++ b/src/libexpr/nix-expr.pc.in @@ -7,4 +7,4 @@ Description: Nix Package Manager Version: @PACKAGE_VERSION@ Requires: nix-store bdw-gc Libs: -L${libdir} -lnixexpr -Cflags: -I${includedir}/nix +Cflags: -I${includedir}/nix -std=c++14 diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index c88f677da085..57dc7bd1279d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -155,7 +155,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v); /* Load a ValueInitializer from a DSO and return whatever it initializes */ -static void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v) +void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; Path path = state.coerceToPath(pos, *args[0], context); @@ -193,7 +193,7 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args /* Execute a program and parse its output */ -static void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) +void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceList(*args[0], pos); auto elems = args[0]->listElems(); @@ -271,7 +271,18 @@ static void prim_isNull(EvalState & state, const Pos & pos, Value * * args, Valu static void prim_isFunction(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0]); - mkBool(v, args[0]->type == tLambda); + bool res; + switch (args[0]->type) { + case tLambda: + case tPrimOp: + case tPrimOpApp: + res = true; + break; + default: + res = false; + break; + } + mkBool(v, res); } diff --git a/src/libexpr/primops.hh b/src/libexpr/primops.hh index 31bf3f84f6c7..c790b30f6d0b 100644 --- a/src/libexpr/primops.hh +++ b/src/libexpr/primops.hh @@ -15,4 +15,12 @@ struct RegisterPrimOp RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun); }; +/* These primops are disabled without enableNativeCode, but plugins + may wish to use them in limited contexts without globally enabling + them. */ +/* Load a ValueInitializer from a DSO and return whatever it initializes */ +void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v); +/* Execute a program and parse its output */ +void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v); + } diff --git a/src/libmain/nix-main.pc.in b/src/libmain/nix-main.pc.in index de1bdf706f72..38bc85c484eb 100644 --- a/src/libmain/nix-main.pc.in +++ b/src/libmain/nix-main.pc.in @@ -6,4 +6,4 @@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnixmain -Cflags: -I${includedir}/nix +Cflags: -I${includedir}/nix -std=c++14 diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 73139d6d551a..416c775a35d2 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -964,6 +964,8 @@ private: } void done(BuildResult::Status status, const string & msg = ""); + + PathSet exportReferences(PathSet storePaths); }; @@ -1730,22 +1732,23 @@ int childEntry(void * arg) } -PathSet exportReferences(Store & store, PathSet storePaths) +PathSet DerivationGoal::exportReferences(PathSet storePaths) { PathSet paths; for (auto storePath : storePaths) { /* Check that the store path is valid. */ - if (!store.isInStore(storePath)) + if (!worker.store.isInStore(storePath)) throw BuildError(format("'exportReferencesGraph' contains a non-store path '%1%'") % storePath); - storePath = store.toStorePath(storePath); - if (!store.isValidPath(storePath)) - throw BuildError(format("'exportReferencesGraph' contains an invalid path '%1%'") - % storePath); - store.computeFSClosure(storePath, paths); + storePath = worker.store.toStorePath(storePath); + + if (!inputPaths.count(storePath)) + throw BuildError("cannot export references of path '%s' because it is not in the input closure of the derivation", storePath); + + worker.store.computeFSClosure(storePath, paths); } /* If there are derivations in the graph, then include their @@ -1756,9 +1759,9 @@ PathSet exportReferences(Store & store, PathSet storePaths) for (auto & j : paths2) { if (isDerivation(j)) { - Derivation drv = store.derivationFromPath(j); + Derivation drv = worker.store.derivationFromPath(j); for (auto & k : drv.outputs) - store.computeFSClosure(k.second.path, paths); + worker.store.computeFSClosure(k.second.path, paths); } } @@ -1882,7 +1885,7 @@ void DerivationGoal::startBuilder() /* Write closure info to <fileName>. */ writeFile(tmpDir + "/" + fileName, worker.store.makeValidityRegistration( - exportReferences(worker.store, {storePath}), false, false)); + exportReferences({storePath}), false, false)); } } @@ -2384,7 +2387,7 @@ void DerivationGoal::writeStructuredAttrs() for (auto & p : *i) storePaths.insert(p.get<std::string>()); worker.store.pathInfoToJSON(jsonRoot, - exportReferences(worker.store, storePaths), false, true); + exportReferences(storePaths), false, true); } json[i.key()] = nlohmann::json::parse(str.str()); // urgh } @@ -2696,8 +2699,8 @@ void DerivationGoal::runChild() } else { if (errno != EINVAL) throw SysError("mounting /dev/pts"); - doBind("/dev/pts", "/dev/pts"); - doBind("/dev/ptmx", "/dev/ptmx"); + doBind("/dev/pts", chrootRootDir + "/dev/pts"); + doBind("/dev/ptmx", chrootRootDir + "/dev/ptmx"); } } @@ -3687,7 +3690,7 @@ void SubstitutionGoal::tryNext() only after we've downloaded the path. */ if (worker.store.requireSigs && !sub->isTrusted - && !info->checkSignatures(worker.store, worker.store.publicKeys)) + && !info->checkSignatures(worker.store, worker.store.getPublicKeys())) { printError("warning: substituter '%s' does not have a valid signature for path '%s'", sub->getUri(), storePath); diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 938d02c35a02..74e706664694 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -9,14 +9,6 @@ namespace nix { typedef std::map<Path,int> Priorities; -static bool isDirectory(const Path & path) -{ - struct stat st; - if (stat(path.c_str(), &st) == -1) - throw SysError(format("getting status of '%1%'") % path); - return S_ISDIR(st.st_mode); -} - // FIXME: change into local variables. static Priorities priorities; @@ -26,14 +18,37 @@ static unsigned long symlinks; /* For each activated package, create symlinks */ static void createLinks(const Path & srcDir, const Path & dstDir, int priority) { - auto srcFiles = readDirectory(srcDir); + DirEntries srcFiles; + + try { + srcFiles = readDirectory(srcDir); + } catch (SysError & e) { + if (e.errNo == ENOTDIR) { + printError("warning: not including '%s' in the user environment because it's not a directory", srcDir); + return; + } + throw; + } + for (const auto & ent : srcFiles) { if (ent.name[0] == '.') /* not matched by glob */ continue; - const auto & srcFile = srcDir + "/" + ent.name; + auto srcFile = srcDir + "/" + ent.name; auto dstFile = dstDir + "/" + ent.name; + struct stat srcSt; + try { + if (stat(srcFile.c_str(), &srcSt) == -1) + throw SysError("getting status of '%1%'", srcFile); + } catch (SysError & e) { + if (e.errNo == ENOENT || e.errNo == ENOTDIR) { + printError("warning: skipping dangling symlink '%s'", dstFile); + continue; + } + throw; + } + /* The files below are special-cased to that they don't show up * in user profiles, either because they are useless, or * because they would cauase pointless collisions (e.g., each @@ -44,9 +59,10 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority) hasSuffix(srcFile, "/nix-support") || hasSuffix(srcFile, "/perllocal.pod") || hasSuffix(srcFile, "/info/dir") || - hasSuffix(srcFile, "/log")) { + hasSuffix(srcFile, "/log")) continue; - } else if (isDirectory(srcFile)) { + + else if (S_ISDIR(srcSt.st_mode)) { struct stat dstSt; auto res = lstat(dstFile.c_str(), &dstSt); if (res == 0) { @@ -54,10 +70,9 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority) createLinks(srcFile, dstFile, priority); continue; } else if (S_ISLNK(dstSt.st_mode)) { - auto target = readLink(dstFile); - if (!isDirectory(target)) - throw Error(format("collision between '%1%' and non-directory '%2%'") - % srcFile % target); + auto target = canonPath(dstFile, true); + if (!S_ISDIR(lstat(target).st_mode)) + throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target); if (unlink(dstFile.c_str()) == -1) throw SysError(format("unlinking '%1%'") % dstFile); if (mkdir(dstFile.c_str(), 0755) == -1) @@ -68,28 +83,31 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority) } } else if (errno != ENOENT) throw SysError(format("getting status of '%1%'") % dstFile); - } else { + } + + else { struct stat dstSt; auto res = lstat(dstFile.c_str(), &dstSt); if (res == 0) { if (S_ISLNK(dstSt.st_mode)) { - auto target = readLink(dstFile); auto prevPriority = priorities[dstFile]; if (prevPriority == priority) - throw Error(format( + throw Error( "packages '%1%' and '%2%' have the same priority %3%; " "use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' " "to change the priority of one of the conflicting packages" - " (0 being the highest priority)" - ) % srcFile % target % priority); + " (0 being the highest priority)", + srcFile, readLink(dstFile), priority); if (prevPriority < priority) continue; if (unlink(dstFile.c_str()) == -1) throw SysError(format("unlinking '%1%'") % dstFile); - } + } else if (S_ISDIR(dstSt.st_mode)) + throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile); } else if (errno != ENOENT) throw SysError(format("getting status of '%1%'") % dstFile); } + createSymlink(srcFile, dstFile); priorities[dstFile] = priority; symlinks++; @@ -105,24 +123,18 @@ static Path out; static void addPkg(const Path & pkgDir, int priority) { - if (done.find(pkgDir) != done.end()) - return; + if (done.count(pkgDir)) return; done.insert(pkgDir); createLinks(pkgDir, out, priority); - auto propagatedFN = pkgDir + "/nix-support/propagated-user-env-packages"; - std::string propagated; - { - AutoCloseFD fd = open(propagatedFN.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) { - if (errno == ENOENT) - return; - throw SysError(format("opening '%1%'") % propagatedFN); - } - propagated = readFile(fd.get()); + + try { + for (const auto & p : tokenizeString<std::vector<string>>( + readFile(pkgDir + "/nix-support/propagated-user-env-packages"), " \n")) + if (!done.count(p)) + postponed.insert(p); + } catch (SysError & e) { + if (e.errNo != ENOENT && e.errNo != ENOTDIR) throw; } - for (const auto & p : tokenizeString<std::vector<string>>(propagated, " \n")) - if (done.find(p) == done.end()) - postponed.insert(p); } struct Package { @@ -190,4 +202,3 @@ void builtinBuildenv(const BasicDerivation & drv) } } - diff --git a/src/libstore/download.cc b/src/libstore/download.cc index 4d7f5690192d..18f9094f82e0 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -173,7 +173,11 @@ struct CurlDownloader : public Downloader int progressCallback(double dltotal, double dlnow) { - act.progress(dlnow, dltotal); + try { + act.progress(dlnow, dltotal); + } catch (nix::Interrupted &) { + assert(_isInterrupted); + } return _isInterrupted; } @@ -730,8 +734,8 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa Hash gotHash = unpack ? hashPath(expectedHash.type, store->toRealPath(storePath)).first : hashFile(expectedHash.type, store->toRealPath(storePath)); - throw nix::Error("hash mismatch in file downloaded from '%s': expected hash '%s', got '%s'", - url, expectedHash.to_string(), gotHash.to_string()); + throw nix::Error("hash mismatch in file downloaded from '%s': got hash '%s' instead of the expected hash '%s'", + url, gotHash.to_string(), expectedHash.to_string()); } return store->toRealPath(storePath); diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index f46e8326235f..544566e0b573 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -159,7 +159,7 @@ void initPlugins() void *handle = dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) - throw Error("could not dynamically open plugin file '%s%': %s%", file, dlerror()); + throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); } } /* We handle settings registrations here, since plugins can add settings */ diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 117404ec14c8..7430bbedbe44 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -313,6 +313,14 @@ public: Setting<Strings> trustedUsers{this, {"root"}, "trusted-users", "Which users or groups are trusted to ask the daemon to do unsafe things."}; + Setting<unsigned int> ttlNegativeNarInfoCache{this, 3600, "narinfo-cache-negative-ttl", + "The TTL in seconds for negative lookups in the disk cache i.e binary cache lookups that " + "return an invalid path result"}; + + Setting<unsigned int> ttlPositiveNarInfoCache{this, 30 * 24 * 3600, "narinfo-cache-positive-ttl", + "The TTL in seconds for positive lookups in the disk cache i.e binary cache lookups that " + "return a valid path result."}; + /* ?Who we trust to use the daemon in safe ways */ Setting<Strings> allowedUsers{this, {"*"}, "allowed-users", "Which users or groups are allowed to connect to the daemon."}; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index acc0002acee1..b63584f28a30 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -53,7 +53,6 @@ LocalStore::LocalStore(const Params & params) , trashDir(realStoreDir + "/trash") , tempRootsDir(stateDir + "/temproots") , fnTempRoots(fmt("%s/%d", tempRootsDir, getpid())) - , publicKeys(getDefaultPublicKeys()) { auto state(_state.lock()); @@ -964,12 +963,21 @@ void LocalStore::invalidatePath(State & state, const Path & path) } +const PublicKeys & LocalStore::getPublicKeys() +{ + auto state(_state.lock()); + if (!state->publicKeys) + state->publicKeys = std::make_unique<PublicKeys>(getDefaultPublicKeys()); + return *state->publicKeys; +} + + void LocalStore::addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) { assert(info.narHash); - if (requireSigs && checkSigs && !info.checkSignatures(*this, publicKeys)) + if (requireSigs && checkSigs && !info.checkSignatures(*this, getPublicKeys())) throw Error("cannot add path '%s' because it lacks a valid signature", info.path); addTempRoot(info.path); diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 0d6c176595c8..1209a06356f7 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -77,6 +77,8 @@ private: minFree but not much below availAfterGC, then there is no point in starting a new GC. */ uint64_t availAfterGC = std::numeric_limits<uint64_t>::max(); + + std::unique_ptr<PublicKeys> publicKeys; }; Sync<State, std::recursive_mutex> _state; @@ -100,7 +102,7 @@ private: settings.requireSigs, "require-sigs", "whether store paths should have a trusted signature on import"}; - PublicKeys publicKeys; + const PublicKeys & getPublicKeys(); public: diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 3c52303f0ea4..35403e5df56f 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -1,6 +1,7 @@ #include "nar-info-disk-cache.hh" #include "sync.hh" #include "sqlite.hh" +#include "globals.hh" #include <sqlite3.h> @@ -47,10 +48,6 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache { public: - /* How long negative and positive lookups are valid. */ - const int ttlNegative = 3600; - const int ttlPositive = 30 * 24 * 3600; - /* How often to purge expired entries from the cache. */ const int purgeInterval = 24 * 3600; @@ -116,8 +113,8 @@ public: SQLiteStmt(state->db, "delete from NARs where ((present = 0 and timestamp < ?) or (present = 1 and timestamp < ?))") .use() - (now - ttlNegative) - (now - ttlPositive) + (now - settings.ttlNegativeNarInfoCache) + (now - settings.ttlPositiveNarInfoCache) .exec(); debug("deleted %d entries from the NAR info disk cache", sqlite3_changes(state->db)); @@ -186,8 +183,8 @@ public: auto queryNAR(state->queryNAR.use() (cache.id) (hashPart) - (now - ttlNegative) - (now - ttlPositive)); + (now - settings.ttlNegativeNarInfoCache) + (now - settings.ttlPositiveNarInfoCache)); if (!queryNAR.next()) return {oUnknown, 0}; diff --git a/src/libstore/nix-store.pc.in b/src/libstore/nix-store.pc.in index 3f1a2d83d2f2..5cf22faadcbe 100644 --- a/src/libstore/nix-store.pc.in +++ b/src/libstore/nix-store.pc.in @@ -5,5 +5,5 @@ includedir=@includedir@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lnixstore -lnixutil -lnixformat -Cflags: -I${includedir}/nix +Libs: -L${libdir} -lnixstore -lnixutil +Cflags: -I${includedir}/nix -std=c++14 diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index cf628519c6e7..a63b3e07ae77 100755 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -212,7 +212,7 @@ void mainWrapped(int argc, char * * argv) // read the shebang to understand which packages to read from. Since // this is handled via nix-shell -p, we wrap our ruby script execution // in ruby -e 'load' which ignores the shebangs. - envCommand = (format("exec %1% %2% -e 'load(\"%3%\") -- %4%") % execArgs % interpreter % script % joined.str()).str(); + envCommand = (format("exec %1% %2% -e 'load(\"%3%\")' -- %4%") % execArgs % interpreter % script % joined.str()).str(); } else { envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % script % joined.str()).str(); } diff --git a/src/nix-channel/local.mk b/src/nix-channel/local.mk index 49fc105c6f79..c14e8c359ca0 100644 --- a/src/nix-channel/local.mk +++ b/src/nix-channel/local.mk @@ -2,6 +2,6 @@ programs += nix-channel nix-channel_DIR := $(d) -nix-channel_LIBS = libmain libutil libformat libstore +nix-channel_LIBS = libmain libformat libstore libutil nix-channel_SOURCES := $(d)/nix-channel.cc diff --git a/src/nix-copy-closure/local.mk b/src/nix-copy-closure/local.mk index 42bb34dd8201..5018ab975b44 100644 --- a/src/nix-copy-closure/local.mk +++ b/src/nix-copy-closure/local.mk @@ -2,6 +2,6 @@ programs += nix-copy-closure nix-copy-closure_DIR := $(d) -nix-copy-closure_LIBS = libmain libutil libformat libstore +nix-copy-closure_LIBS = libmain libformat libstore libutil nix-copy-closure_SOURCES := $(d)/nix-copy-closure.cc diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc index f28a52b73ff9..35603af7082a 100644 --- a/src/nix-daemon/nix-daemon.cc +++ b/src/nix-daemon/nix-daemon.cc @@ -1035,7 +1035,7 @@ static void daemonLoop(char * * argv) }, options); } catch (Interrupted & e) { - throw; + return; } catch (Error & e) { printError(format("error processing connection: %1%") % e.msg()); } diff --git a/src/nix/edit.cc b/src/nix/edit.cc index 7eaa86e2f914..c9671f76d0fa 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -61,7 +61,7 @@ struct CmdEdit : InstallableCommand auto editor = getEnv("EDITOR", "cat"); - Strings args{editor}; + auto args = tokenizeString<Strings>(editor); if (editor.find("emacs") != std::string::npos || editor.find("nano") != std::string::npos || @@ -72,7 +72,7 @@ struct CmdEdit : InstallableCommand stopProgressBar(); - execvp(editor.c_str(), stringsToCharPtrs(args).data()); + execvp(args.front().c_str(), stringsToCharPtrs(args).data()); throw SysError("cannot run editor '%s'", editor); } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 9216209173d9..f84774a53367 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -189,6 +189,7 @@ bool NixRepl::getLine(string & input, const std::string &prompt) if (!s) { switch (auto type = linenoiseKeyType()) { case 1: // ctrl-C + input = ""; return true; case 2: // ctrl-D return false; @@ -197,6 +198,7 @@ bool NixRepl::getLine(string & input, const std::string &prompt) } } input += s; + input += '\n'; return true; } diff --git a/src/nix/search.cc b/src/nix/search.cc index 5ccf1b7cf529..539676698086 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -25,14 +25,14 @@ std::string hilite(const std::string & s, const std::smatch & m) struct CmdSearch : SourceExprCommand, MixJSON { - std::string re; + std::vector<std::string> res; bool writeCache = true; bool useCache = true; CmdSearch() { - expectArg("regex", &re, true); + expectArgs("regex", &res); mkFlag() .longName("update-cache") @@ -68,9 +68,13 @@ struct CmdSearch : SourceExprCommand, MixJSON "nix search blender" }, Example{ - "To search for Firefox and Chromium:", + "To search for Firefox or Chromium:", "nix search 'firefox|chromium'" }, + Example{ + "To search for git and frontend or gui:", + "nix search git 'frontend|gui'" + }, }; } @@ -81,9 +85,16 @@ 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 (re.empty()) re = "^"; + if (res.empty()) { + res.push_back("^"); + } - std::regex regex(re, std::regex::extended | std::regex::icase); + std::vector<std::regex> regexes; + regexes.reserve(res.size()); + + for (auto &re : res) { + regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase)); + } auto state = getEvalState(); @@ -102,6 +113,7 @@ struct CmdSearch : SourceExprCommand, MixJSON debug("at attribute '%s'", attrPath); try { + uint found = 0; state->forceValue(*v); @@ -115,25 +127,33 @@ struct CmdSearch : SourceExprCommand, MixJSON 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()); - std::smatch attrPathMatch; - std::regex_search(attrPath, attrPathMatch, regex); + for (auto ®ex : regexes) { + std::regex_search(attrPath, attrPathMatch, regex); - auto name = parsed.name; - std::smatch nameMatch; - std::regex_search(name, nameMatch, regex); + name = parsed.name; + std::regex_search(name, nameMatch, regex); - std::string description = drv.queryMetaString("description"); - std::replace(description.begin(), description.end(), '\n', ' '); - std::smatch descriptionMatch; - std::regex_search(description, descriptionMatch, 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++; + } + } - if (!attrPathMatch.empty() - || !nameMatch.empty() - || !descriptionMatch.empty()) - { + if (found == res.size()) { if (json) { auto jsonElem = jsonOut->object(attrPath); |