1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
#include "lix/libcmd/command.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libmain/shared.hh"
#include "lix/libstore/path-tree.hh"
#include "why-depends.hh"
namespace nix {
struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
{
std::string _package, _dependency;
bool all = false;
bool precise = false;
CmdWhyDepends()
{
expectArgs({
.label = "package",
.handler = {&_package},
.completer = getCompleteInstallable(),
});
expectArgs({
.label = "dependency",
.handler = {&_dependency},
.completer = getCompleteInstallable(),
});
addFlag({
.longName = "all",
.shortName = 'a',
.description = "Show all edges in the dependency graph leading from *package* to *dependency*, rather than just a shortest path.",
.handler = {&all, true},
});
addFlag({
.longName = "precise",
.description = "For each edge in the dependency graph, show the files in the parent that cause the dependency.",
.handler = {&precise, true},
});
}
std::string description() override
{
return "show why a package has another package in its closure";
}
std::string doc() override
{
return
#include "why-depends.md"
;
}
Category category() override { return catSecondary; }
void run(ref<Store> store) override
{
auto state = getEvaluator()->begin(aio());
auto package = parseInstallable(*state, store, _package);
auto packagePath = Installable::toStorePath(*state, getEvalStore(), store, Realise::Outputs, operateOn, package);
/* We don't need to build `dependency`. We try to get the store
* path if it's already known, and if not, then it's not a dependency.
*
* Why? If `package` does depends on `dependency`, then getting the
* store path of `package` above necessitated having the store path
* of `dependency`. The contrapositive is, if the store path of
* `dependency` is not already known at this point (i.e. it's a CA
* derivation which hasn't been built), then `package` did not need it
* to build.
*/
auto dependency = parseInstallable(*state, store, _dependency);
auto optDependencyPath = [&]() -> std::optional<StorePath> {
try {
return {Installable::toStorePath(*state, getEvalStore(), store, Realise::Derivation, operateOn, dependency)};
} catch (MissingRealisation &) {
return std::nullopt;
}
}();
StorePathSet closure;
aio().blockOn(store->computeFSClosure({packagePath}, closure, false, false));
if (!optDependencyPath.has_value() || !closure.count(*optDependencyPath)) {
printError("'%s' does not depend on '%s'", package->what(), dependency->what());
return;
}
auto dependencyPath = *optDependencyPath;
logger->pause(); // FIXME
auto accessor = store->getFSAccessor();
std::map<StorePath, StorePathSet> graphData;
for (auto & path : closure) {
graphData.emplace(path, aio().blockOn(store->queryPathInfo(path))->references);
}
/* 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`
and `dependency`. */
withPager([&](Pager & pager) {
pager << aio().blockOn(
genGraphString(packagePath, dependencyPath, graphData, *store, all, precise)
);
});
}
};
void registerNixWhyDepends()
{
registerCommand<CmdWhyDepends>("why-depends");
}
}
|