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) {
|
for (auto & i : options.pathsToDelete) {
|
||||||
assertStorePath(i);
|
assertStorePath(i);
|
||||||
tryToDelete(state, i);
|
tryToDelete(state, i);
|
||||||
if (state.dead.find(i) == state.dead.end())
|
if (state.dead.find(i) == state.dead.end()) {
|
||||||
throw Error(format("cannot delete path '%1%' since it is still alive") % i);
|
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) {
|
} else if (options.maxFreed > 0) {
|
||||||
|
|
|
@ -237,6 +237,40 @@ void Store::queryMissing(const PathSet & targets,
|
||||||
pool.process();
|
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)
|
Paths Store::topoSortPaths(const PathSet & paths)
|
||||||
{
|
{
|
||||||
|
|
|
@ -551,6 +551,9 @@ public:
|
||||||
relation. If p refers to q, then p preceeds q in this list. */
|
relation. If p refers to q, then p preceeds q in this list. */
|
||||||
Paths topoSortPaths(const PathSet & paths);
|
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
|
/* Export multiple paths in the format expected by ‘nix-store
|
||||||
--import’. */
|
--import’. */
|
||||||
void exportPaths(const Paths & paths, Sink & sink);
|
void exportPaths(const Paths & paths, Sink & sink);
|
||||||
|
|
|
@ -228,45 +228,6 @@ static PathSet maybeUseOutputs(const Path & storePath, bool useOutput, bool forc
|
||||||
else return {storePath};
|
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. */
|
/* Perform various sorts of queries. */
|
||||||
static void opQuery(Strings opFlags, Strings opArgs)
|
static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
{
|
{
|
||||||
|
@ -388,8 +349,19 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
|
|
||||||
case qTree: {
|
case qTree: {
|
||||||
PathSet done;
|
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)
|
for (auto & i : opArgs)
|
||||||
printTree(store->followLinksToStorePath(i), "", "", done);
|
cout << store->printTree(store->followLinksToStorePath(i), "", "", done, getChildren);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue