why-depends: initialize internal graph data from std::map<StorePath, StorePathSet>

The Node struct should become an implementation detail when moving this
into libstore. A map from a node to its direct references is more
intuitive here.

Change-Id: I9fddce6b398b8bb97834e5586bee72b244885fdd
This commit is contained in:
Maximilian Bosch
2025-07-07 11:44:17 +00:00
parent 353a87727b
commit 49263f154a
+61 -41
View File
@@ -7,6 +7,7 @@
#include "lix/libutil/result.hh"
#include "why-depends.hh"
#include <algorithm>
#include <queue>
namespace nix {
@@ -229,6 +230,61 @@ try {
co_return result::current_exception();
}
static std::map<StorePath, Node> mkGraph(
const StorePath & packagePath,
const StorePath & dependencyPath,
const std::map<StorePath, StorePathSet> & graph,
Store & store,
bool all,
bool precise
)
{
std::map<StorePath, Node> graph_data;
for (auto & [path, dependencies] : graph) {
graph_data.emplace(
path,
Node{
.path = path,
.dependencies = dependencies,
.dist = path == dependencyPath ? std::optional(0) : std::nullopt,
}
);
}
for (auto & node : graph_data) {
for (auto & ref : node.second.dependencies) {
graph_data.find(ref)->second.dependents.insert(node.first);
}
}
/* Run Dijkstra's shortest path algorithm to get the distance
of every path in the closure to 'dependency'. */
std::priority_queue<Node *> queue;
queue.push(&graph_data.at(dependencyPath));
auto const inf = std::numeric_limits<size_t>::max();
while (!queue.empty()) {
auto & node = *queue.top();
queue.pop();
for (auto & rref : node.dependents) {
auto & node2 = graph_data.at(rref);
auto dist = node.dist.transform([](auto n) { return n + 1; });
if (dist.value_or(inf) < node2.dist.value_or(inf)) {
node2.dist = dist;
node2.prev = &node;
if (!node2.queued) {
node2.queued = true;
queue.push(&node2);
}
}
}
}
return graph_data;
}
struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
{
std::string _package, _dependency;
@@ -316,49 +372,13 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
auto accessor = store->getFSAccessor();
std::map<StorePath, Node> graph;
for (auto & path : closure)
graph.emplace(
path,
Node{
.path = path,
.dependencies = aio().blockOn(store->queryPathInfo(path))->references,
.dist = path == dependencyPath ? std::optional(0) : std::nullopt
}
);
// Transpose the graph.
for (auto & node : graph)
for (auto & ref : node.second.dependencies) {
graph.find(ref)->second.dependents.insert(node.first);
}
/* Run Dijkstra's shortest path algorithm to get the distance
of every path in the closure to 'dependency'. */
std::priority_queue<Node *> queue;
queue.push(&graph.at(dependencyPath));
auto const inf = std::numeric_limits<size_t>::max();
while (!queue.empty()) {
auto & node = *queue.top();
queue.pop();
for (auto & rref : node.dependents) {
auto & node2 = graph.at(rref);
auto dist = node.dist.transform([](auto n) { return n + 1; });
if (dist.value_or(inf) < node2.dist.value_or(inf)) {
node2.dist = dist;
node2.prev = &node;
if (!node2.queued) {
node2.queued = true;
queue.push(&node2);
}
}
}
std::map<StorePath, StorePathSet> graphData;
for (auto & path : closure) {
graphData.emplace(path, aio().blockOn(store->queryPathInfo(path))->references);
}
auto graph = mkGraph(packagePath, dependencyPath, graphData, *store, all, precise);
/* Print the subgraph of nodes that have 'dependency' in their
closure (i.e., that have a non-infinite distance to
'dependency'). Print every edge on a path between `package`