Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
1 changed file
with
133 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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>()); |