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.
This commit is contained in:
Félix Baylac-Jacqué 2018-04-05 21:21:09 +02:00
parent 3fbaa230a2
commit a5269fd214
No known key found for this signature in database
GPG Key ID: EFD315F31848DBA4
4 changed files with 66 additions and 42 deletions

View File

@ -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) {

View File

@ -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<Paths(const Path&)> 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)
{

View File

@ -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<Paths(const Path&)> getChildren);
/* Export multiple paths in the format expected by nix-store
--import. */
void exportPaths(const Paths & paths, Sink & sink);

View File

@ -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;
}