libstore: asyncify Store::computeFSClosure
Change-Id: I79d3e9c60886a9eb58051a28cdda25dadcead9f1
This commit is contained in:
@@ -459,7 +459,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
|
||||
auto outputs = store->queryPartialDerivationOutputMap(inputDrv, &*evalStore);
|
||||
for (auto & i : inputNode.value) {
|
||||
auto o = outputs.at(i);
|
||||
store->computeFSClosure(*o, inputs);
|
||||
aio.blockOn(store->computeFSClosure(*o, inputs));
|
||||
}
|
||||
for (const auto & [outputName, childNode] : inputNode.childMap)
|
||||
accumInputClosure(*outputs.at(outputName), childNode);
|
||||
|
||||
+13
-6
@@ -358,7 +358,9 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
|
||||
for (auto & i : opArgs) {
|
||||
auto ps = aio.blockOn(maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise));
|
||||
for (auto & j : ps) {
|
||||
if (query == qRequisites) store->computeFSClosure(j, paths, false, includeOutputs);
|
||||
if (query == qRequisites) {
|
||||
aio.blockOn(store->computeFSClosure(j, paths, false, includeOutputs));
|
||||
}
|
||||
else if (query == qReferences) {
|
||||
for (auto & p : store->queryPathInfo(j)->references)
|
||||
paths.insert(p);
|
||||
@@ -369,7 +371,8 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
|
||||
for (auto & i : tmp)
|
||||
paths.insert(i);
|
||||
}
|
||||
else if (query == qReferrersClosure) store->computeFSClosure(j, paths, true);
|
||||
else if (query == qReferrersClosure)
|
||||
aio.blockOn(store->computeFSClosure(j, paths, true));
|
||||
}
|
||||
}
|
||||
auto sorted = store->topoSortPaths(paths);
|
||||
@@ -465,8 +468,8 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
|
||||
args.insert(p);
|
||||
|
||||
StorePathSet referrers;
|
||||
store->computeFSClosure(
|
||||
args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations);
|
||||
aio.blockOn(store->computeFSClosure(
|
||||
args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations));
|
||||
|
||||
auto & gcStore = require<GcStore>(*store);
|
||||
Roots roots = aio.blockOn(gcStore.findRoots(false));
|
||||
@@ -974,8 +977,12 @@ static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
|
||||
case ServeProto::Command::QueryClosure: {
|
||||
bool includeOutputs = readInt(in);
|
||||
StorePathSet closure;
|
||||
store->computeFSClosure(ServeProto::Serialise<StorePathSet>::read(*store, rconn),
|
||||
closure, false, includeOutputs);
|
||||
aio.blockOn(store->computeFSClosure(
|
||||
ServeProto::Serialise<StorePathSet>::read(*store, rconn),
|
||||
closure,
|
||||
false,
|
||||
includeOutputs
|
||||
));
|
||||
out << ServeProto::write(*store, wconn, closure);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
|
||||
auto rootFromThis = root.outPaths();
|
||||
pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
|
||||
}
|
||||
store->computeFSClosure(pathsRoots, pathsClosure);
|
||||
aio().blockOn(store->computeFSClosure(pathsRoots, pathsClosure));
|
||||
for (auto & path : pathsClosure)
|
||||
paths.emplace_back(BuiltPath::Opaque{path});
|
||||
}
|
||||
|
||||
+1
-1
@@ -295,7 +295,7 @@ EvalPaths::EvalPaths(
|
||||
if (store->isInStore(path)) {
|
||||
try {
|
||||
StorePathSet closure;
|
||||
store->computeFSClosure(store->toStorePath(path).first, closure);
|
||||
aio.blockOn(store->computeFSClosure(store->toStorePath(path).first, closure));
|
||||
for (auto & path : closure)
|
||||
allowPath(path);
|
||||
} catch (InvalidPath &) {
|
||||
|
||||
@@ -978,7 +978,7 @@ drvName, Bindings * attrs, Value & v)
|
||||
[&](const NixStringContextElem::DrvDeep & d) {
|
||||
/* !!! This doesn't work if readOnlyMode is set. */
|
||||
StorePathSet refs;
|
||||
state.ctx.store->computeFSClosure(d.drvPath, refs);
|
||||
state.aio.blockOn(state.ctx.store->computeFSClosure(d.drvPath, refs));
|
||||
for (auto & j : refs) {
|
||||
drv.inputSrcs.insert(j);
|
||||
if (j.isDerivation()) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "lix/libutil/logging-json.hh"
|
||||
#include "lix/libstore/build/substitution-goal.hh"
|
||||
#include "lix/libstore/build/drv-output-substitution-goal.hh"
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
|
||||
#include <boost/outcome/try.hpp>
|
||||
@@ -466,7 +467,7 @@ try {
|
||||
StorePathSet outputClosure;
|
||||
for (auto & i : outputs) {
|
||||
if (!wantedOutputs.contains(i.first)) continue;
|
||||
worker.store.computeFSClosure(i.second, outputClosure);
|
||||
TRY_AWAIT(worker.store.computeFSClosure(i.second, outputClosure));
|
||||
}
|
||||
|
||||
/* Filter out our own outputs (which we have already checked). */
|
||||
@@ -477,7 +478,7 @@ try {
|
||||
derivation is responsible for which path in the output
|
||||
closure. */
|
||||
StorePathSet inputClosure;
|
||||
if (useDerivation) worker.store.computeFSClosure(drvPath, inputClosure);
|
||||
if (useDerivation) TRY_AWAIT(worker.store.computeFSClosure(drvPath, inputClosure));
|
||||
std::map<StorePath, StorePath> outputsToDrv;
|
||||
for (auto & i : inputClosure)
|
||||
if (i.isDerivation()) {
|
||||
@@ -619,58 +620,64 @@ try {
|
||||
co_return co_await resolvedFinished();
|
||||
}
|
||||
|
||||
std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accumInputPaths;
|
||||
std::function<kj::Promise<Result<void>>(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accumInputPaths;
|
||||
|
||||
accumInputPaths = [&](const StorePath & depDrvPath, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
|
||||
/* Add the relevant output closures of the input derivation
|
||||
`i' as input paths. Only add the closures of output paths
|
||||
that are specified as inputs. */
|
||||
auto getOutput = [&](const std::string & outputName) {
|
||||
/* TODO (impure derivations-induced tech debt):
|
||||
Tracking input derivation outputs statefully through the
|
||||
goals is error prone and has led to bugs.
|
||||
For a robust nix, we need to move towards the `else` branch,
|
||||
which does not rely on goal state to match up with the
|
||||
reality of the store, which is our real source of truth.
|
||||
However, the impure derivations feature still relies on this
|
||||
fragile way of doing things, because its builds do not have
|
||||
a representation in the store, which is a usability problem
|
||||
in itself. When implementing this logic entirely with lookups
|
||||
make sure that they're cached. */
|
||||
if (auto outPath = get(inputDrvOutputs, { depDrvPath, outputName })) {
|
||||
return *outPath;
|
||||
}
|
||||
else {
|
||||
auto outMap = [&]{
|
||||
for (auto * drvStore : { &worker.evalStore, &worker.store })
|
||||
if (drvStore->isValidPath(depDrvPath))
|
||||
return worker.store.queryDerivationOutputMap(depDrvPath, drvStore);
|
||||
assert(false);
|
||||
}();
|
||||
|
||||
auto outMapPath = outMap.find(outputName);
|
||||
if (outMapPath == outMap.end()) {
|
||||
throw Error(
|
||||
"derivation '%s' requires non-existent output '%s' from input derivation '%s'",
|
||||
worker.store.printStorePath(drvPath), outputName, worker.store.printStorePath(depDrvPath));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines)
|
||||
accumInputPaths = [&](const StorePath & depDrvPath, const DerivedPathMap<StringSet>::ChildNode & inputNode) -> kj::Promise<Result<void>> {
|
||||
try {
|
||||
/* Add the relevant output closures of the input derivation
|
||||
`i' as input paths. Only add the closures of output paths
|
||||
that are specified as inputs. */
|
||||
auto getOutput = [&](const std::string & outputName) {
|
||||
/* TODO (impure derivations-induced tech debt):
|
||||
Tracking input derivation outputs statefully through the
|
||||
goals is error prone and has led to bugs.
|
||||
For a robust nix, we need to move towards the `else` branch,
|
||||
which does not rely on goal state to match up with the
|
||||
reality of the store, which is our real source of truth.
|
||||
However, the impure derivations feature still relies on this
|
||||
fragile way of doing things, because its builds do not have
|
||||
a representation in the store, which is a usability problem
|
||||
in itself. When implementing this logic entirely with lookups
|
||||
make sure that they're cached. */
|
||||
if (auto outPath = get(inputDrvOutputs, { depDrvPath, outputName })) {
|
||||
return *outPath;
|
||||
}
|
||||
return outMapPath->second;
|
||||
}
|
||||
};
|
||||
else {
|
||||
auto outMap = [&]{
|
||||
for (auto * drvStore : { &worker.evalStore, &worker.store })
|
||||
if (drvStore->isValidPath(depDrvPath))
|
||||
return worker.store.queryDerivationOutputMap(depDrvPath, drvStore);
|
||||
assert(false);
|
||||
}();
|
||||
|
||||
for (auto & outputName : inputNode.value)
|
||||
worker.store.computeFSClosure(getOutput(outputName), inputPaths);
|
||||
auto outMapPath = outMap.find(outputName);
|
||||
if (outMapPath == outMap.end()) {
|
||||
throw Error(
|
||||
"derivation '%s' requires non-existent output '%s' from input derivation '%s'",
|
||||
worker.store.printStorePath(drvPath), outputName, worker.store.printStorePath(depDrvPath));
|
||||
}
|
||||
return outMapPath->second;
|
||||
}
|
||||
};
|
||||
|
||||
for (auto & [outputName, childNode] : inputNode.childMap)
|
||||
accumInputPaths(getOutput(outputName), childNode);
|
||||
for (auto & outputName : inputNode.value)
|
||||
TRY_AWAIT(worker.store.computeFSClosure(getOutput(outputName), inputPaths));
|
||||
|
||||
for (auto & [outputName, childNode] : inputNode.childMap)
|
||||
TRY_AWAIT(accumInputPaths(getOutput(outputName), childNode));
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
};
|
||||
|
||||
for (auto & [depDrvPath, depNode] : fullDrv.inputDrvs.map)
|
||||
accumInputPaths(depDrvPath, depNode);
|
||||
TRY_AWAIT(accumInputPaths(depDrvPath, depNode));
|
||||
}
|
||||
|
||||
/* Second, the input sources. */
|
||||
worker.store.computeFSClosure(drv->inputSrcs, inputPaths);
|
||||
TRY_AWAIT(worker.store.computeFSClosure(drv->inputSrcs, inputPaths));
|
||||
|
||||
debug("added input paths %s", worker.store.showPaths(inputPaths));
|
||||
|
||||
|
||||
@@ -610,8 +610,11 @@ try {
|
||||
StorePathSet closure;
|
||||
for (auto & i : pathsInChroot)
|
||||
try {
|
||||
if (worker.store.isInStore(i.second.source))
|
||||
worker.store.computeFSClosure(worker.store.toStorePath(i.second.source).first, closure);
|
||||
if (worker.store.isInStore(i.second.source)) {
|
||||
TRY_AWAIT(worker.store.computeFSClosure(
|
||||
worker.store.toStorePath(i.second.source).first, closure
|
||||
));
|
||||
}
|
||||
} catch (InvalidPath & e) {
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "while processing 'sandbox-paths'");
|
||||
@@ -1191,7 +1194,7 @@ struct RestrictedStore : public virtual IndirectRootStore, public virtual GcStor
|
||||
}
|
||||
|
||||
StorePathSet closure;
|
||||
next->computeFSClosure(newPaths, closure);
|
||||
TRY_AWAIT(next->computeFSClosure(newPaths, closure));
|
||||
for (auto & path : closure)
|
||||
goal.addDependency(path);
|
||||
for (auto & real : Realisation::closure(*next, newRealisations))
|
||||
|
||||
+110
-96
@@ -682,111 +682,125 @@ try {
|
||||
via the referrers edges and optionally derivers and derivation
|
||||
output edges. If none of those paths are roots, then all
|
||||
visited paths are garbage and are deleted. */
|
||||
auto deleteReferrersClosure = [&](const StorePath & start) {
|
||||
StorePathSet visited;
|
||||
std::queue<StorePath> todo;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines)
|
||||
auto deleteReferrersClosure = [&](const StorePath & start) -> kj::Promise<Result<void>> {
|
||||
try {
|
||||
StorePathSet visited;
|
||||
std::queue<StorePath> todo;
|
||||
|
||||
/* Wake up any GC client waiting for deletion of the paths in
|
||||
'visited' to finish. */
|
||||
Finally releasePending([&]() {
|
||||
gcServer.releasePending();
|
||||
});
|
||||
/* Wake up any GC client waiting for deletion of the paths in
|
||||
'visited' to finish. */
|
||||
Finally releasePending([&]() {
|
||||
gcServer.releasePending();
|
||||
});
|
||||
|
||||
auto enqueue = [&](const StorePath & path) {
|
||||
if (visited.insert(path).second)
|
||||
todo.push(path);
|
||||
};
|
||||
|
||||
enqueue(start);
|
||||
|
||||
while (auto path = pop(todo)) {
|
||||
checkInterrupt();
|
||||
|
||||
/* Bail out if we've previously discovered that this path
|
||||
is alive. */
|
||||
if (alive.count(*path)) {
|
||||
alive.insert(start);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we've previously deleted this path, we don't have to
|
||||
handle it again. */
|
||||
if (dead.count(*path)) continue;
|
||||
|
||||
auto markAlive = [&]()
|
||||
{
|
||||
alive.insert(*path);
|
||||
alive.insert(start);
|
||||
try {
|
||||
StorePathSet closure;
|
||||
computeFSClosure(*path, closure,
|
||||
/* flipDirection */ false, gcKeepOutputs, gcKeepDerivations);
|
||||
for (auto & p : closure)
|
||||
alive.insert(p);
|
||||
} catch (InvalidPath &) { }
|
||||
auto enqueue = [&](const StorePath & path) {
|
||||
if (visited.insert(path).second)
|
||||
todo.push(path);
|
||||
};
|
||||
|
||||
/* If this is a root, bail out. */
|
||||
if (roots.count(*path)) {
|
||||
debug("cannot delete '%s' because it's a root", printStorePath(*path));
|
||||
return markAlive();
|
||||
}
|
||||
enqueue(start);
|
||||
|
||||
if (options.action == GCOptions::gcDeleteSpecific
|
||||
&& !options.pathsToDelete.count(*path))
|
||||
return;
|
||||
while (auto path = pop(todo)) {
|
||||
checkInterrupt();
|
||||
|
||||
if (!gcServer.markPendingIfPresent(std::string(path->hashPart()))) {
|
||||
debug("cannot delete '%s' because it's a temporary root", printStorePath(*path));
|
||||
return markAlive();
|
||||
}
|
||||
|
||||
if (isValidPath(*path)) {
|
||||
|
||||
/* Visit the referrers of this path. */
|
||||
auto i = referrersCache.find(*path);
|
||||
if (i == referrersCache.end()) {
|
||||
StorePathSet referrers;
|
||||
queryReferrers(*path, referrers);
|
||||
referrersCache.emplace(*path, std::move(referrers));
|
||||
i = referrersCache.find(*path);
|
||||
}
|
||||
for (auto & p : i->second)
|
||||
enqueue(p);
|
||||
|
||||
/* If keep-derivations is set and this is a
|
||||
derivation, then visit the derivation outputs. */
|
||||
if (gcKeepDerivations && path->isDerivation()) {
|
||||
for (auto & [name, maybeOutPath] : queryPartialDerivationOutputMap(*path))
|
||||
if (maybeOutPath &&
|
||||
isValidPath(*maybeOutPath) &&
|
||||
queryPathInfo(*maybeOutPath)->deriver == *path)
|
||||
enqueue(*maybeOutPath);
|
||||
/* Bail out if we've previously discovered that this path
|
||||
is alive. */
|
||||
if (alive.count(*path)) {
|
||||
alive.insert(start);
|
||||
co_return result::success();
|
||||
}
|
||||
|
||||
/* If keep-outputs is set, then visit the derivers. */
|
||||
if (gcKeepOutputs) {
|
||||
auto derivers = queryValidDerivers(*path);
|
||||
for (auto & i : derivers)
|
||||
enqueue(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If we've previously deleted this path, we don't have to
|
||||
handle it again. */
|
||||
if (dead.count(*path)) continue;
|
||||
|
||||
for (auto & path : topoSortPaths(visited)) {
|
||||
if (!dead.insert(path).second) continue;
|
||||
if (shouldDelete) {
|
||||
try {
|
||||
invalidatePathChecked(path);
|
||||
deleteFromStore(path.to_string());
|
||||
referrersCache.erase(path);
|
||||
} catch (PathInUse &) {
|
||||
// References to upstream "bugs":
|
||||
// https://github.com/NixOS/nix/issues/11923
|
||||
// https://git.lix.systems/lix-project/lix/issues/621
|
||||
printInfo("Skipping deletion of path '%1%' because it is now in use, preventing its removal.", printStorePath(path));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines)
|
||||
auto markAlive = [&]() -> kj::Promise<Result<void>>
|
||||
{
|
||||
try {
|
||||
alive.insert(*path);
|
||||
alive.insert(start);
|
||||
try {
|
||||
StorePathSet closure;
|
||||
TRY_AWAIT(computeFSClosure(*path, closure,
|
||||
/* flipDirection */ false, gcKeepOutputs, gcKeepDerivations));
|
||||
for (auto & p : closure)
|
||||
alive.insert(p);
|
||||
} catch (InvalidPath &) { }
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
};
|
||||
|
||||
/* If this is a root, bail out. */
|
||||
if (roots.count(*path)) {
|
||||
debug("cannot delete '%s' because it's a root", printStorePath(*path));
|
||||
TRY_AWAIT(markAlive());
|
||||
co_return result::success();
|
||||
}
|
||||
|
||||
if (options.action == GCOptions::gcDeleteSpecific
|
||||
&& !options.pathsToDelete.count(*path))
|
||||
co_return result::success();
|
||||
|
||||
if (!gcServer.markPendingIfPresent(std::string(path->hashPart()))) {
|
||||
debug("cannot delete '%s' because it's a temporary root", printStorePath(*path));
|
||||
TRY_AWAIT(markAlive());
|
||||
co_return result::success();
|
||||
}
|
||||
|
||||
if (isValidPath(*path)) {
|
||||
|
||||
/* Visit the referrers of this path. */
|
||||
auto i = referrersCache.find(*path);
|
||||
if (i == referrersCache.end()) {
|
||||
StorePathSet referrers;
|
||||
queryReferrers(*path, referrers);
|
||||
referrersCache.emplace(*path, std::move(referrers));
|
||||
i = referrersCache.find(*path);
|
||||
}
|
||||
for (auto & p : i->second)
|
||||
enqueue(p);
|
||||
|
||||
/* If keep-derivations is set and this is a
|
||||
derivation, then visit the derivation outputs. */
|
||||
if (gcKeepDerivations && path->isDerivation()) {
|
||||
for (auto & [name, maybeOutPath] : queryPartialDerivationOutputMap(*path))
|
||||
if (maybeOutPath &&
|
||||
isValidPath(*maybeOutPath) &&
|
||||
queryPathInfo(*maybeOutPath)->deriver == *path)
|
||||
enqueue(*maybeOutPath);
|
||||
}
|
||||
|
||||
/* If keep-outputs is set, then visit the derivers. */
|
||||
if (gcKeepOutputs) {
|
||||
auto derivers = queryValidDerivers(*path);
|
||||
for (auto & i : derivers)
|
||||
enqueue(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & path : topoSortPaths(visited)) {
|
||||
if (!dead.insert(path).second) continue;
|
||||
if (shouldDelete) {
|
||||
try {
|
||||
invalidatePathChecked(path);
|
||||
deleteFromStore(path.to_string());
|
||||
referrersCache.erase(path);
|
||||
} catch (PathInUse &) {
|
||||
// References to upstream "bugs":
|
||||
// https://github.com/NixOS/nix/issues/11923
|
||||
// https://git.lix.systems/lix-project/lix/issues/621
|
||||
printInfo("Skipping deletion of path '%1%' because it is now in use, preventing its removal.", printStorePath(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -795,7 +809,7 @@ try {
|
||||
if (options.action == GCOptions::gcDeleteSpecific) {
|
||||
|
||||
for (auto & i : options.pathsToDelete) {
|
||||
deleteReferrersClosure(i);
|
||||
TRY_AWAIT(deleteReferrersClosure(i));
|
||||
if (!dead.count(i))
|
||||
throw Error(
|
||||
"Cannot delete path '%1%' since it is still alive. "
|
||||
@@ -827,7 +841,7 @@ try {
|
||||
if (name == "." || name == ".." || name == linksName) continue;
|
||||
|
||||
if (auto storePath = maybeParseStorePath(config().storeDir + "/" + name))
|
||||
deleteReferrersClosure(*storePath);
|
||||
TRY_AWAIT(deleteReferrersClosure(*storePath));
|
||||
else
|
||||
deleteFromStore(name);
|
||||
|
||||
|
||||
@@ -384,13 +384,15 @@ public:
|
||||
kj::Promise<Result<void>> repairPath(const StorePath & path) override
|
||||
try { unsupported("repairPath"); } catch (...) { return {result::current_exception()}; }
|
||||
|
||||
void computeFSClosure(const StorePathSet & paths,
|
||||
kj::Promise<Result<void>> computeFSClosure(const StorePathSet & paths,
|
||||
StorePathSet & out, bool flipDirection = false,
|
||||
bool includeOutputs = false, bool includeDerivers = false) override
|
||||
{
|
||||
try {
|
||||
if (flipDirection || includeDerivers) {
|
||||
Store::computeFSClosure(paths, out, flipDirection, includeOutputs, includeDerivers);
|
||||
return;
|
||||
TRY_AWAIT(
|
||||
Store::computeFSClosure(paths, out, flipDirection, includeOutputs, includeDerivers)
|
||||
);
|
||||
co_return result::success();
|
||||
}
|
||||
|
||||
auto conn(connections->get());
|
||||
@@ -403,6 +405,9 @@ public:
|
||||
|
||||
for (auto & i : ServeProto::Serialise<StorePathSet>::read(*this, *conn))
|
||||
out.insert(i);
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
StorePathSet queryValidPaths(const StorePathSet & paths,
|
||||
|
||||
@@ -11,7 +11,7 @@ kj::Promise<Result<std::map<StorePath, StorePath>>> makeContentAddressed(
|
||||
const StorePathSet & storePaths)
|
||||
try {
|
||||
StorePathSet closure;
|
||||
srcStore.computeFSClosure(storePaths, closure);
|
||||
TRY_AWAIT(srcStore.computeFSClosure(storePaths, closure));
|
||||
|
||||
auto paths = srcStore.topoSortPaths(closure);
|
||||
|
||||
|
||||
+59
-38
@@ -12,59 +12,80 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
void Store::computeFSClosure(const StorePathSet & startPaths,
|
||||
kj::Promise<Result<void>> Store::computeFSClosure(const StorePathSet & startPaths,
|
||||
StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers)
|
||||
{
|
||||
std::function<std::set<StorePath>(const StorePath & path, ref<const ValidPathInfo>)> queryDeps;
|
||||
if (flipDirection)
|
||||
queryDeps = [&](const StorePath& path, ref<const ValidPathInfo>) {
|
||||
StorePathSet res;
|
||||
StorePathSet referrers;
|
||||
queryReferrers(path, referrers);
|
||||
for (auto& ref : referrers)
|
||||
if (ref != path)
|
||||
res.insert(ref);
|
||||
try {
|
||||
std::function<
|
||||
kj::Promise<Result<std::set<StorePath>>>(const StorePath & path, ref<const ValidPathInfo>)>
|
||||
queryDeps;
|
||||
if (flipDirection) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines)
|
||||
queryDeps = [&](const StorePath & path,
|
||||
ref<const ValidPathInfo>) -> kj::Promise<Result<std::set<StorePath>>> {
|
||||
try {
|
||||
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 (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;
|
||||
if (includeDerivers && path.isDerivation())
|
||||
for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path))
|
||||
if (maybeOutPath && isValidPath(*maybeOutPath))
|
||||
res.insert(*maybeOutPath);
|
||||
co_return res;
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
};
|
||||
else
|
||||
queryDeps = [&](const StorePath& path, ref<const ValidPathInfo> info) {
|
||||
StorePathSet res;
|
||||
for (auto& ref : info->references)
|
||||
if (ref != path)
|
||||
res.insert(ref);
|
||||
} else {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines)
|
||||
queryDeps = [&](const StorePath & path,
|
||||
ref<const ValidPathInfo> info) -> kj::Promise<Result<std::set<StorePath>>> {
|
||||
try {
|
||||
StorePathSet res;
|
||||
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 (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;
|
||||
if (includeDerivers && info->deriver && isValidPath(*info->deriver))
|
||||
res.insert(*info->deriver);
|
||||
co_return res;
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
paths_.merge(computeClosure<StorePath>(
|
||||
paths_.merge(TRY_AWAIT(computeClosureAsync<StorePath>(
|
||||
startPaths,
|
||||
[&](const StorePath& path) -> std::set<StorePath> {
|
||||
[&](const StorePath& path) -> kj::Promise<Result<std::set<StorePath>>> {
|
||||
return queryDeps(path, queryPathInfo(path));
|
||||
}));
|
||||
})));
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
void Store::computeFSClosure(const StorePath & startPath,
|
||||
kj::Promise<Result<void>> Store::computeFSClosure(const StorePath & startPath,
|
||||
StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers)
|
||||
{
|
||||
try {
|
||||
StorePathSet paths;
|
||||
paths.insert(startPath);
|
||||
computeFSClosure(paths, paths_, flipDirection, includeOutputs, includeDerivers);
|
||||
TRY_AWAIT(computeFSClosure(paths, paths_, flipDirection, includeOutputs, includeDerivers));
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ try {
|
||||
StorePathSet initialStorePaths, pathsClosure;
|
||||
for (auto& path : startPaths)
|
||||
initialStorePaths.insert(path.path());
|
||||
store.computeFSClosure(initialStorePaths, pathsClosure);
|
||||
TRY_AWAIT(store.computeFSClosure(initialStorePaths, pathsClosure));
|
||||
ret.insert(startPaths.begin(), startPaths.end());
|
||||
ret.insert(pathsClosure.begin(), pathsClosure.end());
|
||||
co_return result::success();
|
||||
|
||||
@@ -943,7 +943,7 @@ try {
|
||||
if (!inputPaths.count(storePath))
|
||||
throw BuildError("cannot export references of path '%s' because it is not in the input closure of the derivation", printStorePath(storePath));
|
||||
|
||||
computeFSClosure({storePath}, paths);
|
||||
TRY_AWAIT(computeFSClosure({storePath}, paths));
|
||||
}
|
||||
|
||||
/* If there are derivations in the graph, then include their
|
||||
@@ -962,7 +962,7 @@ try {
|
||||
derivation itself. That doesn't seem right to me, so I
|
||||
won't try to implemented this for CA derivations. */
|
||||
throw UnimplementedError("exportReferences on CA derivations is not yet implemented");
|
||||
computeFSClosure(*k.second.second, paths);
|
||||
TRY_AWAIT(computeFSClosure(*k.second.second, paths));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1053,7 +1053,7 @@ Store::getClosureSize(const StorePath & storePath)
|
||||
try {
|
||||
uint64_t totalNarSize = 0, totalDownloadSize = 0;
|
||||
StorePathSet closure;
|
||||
computeFSClosure(storePath, closure, false, false);
|
||||
TRY_AWAIT(computeFSClosure(storePath, closure, false, false));
|
||||
for (auto & p : closure) {
|
||||
auto info = queryPathInfo(p);
|
||||
totalNarSize += info->narSize;
|
||||
@@ -1339,7 +1339,7 @@ try {
|
||||
if (&srcStore == &dstStore) co_return result::success();
|
||||
|
||||
StorePathSet closure;
|
||||
srcStore.computeFSClosure(storePaths, closure);
|
||||
TRY_AWAIT(srcStore.computeFSClosure(storePaths, closure));
|
||||
TRY_AWAIT(copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute));
|
||||
co_return result::success();
|
||||
} catch (...) {
|
||||
|
||||
@@ -766,11 +766,11 @@ public:
|
||||
* `referrers` relation instead of the `references` relation is
|
||||
* returned.
|
||||
*/
|
||||
virtual void computeFSClosure(const StorePathSet & paths,
|
||||
virtual kj::Promise<Result<void>> computeFSClosure(const StorePathSet & paths,
|
||||
StorePathSet & out, bool flipDirection = false,
|
||||
bool includeOutputs = false, bool includeDerivers = false);
|
||||
|
||||
void computeFSClosure(const StorePath & path,
|
||||
kj::Promise<Result<void>> computeFSClosure(const StorePath & path,
|
||||
StorePathSet & out, bool flipDirection = false,
|
||||
bool includeOutputs = false, bool includeDerivers = false);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ struct CmdShowDerivation : InstallablesCommand
|
||||
|
||||
if (recursive) {
|
||||
StorePathSet closure;
|
||||
store->computeFSClosure(drvPaths, closure);
|
||||
aio().blockOn(store->computeFSClosure(drvPaths, closure));
|
||||
drvPaths = std::move(closure);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ static kj::Promise<Result<GroupedPaths>>
|
||||
getClosureInfo(ref<Store> store, const StorePath & toplevel)
|
||||
try {
|
||||
StorePathSet closure;
|
||||
store->computeFSClosure({toplevel}, closure);
|
||||
TRY_AWAIT(store->computeFSClosure({toplevel}, closure));
|
||||
|
||||
GroupedPaths groupedPaths;
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||
}();
|
||||
|
||||
StorePathSet closure;
|
||||
store->computeFSClosure({packagePath}, closure, false, false);
|
||||
aio().blockOn(store->computeFSClosure({packagePath}, closure, false, false));
|
||||
|
||||
if (!optDependencyPath.has_value() || !closure.count(*optDependencyPath)) {
|
||||
printError("'%s' does not depend on '%s'", package->what(), dependency->what());
|
||||
|
||||
Reference in New Issue
Block a user