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:
parent
3fbaa230a2
commit
a5269fd214
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue