From a5269fd21405bed814e373f5fb66e0413fe95f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac-Jacqu=C3=A9?= Date: Thu, 5 Apr 2018 21:21:09 +0200 Subject: [PATCH] nix-store: Explain why nix path cannot be deleted. Print a tree showing the links to the garbage roots when a path cannot be removed from nix-store. --- src/libstore/gc.cc | 19 ++++++++++++-- src/libstore/misc.cc | 34 +++++++++++++++++++++++++ src/libstore/store-api.hh | 3 +++ src/nix-store/nix-store.cc | 52 +++++++++----------------------------- 4 files changed, 66 insertions(+), 42 deletions(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index ba49749d..bcf4427a 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -786,8 +786,23 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) for (auto & i : options.pathsToDelete) { assertStorePath(i); tryToDelete(state, i); - if (state.dead.find(i) == state.dead.end()) - throw Error(format("cannot delete path '%1%' since it is still alive") % i); + if (state.dead.find(i) == state.dead.end()) { + PathSet done; + auto getGcRootChildren = [this, &state](Path p){ + PathSet incomingLinks; + PathSet alreadyVisited; + PathSet children; + queryReferrers(p, incomingLinks); + for (auto & j : incomingLinks) + if(canReachRoot(state, alreadyVisited, p)) + children.insert(j); + Paths sorted = topoSortPaths(children); + reverse(sorted.begin(), sorted.end()); + return sorted; + }; + string tree = printTree(i, "", "", done, getGcRootChildren); + throw Error(format("cannot delete path '%1%': it's still referrenced by the following GC roots \n\n%2%") % i % tree); + } } } else if (options.maxFreed > 0) { diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index a82aa4e9..27ba7998 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -237,6 +237,40 @@ void Store::queryMissing(const PathSet & targets, pool.process(); } +/* Some code to print a tree representation of a derivation dependency + graph. Topological sorting is used to keep the tree relatively + flat. */ + +const string treeConn = "+---"; +const string treeLine = "| "; +const string treeNull = " "; + + +string Store::printTree(const Path & path, + const string & firstPad, const string & tailPad, PathSet & done, + std::function getChildren) +{ + string out; + Paths children; + + if (done.find(path) != done.end()) { + out.append(str(format("%1%%2% [...]\n") % firstPad % path)); + return ""; + } + done.insert(path); + + out.append(str(format("%1%%2%\n") % firstPad % path)); + + children = getChildren(path); + + for (auto i = children.begin(); i != children.end(); ++i) { + auto j = i; ++j; + out.append(printTree(*i, tailPad + treeConn, + j == children.end() ? tailPad + treeNull : tailPad + treeLine, + done, getChildren)); + } + return out; +} Paths Store::topoSortPaths(const PathSet & paths) { diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index ea259f07..b92561b7 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -551,6 +551,9 @@ public: relation. If p refers to q, then p preceeds q in this list. */ Paths topoSortPaths(const PathSet & paths); + string printTree(const Path & path, const string & firstPad, + const string & tailPad, PathSet & done, std::function getChildren); + /* Export multiple paths in the format expected by ‘nix-store --import’. */ void exportPaths(const Paths & paths, Sink & sink); diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index e1e27cee..5b73bbc4 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -228,45 +228,6 @@ static PathSet maybeUseOutputs(const Path & storePath, bool useOutput, bool forc else return {storePath}; } - -/* Some code to print a tree representation of a derivation dependency - graph. Topological sorting is used to keep the tree relatively - flat. */ - -const string treeConn = "+---"; -const string treeLine = "| "; -const string treeNull = " "; - - -static void printTree(const Path & path, - const string & firstPad, const string & tailPad, PathSet & done) -{ - if (done.find(path) != done.end()) { - cout << format("%1%%2% [...]\n") % firstPad % path; - return; - } - done.insert(path); - - cout << format("%1%%2%\n") % firstPad % path; - - auto references = store->queryPathInfo(path)->references; - - /* Topologically sort under the relation A < B iff A \in - closure(B). That is, if derivation A is an (possibly indirect) - input of B, then A is printed first. This has the effect of - flattening the tree, preventing deeply nested structures. */ - Paths sorted = store->topoSortPaths(references); - reverse(sorted.begin(), sorted.end()); - - for (auto i = sorted.begin(); i != sorted.end(); ++i) { - auto j = i; ++j; - printTree(*i, tailPad + treeConn, - j == sorted.end() ? tailPad + treeNull : tailPad + treeLine, - done); - } -} - - /* Perform various sorts of queries. */ static void opQuery(Strings opFlags, Strings opArgs) { @@ -388,8 +349,19 @@ static void opQuery(Strings opFlags, Strings opArgs) case qTree: { PathSet done; + auto locStorePtr = store; + auto getChildren = [locStorePtr](Path p){ + auto references = locStorePtr->queryPathInfo(p)->references; + /* Topologically sort under the relation A < B iff A \in + closure(B). That is, if derivation A is an (possibly indirect) + input of B, then A is printed first. This has the effect of + flattening the tree, preventing deeply nested structures. */ + Paths sorted = locStorePtr->topoSortPaths(references); + reverse(sorted.begin(), sorted.end()); + return sorted; + }; for (auto & i : opArgs) - printTree(store->followLinksToStorePath(i), "", "", done); + cout << store->printTree(store->followLinksToStorePath(i), "", "", done, getChildren); break; }