Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Add command "nix why-depends"
This command shows why a package has another package in its runtime closure. For example, to see why VLC has libdrm.dev in its closure: $ nix why-depends nixpkgs.vlc nixpkgs.libdrm.dev /nix/store/g901z9pcj0n5yy5n6ykxk3qm4ina1d6z-vlc-2.2.5.1: lib/libvlccore.so.8.0.0: …nfig:/nix/store/405lmx6jl8lp0ad1vrr6j498chrqhz8g-libdrm-2.4.75-d… /nix/store/s3nm7kd8hlcg0facn2q1ff2n7wrwdi2l-mesa-noglu-17.0.7-dev: nix-support/propagated-native-build-inputs: …-dev /nix/store/405lmx6jl8lp0ad1vrr6j498chrqhz8g-libdrm-2.4.75-d… Thus, VLC's lib/libvlccore.so.8.0.0 as well as mesa-noglu's nix-support/propagated-native-build-inputs cause the dependency.
- Loading branch information
@@ -0,0 +1,133 @@ | ||
#include "command.hh" | ||
#include "common-args.hh" | ||
#include "shared.hh" | ||
#include "store-api.hh" | ||
#include "progress-bar.hh" | ||
#include "fs-accessor.hh" | ||
|
||
using namespace nix; | ||
|
||
static std::string hilite(const std::string & s, size_t pos, size_t len) | ||
{ | ||
return | ||
std::string(s, 0, pos) | ||
+ ANSI_RED | ||
+ std::string(s, pos, len) | ||
+ ANSI_NORMAL | ||
+ std::string(s, pos + len); | ||
} | ||
|
||
struct CmdWhyDepends : SourceExprCommand | ||
{ | ||
std::string _package, _dependency; | ||
|
||
CmdWhyDepends() | ||
{ | ||
expectArg("package", &_package); | ||
expectArg("dependency", &_dependency); | ||
} | ||
|
||
std::string name() override | ||
{ | ||
return "why-depends"; | ||
} | ||
|
||
std::string description() override | ||
{ | ||
return "show why a package has another package in its closure"; | ||
} | ||
|
||
Examples examples() override | ||
{ | ||
return { | ||
Example{ | ||
"To show which files in Hello's closure depend on Glibc:", | ||
"nix why-depends nixpkgs.hello nixpkgs.glibc" | ||
}, | ||
}; | ||
} | ||
|
||
void run(ref<Store> store) override | ||
{ | ||
auto package = parseInstallable(*this, store, _package, false); | ||
auto packagePath = toStorePath(store, Build, package); | ||
auto dependency = parseInstallable(*this, store, _dependency, false); | ||
auto dependencyPath = toStorePath(store, NoBuild, dependency); | ||
auto dependencyPathHash = storePathToHash(dependencyPath); | ||
|
||
PathSet closure; | ||
store->computeFSClosure({packagePath}, closure, false, false); | ||
|
||
if (!closure.count(dependencyPath)) { | ||
printError("'%s' does not depend on '%s'", package->what(), dependency->what()); | ||
return; | ||
} | ||
|
||
stopProgressBar(); // FIXME | ||
|
||
auto accessor = store->getFSAccessor(); | ||
|
||
// FIXME: show the path through the dependency graph. | ||
|
||
bool first = true; | ||
|
||
for (auto & path : closure) { | ||
|
||
if (path == dependencyPath && packagePath != dependencyPath) | ||
continue; | ||
|
||
if (!store->queryPathInfo(path)->references.count(dependencyPath)) | ||
continue; | ||
|
||
if (!first) std::cerr << "\n"; | ||
first = false; | ||
|
||
std::cerr << fmt("%s:\n", path); | ||
|
||
std::function<void(const Path &)> recurse; | ||
|
||
recurse = [&](const Path & p) { | ||
auto st = accessor->stat(p); | ||
|
||
auto p2 = p == path ? "/" : std::string(p, path.size() + 1); | ||
|
||
if (st.type == FSAccessor::Type::tDirectory) { | ||
auto names = accessor->readDirectory(p); | ||
for (auto & name : names) | ||
recurse(p + "/" + name); | ||
} | ||
|
||
else if (st.type == FSAccessor::Type::tRegular) { | ||
auto contents = accessor->readFile(p); | ||
auto pos = contents.find(dependencyPathHash); | ||
if (pos != std::string::npos) { | ||
size_t margin = 16; | ||
auto pos2 = pos >= margin ? pos - margin : 0; | ||
std::string fragment; | ||
for (char c : std::string(contents, pos2, | ||
pos - pos2 + dependencyPathHash.size() + margin)) | ||
{ | ||
fragment += isprint(c) ? c : '.'; | ||
} | ||
|
||
std::cerr << fmt(" %s: …%s…\n", | ||
p2, | ||
hilite(fragment, pos - pos2, storePathHashLen)); | ||
} | ||
} | ||
|
||
else if (st.type == FSAccessor::Type::tSymlink) { | ||
auto target = accessor->readLink(p); | ||
auto pos = target.find(dependencyPathHash); | ||
if (pos != std::string::npos) { | ||
std::cerr << fmt(" %s -> %s\n", p2, hilite(target, pos, storePathHashLen)); | ||
} | ||
} | ||
}; | ||
|
||
recurse(path); | ||
} | ||
} | ||
}; | ||
|
||
static RegisterCommand r1(make_ref<CmdWhyDepends>()); |