From 678301072f05b650dc15c5edb4c25f08f0d6cace Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 Jan 2020 14:01:46 +0100 Subject: [PATCH] nix flake list-inputs: Pretty-print the tree --- src/libutil/util.hh | 34 ++++++++++++++++++++++++++++++++++ src/nix-store/nix-store.cc | 15 +++++---------- src/nix/flake.cc | 17 +++++++++++------ src/nix/why-depends.cc | 5 ----- tests/dependencies.sh | 8 ++++---- 5 files changed, 54 insertions(+), 25 deletions(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 2f1613ac1..5d44b92c3 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -458,6 +458,13 @@ void ignoreException(); #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' is true, all ANSI escape sequences are filtered out. Otherwise, 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); +// A Rust/Python-like enumerate() iterator adapter. +// Borrowed from http://reedbeta.com/blog/python-like-enumerate-in-cpp17. +template ())), + typename = decltype(std::end(std::declval()))> +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(iterable) }; +} + + } diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 45e152c47..9336baa83 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -229,12 +229,6 @@ static StorePathSet maybeUseOutputs(const StorePath & storePath, bool useOutput, /* Some code to print a tree representation of a derivation dependency graph. Topological sorting is used to keep the tree relatively flat. */ - -const string treeConn = "+---"; -const string treeLine = "| "; -const string treeNull = " "; - - static void printTree(const StorePath & path, const string & firstPad, const string & tailPad, StorePathSet & done) { @@ -254,10 +248,11 @@ static void printTree(const StorePath & path, auto sorted = store->topoSortPaths(info->references); reverse(sorted.begin(), sorted.end()); - for (auto i = sorted.begin(); i != sorted.end(); ++i) { - auto j = i; ++j; - printTree(*i, tailPad + treeConn, - j == sorted.end() ? tailPad + treeNull : tailPad + treeLine, + for (const auto &[n, i] : enumerate(sorted)) { + bool last = n + 1 == sorted.size(); + printTree(i, + tailPad + (last ? treeLast : treeConn), + tailPad + (last ? treeNull : treeLine), done); } } diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 5450f1fb5..9b23570bd 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -193,22 +193,27 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON { auto flake = lockFlake(); + stopProgressBar(); + if (json) std::cout << ((LockedInputs &) flake.lockFile).toJson() << "\n"; else { std::cout << fmt("%s\n", flake.flake.resolvedRef); - std::function recurse; + std::function recurse; - recurse = [&](const LockedInputs & inputs, size_t depth) + recurse = [&](const LockedInputs & inputs, const std::string & prefix) { - for (auto & input : inputs.inputs) { - std::cout << fmt("%s%s: %s\n", std::string(depth * 2, ' '), input.first, input.second.ref); - recurse(input.second, depth + 1); + for (const auto & [i, input] : enumerate(inputs.inputs)) { + //auto tree2 = tree.child(i + 1 == inputs.inputs.size()); + 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, ""); } } }; diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index c539adb7f..fb12d5380 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -143,11 +143,6 @@ struct CmdWhyDepends : SourceExprCommand and `dependency`. */ std::function printNode; - const string treeConn = "╠═══"; - const string treeLast = "╚═══"; - const string treeLine = "║ "; - const string treeNull = " "; - struct BailOut { }; printNode = [&](Node & node, const string & firstPad, const string & tailPad) { diff --git a/tests/dependencies.sh b/tests/dependencies.sh index df204d185..93b681f18 100644 --- a/tests/dependencies.sh +++ b/tests/dependencies.sh @@ -6,7 +6,7 @@ drvPath=$(nix-instantiate dependencies.nix) echo "derivation is $drvPath" -nix-store -q --tree "$drvPath" | grep ' +---.*builder1.sh' +nix-store -q --tree "$drvPath" | grep '║ ╚═══.*builder1.sh' # Test Graphviz graph generation. nix-store -q --graph "$drvPath" > $TEST_ROOT/graph @@ -22,9 +22,9 @@ nix-store -q --graph "$outPath" > $TEST_ROOT/graph if test -n "$dot"; then # Does it parse? $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" @@ -49,4 +49,4 @@ nix-store -q --referrers-closure "$input2OutPath" | grep "$outPath" # Check that the derivers are set properly. test $(nix-store -q --deriver "$outPath") = "$drvPath" -nix-store -q --deriver "$input2OutPath" | grep -q -- "-input-2.drv" +nix-store -q --deriver "$input2OutPath" | grep -q -- "-input-2.drv"