nix flake list-inputs: Pretty-print the tree

This commit is contained in:
Eelco Dolstra 2020-01-31 14:01:46 +01:00
parent a6e2b6b360
commit 678301072f
5 changed files with 54 additions and 25 deletions

View file

@ -458,6 +458,13 @@ void ignoreException();
#define ANSI_BLUE "\e[34;1m" #define ANSI_BLUE "\e[34;1m"
/* Tree formatting. */
constexpr char treeConn[] = "╠═══";
constexpr char treeLast[] = "╚═══";
constexpr char treeLine[] = "";
constexpr char treeNull[] = " ";
/* Truncate a string to 'width' printable characters. If 'filterAll' /* Truncate a string to 'width' printable characters. If 'filterAll'
is true, all ANSI escape sequences are filtered out. Otherwise, is true, all ANSI escape sequences are filtered out. Otherwise,
some escape sequences (such as colour setting) are copied but not some escape sequences (such as colour setting) are copied but not
@ -583,4 +590,31 @@ extern PathFilter defaultPathFilter;
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode);
// A Rust/Python-like enumerate() iterator adapter.
// Borrowed from http://reedbeta.com/blog/python-like-enumerate-in-cpp17.
template <typename T,
typename TIter = decltype(std::begin(std::declval<T>())),
typename = decltype(std::end(std::declval<T>()))>
constexpr auto enumerate(T && iterable)
{
struct iterator
{
size_t i;
TIter iter;
bool operator != (const iterator & other) const { return iter != other.iter; }
void operator ++ () { ++i; ++iter; }
auto operator * () const { return std::tie(i, *iter); }
};
struct iterable_wrapper
{
T iterable;
auto begin() { return iterator{ 0, std::begin(iterable) }; }
auto end() { return iterator{ 0, std::end(iterable) }; }
};
return iterable_wrapper{ std::forward<T>(iterable) };
}
} }

View file

@ -229,12 +229,6 @@ static StorePathSet maybeUseOutputs(const StorePath & storePath, bool useOutput,
/* Some code to print a tree representation of a derivation dependency /* Some code to print a tree representation of a derivation dependency
graph. Topological sorting is used to keep the tree relatively graph. Topological sorting is used to keep the tree relatively
flat. */ flat. */
const string treeConn = "+---";
const string treeLine = "| ";
const string treeNull = " ";
static void printTree(const StorePath & path, static void printTree(const StorePath & path,
const string & firstPad, const string & tailPad, StorePathSet & done) const string & firstPad, const string & tailPad, StorePathSet & done)
{ {
@ -254,10 +248,11 @@ static void printTree(const StorePath & path,
auto sorted = store->topoSortPaths(info->references); auto sorted = store->topoSortPaths(info->references);
reverse(sorted.begin(), sorted.end()); reverse(sorted.begin(), sorted.end());
for (auto i = sorted.begin(); i != sorted.end(); ++i) { for (const auto &[n, i] : enumerate(sorted)) {
auto j = i; ++j; bool last = n + 1 == sorted.size();
printTree(*i, tailPad + treeConn, printTree(i,
j == sorted.end() ? tailPad + treeNull : tailPad + treeLine, tailPad + (last ? treeLast : treeConn),
tailPad + (last ? treeNull : treeLine),
done); done);
} }
} }

View file

@ -193,22 +193,27 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON
{ {
auto flake = lockFlake(); auto flake = lockFlake();
stopProgressBar();
if (json) if (json)
std::cout << ((LockedInputs &) flake.lockFile).toJson() << "\n"; std::cout << ((LockedInputs &) flake.lockFile).toJson() << "\n";
else { else {
std::cout << fmt("%s\n", flake.flake.resolvedRef); std::cout << fmt("%s\n", flake.flake.resolvedRef);
std::function<void(const LockedInputs & inputs, size_t depth)> recurse; std::function<void(const LockedInputs & inputs, const std::string & prefix)> recurse;
recurse = [&](const LockedInputs & inputs, size_t depth) recurse = [&](const LockedInputs & inputs, const std::string & prefix)
{ {
for (auto & input : inputs.inputs) { for (const auto & [i, input] : enumerate(inputs.inputs)) {
std::cout << fmt("%s%s: %s\n", std::string(depth * 2, ' '), input.first, input.second.ref); //auto tree2 = tree.child(i + 1 == inputs.inputs.size());
recurse(input.second, depth + 1); bool last = i + 1 == inputs.inputs.size();
std::cout << fmt("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s\n",
prefix + (last ? treeLast : treeConn), input.first, input.second.ref);
recurse(input.second, prefix + (last ? treeNull : treeLine));
} }
}; };
recurse(flake.lockFile, 1); recurse(flake.lockFile, "");
} }
} }
}; };

View file

@ -143,11 +143,6 @@ struct CmdWhyDepends : SourceExprCommand
and `dependency`. */ and `dependency`. */
std::function<void(Node &, const string &, const string &)> printNode; std::function<void(Node &, const string &, const string &)> printNode;
const string treeConn = "╠═══";
const string treeLast = "╚═══";
const string treeLine = "";
const string treeNull = " ";
struct BailOut { }; struct BailOut { };
printNode = [&](Node & node, const string & firstPad, const string & tailPad) { printNode = [&](Node & node, const string & firstPad, const string & tailPad) {

View file

@ -6,7 +6,7 @@ drvPath=$(nix-instantiate dependencies.nix)
echo "derivation is $drvPath" echo "derivation is $drvPath"
nix-store -q --tree "$drvPath" | grep ' +---.*builder1.sh' nix-store -q --tree "$drvPath" | grep '║ ╚═══.*builder1.sh'
# Test Graphviz graph generation. # Test Graphviz graph generation.
nix-store -q --graph "$drvPath" > $TEST_ROOT/graph nix-store -q --graph "$drvPath" > $TEST_ROOT/graph
@ -24,7 +24,7 @@ if test -n "$dot"; then
$dot < $TEST_ROOT/graph $dot < $TEST_ROOT/graph
fi fi
nix-store -q --tree "$outPath" | grep '+---.*dependencies-input-2' nix-store -q --tree "$outPath" | grep '═══.*dependencies-input-2'
echo "output path is $outPath" echo "output path is $outPath"