diff --git a/perl/lib/Nix/CopyClosure.pm b/perl/lib/Nix/CopyClosure.pm index 902ee1a1bc9f..f8e3ce8a8d78 100644 --- a/perl/lib/Nix/CopyClosure.pm +++ b/perl/lib/Nix/CopyClosure.pm @@ -15,7 +15,7 @@ sub copyToOpen { $useSubstitutes = 0 if $dryRun || !defined $useSubstitutes; # Get the closure of this path. - my @closure = reverse(topoSortPaths(computeFSClosure(0, $includeOutputs, + my @closure = reverse(topoSortPaths(computeFSClosure($includeOutputs, map { followLinksToStorePath $_ } @{$storePaths}))); # Send the "query valid paths" command with the "lock" option diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs index 41ecbbeb4794..52010febb2c6 100644 --- a/perl/lib/Nix/Store.xs +++ b/perl/lib/Nix/Store.xs @@ -143,12 +143,12 @@ SV * queryPathFromHashPart(char * hashPart) } -SV * computeFSClosure(int flipDirection, int includeOutputs, ...) +SV * computeFSClosure(int includeOutputs, ...) PPCODE: try { StorePathSet paths; for (int n = 2; n < items; ++n) - store()->computeFSClosure(store()->parseStorePath(SvPV_nolen(ST(n))), paths, flipDirection, includeOutputs); + store()->computeFSClosure(store()->parseStorePath(SvPV_nolen(ST(n))), paths, includeOutputs); for (auto & i : paths) XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(i).c_str(), 0))); } catch (Error & e) { diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 0038ec80257c..c3ab5cf8697e 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -704,7 +704,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) try { StorePathSet closure; computeFSClosure(*path, closure, - /* flipDirection */ false, gcKeepOutputs, gcKeepDerivations); + gcKeepOutputs, gcKeepDerivations); for (auto & p : closure) alive.insert(p); } catch (InvalidPath &) { } diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 2012584e06aa..772deb723b8b 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -345,11 +345,11 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor { unsupported("getFSAccessor"); } void computeFSClosure(const StorePathSet & paths, - StorePathSet & out, bool flipDirection = false, + StorePathSet & out, bool includeOutputs = false, bool includeDerivers = false) override { - if (flipDirection || includeDerivers) { - Store::computeFSClosure(paths, out, flipDirection, includeOutputs, includeDerivers); + if (includeDerivers) { + Store::computeFSClosure(paths, out, includeOutputs, includeDerivers); return; } diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index a61c09e99fa9..f75eb7aa8e27 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -13,56 +13,18 @@ namespace nix { -void Store::computeFSClosure(const StorePathSet & startPaths, - StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers) -{ - std::function(const StorePath & path, std::future> &)> queryDeps; - if (flipDirection) { - // FIXME It is not good to do partial downcasts "deep" in the - // code, we should only do this in "boundary" code like the CLI - // or protocol handlers. - auto & referrersStore = require(*this); - queryDeps = [&](const StorePath& path, - std::future> & fut) { - StorePathSet res; - StorePathSet referrers; - referrersStore.queryReferrers(path, referrers); - for (auto& ref : referrers) - if (ref != path) - res.insert(ref); - - if (includeOutputs) - for (auto& i : referrersStore.queryValidDerivers(path)) - res.insert(i); - - if (includeDerivers && path.isDerivation()) - for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path)) - if (maybeOutPath && isValidPath(*maybeOutPath)) - res.insert(*maybeOutPath); - return res; - }; - } else - queryDeps = [&](const StorePath& path, - std::future> & fut) { - StorePathSet res; - auto info = fut.get(); - for (auto& ref : info->references) - if (ref != path) - res.insert(ref); - - if (includeOutputs && path.isDerivation()) - for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path)) - if (maybeOutPath && isValidPath(*maybeOutPath)) - res.insert(*maybeOutPath); - - if (includeDerivers && info->deriver && isValidPath(*info->deriver)) - res.insert(*info->deriver); - return res; - }; +typedef std::set QueryDeps(const StorePath & path, std::future> &); +static void computeClosure( + Store & store, + std::function queryDeps, + const StorePathSet & startPaths, + StorePathSet & paths_, + bool includeOutputs, bool includeDerivers) +{ computeClosure( startPaths, paths_, - [&](const StorePath& path, + [&](const StorePath & path, std::function>&)> processEdges) { std::promise> promise; @@ -75,17 +37,75 @@ void Store::computeFSClosure(const StorePathSet & startPaths, promise.set_exception(std::current_exception()); } }; - queryPathInfo(path, getDependencies); + store.queryPathInfo(path, getDependencies); processEdges(promise); }); } +void ReferrersStore::computeFSCoClosure(const StorePathSet & startPaths, + StorePathSet & paths_, bool includeOutputs, bool includeDerivers) +{ + std::function queryDeps; + queryDeps = [&](const StorePath & path, + std::future> & fut) { + StorePathSet res; + StorePathSet referrers; + queryReferrers(path, referrers); + for (auto& ref : referrers) + if (ref != path) + res.insert(ref); + + if (includeOutputs) + for (auto& i : queryValidDerivers(path)) + res.insert(i); + + if (includeDerivers && path.isDerivation()) + for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path)) + if (maybeOutPath && isValidPath(*maybeOutPath)) + res.insert(*maybeOutPath); + return res; + }; + computeClosure(*this, queryDeps, startPaths, paths_, includeOutputs, includeDerivers); +} + +void Store::computeFSClosure(const StorePathSet & startPaths, + StorePathSet & paths_, bool includeOutputs, bool includeDerivers) +{ + std::function queryDeps; + queryDeps = [&](const StorePath& path, + std::future> & fut) { + StorePathSet res; + auto info = fut.get(); + for (auto& ref : info->references) + if (ref != path) + res.insert(ref); + + if (includeOutputs && path.isDerivation()) + for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path)) + if (maybeOutPath && isValidPath(*maybeOutPath)) + res.insert(*maybeOutPath); + + if (includeDerivers && info->deriver && isValidPath(*info->deriver)) + res.insert(*info->deriver); + return res; + }; + computeClosure(*this, queryDeps, startPaths, paths_, includeOutputs, includeDerivers); +} + +void ReferrersStore::computeFSCoClosure(const StorePath & startPath, + StorePathSet & paths_, bool includeOutputs, bool includeDerivers) +{ + StorePathSet paths; + paths.insert(startPath); + computeFSCoClosure(paths, paths_, includeOutputs, includeDerivers); +} + void Store::computeFSClosure(const StorePath & startPath, - StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers) + StorePathSet & paths_, bool includeOutputs, bool includeDerivers) { StorePathSet paths; paths.insert(startPath); - computeFSClosure(paths, paths_, flipDirection, includeOutputs, includeDerivers); + computeFSClosure(paths, paths_, includeOutputs, includeDerivers); } diff --git a/src/libstore/referrers-store.hh b/src/libstore/referrers-store.hh index a62f24e3418b..b1a099c9e878 100644 --- a/src/libstore/referrers-store.hh +++ b/src/libstore/referrers-store.hh @@ -33,6 +33,24 @@ struct ReferrersStore : virtual VisibleStore * of `path`. */ virtual void queryReferrers(const StorePath & path, StorePathSet & referrers) = 0; + + /** + * @param [out] out Place in here the set of all store paths in the + * file system co-closure of `storePath`; that is, all paths than + * directly or indirectly refer from it. `out` is not cleared. + * + * Whereas `Store::computeFSClosure` uses the `references` relation, + * this function uses the dual of it which is the `referrers` + * relation. + */ + virtual void computeFSCoClosure(const StorePathSet & paths, + StorePathSet & out, + bool includeOutputs = false, bool includeDerivers = false); + + void computeFSCoClosure(const StorePath & path, + StorePathSet & out, + bool includeOutputs = false, bool includeDerivers = false); + }; } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 0e1fc2b238dd..6e063a889eee 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -700,11 +700,11 @@ public: * returned. */ virtual void computeFSClosure(const StorePathSet & paths, - StorePathSet & out, bool flipDirection = false, + StorePathSet & out, bool includeOutputs = false, bool includeDerivers = false); void computeFSClosure(const StorePath & path, - StorePathSet & out, bool flipDirection = false, + StorePathSet & out, bool includeOutputs = false, bool includeDerivers = false); /** diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 3ec7e5e126cb..6eceb8588f13 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -346,7 +346,7 @@ static void opQuery(Strings opFlags, Strings opArgs) for (auto & i : opArgs) { auto ps = maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise); for (auto & j : ps) { - if (query == qRequisites) store->computeFSClosure(j, paths, false, includeOutputs); + if (query == qRequisites) store->computeFSClosure(j, paths, includeOutputs); else if (query == qReferences) { for (auto & p : store->queryPathInfo(j)->references) paths.insert(p); @@ -358,7 +358,10 @@ static void opQuery(Strings opFlags, Strings opArgs) for (auto & i : tmp) paths.insert(i); } - else if (query == qReferrersClosure) store->computeFSClosure(j, paths, true); + else if (query == qReferrersClosure) { + auto & referrersStore = require(*store); + referrersStore.computeFSCoClosure(j, paths); + } } } auto sorted = store->topoSortPaths(paths); @@ -433,16 +436,17 @@ static void opQuery(Strings opFlags, Strings opArgs) } case qRoots: { + auto & referrersStore = require(*store); + auto & gcStore = require(*store); StorePathSet args; for (auto & i : opArgs) for (auto & p : maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise)) args.insert(p); StorePathSet referrers; - store->computeFSClosure( - args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations); + referrersStore.computeFSCoClosure( + args, referrers, settings.gcKeepOutputs, settings.gcKeepDerivations); - auto & gcStore = require(*store); Roots roots = gcStore.findRoots(false); for (auto & [target, links] : roots) if (referrers.find(target) != referrers.end())