Skip to content

Commit

Permalink
Add command "nix why-depends"
Browse files Browse the repository at this point in the history
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
edolstra committed Sep 10, 2017
1 parent 8af704e commit d41c5eb
Showing 1 changed file with 133 additions and 0 deletions.
133 changes: 133 additions & 0 deletions src/nix/why-depends.cc
@@ -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>());

0 comments on commit d41c5eb

Please sign in to comment.