why-depends: asyncify printNode

Instead of logging directly, we now write into a `Strings` set that is
referenced by the caller.

While at it, added a test-case to ensure that self-reference invocations
and --all behave properly.

Change-Id: Ib183ab8e8e90436300e1c870fb3ae8f18730abbf
This commit is contained in:
Maximilian Bosch
2025-07-07 11:44:17 +00:00
parent 1d6c8bff77
commit 57e071e815
5 changed files with 73 additions and 39 deletions
+51 -38
View File
@@ -3,6 +3,7 @@
#include "lix/libstore/fs-accessor.hh"
#include "lix/libmain/shared.hh"
#include "lix/libutil/error.hh"
#include "lix/libutil/generator.hh"
#include "lix/libutil/result.hh"
#include "why-depends.hh"
@@ -99,9 +100,6 @@ try {
co_return result::current_exception();
}
struct BailOut : BaseException
{};
auto const inf = std::numeric_limits<size_t>::max();
struct Node
{
@@ -114,29 +112,32 @@ struct Node
bool visited = false;
};
static void printNode(
struct BailOut : BaseException
{};
static kj::Promise<Result<void>> printNode(
Node & node,
const std::string & firstPad,
const std::string & tailPad,
bool all,
bool precise,
Store & store,
AsyncIoRoot & aio,
const StorePath & packagePath,
const StorePath & dependencyPath,
std::map<StorePath, Node> & graph
std::map<StorePath, Node> & graph,
Strings & output
)
{
try {
auto pathS = store.printStorePath(node.path);
assert(node.dist != inf);
if (precise) {
logger->cout(
"%s%s%s%s" ANSI_NORMAL,
firstPad,
node.visited ? "\e[38;5;244m" : "",
firstPad != "" ? "" : "",
pathS
output.push_back(
fmt("%s%s%s%s" ANSI_NORMAL,
firstPad,
node.visited ? "\e[38;5;244m" : "",
firstPad != "" ? "" : "",
pathS)
);
}
@@ -145,8 +146,9 @@ static void printNode(
}
if (node.visited) {
return;
co_return result::success();
}
if (precise) {
node.visited = true;
}
@@ -175,7 +177,7 @@ static void printNode(
// FIXME: should use scanForReferences().
if (precise) {
hits = aio.blockOn(visitPath(
hits = TRY_AWAIT(visitPath(
pathS, store, pathS, dependencyPath.hashPart(), hashes, packagePath, dependencyPath
));
}
@@ -187,11 +189,11 @@ static void printNode(
for (auto & hit : hits[hash]) {
bool first = hit == *hits[hash].begin();
logger->cout(
"%s%s%s",
tailPad,
(first ? (last ? treeLast : treeConn) : (last ? treeNull : treeLine)),
hit
output.push_back(
fmt("%s%s%s",
tailPad,
(first ? (last ? treeLast : treeConn) : (last ? treeNull : treeLine)),
hit)
);
if (!all) {
break;
@@ -200,29 +202,33 @@ static void printNode(
if (!precise) {
auto pathS = store.printStorePath(ref.second->path);
logger->cout(
"%s%s%s%s" ANSI_NORMAL,
firstPad,
ref.second->visited ? "\e[38;5;244m" : "",
last ? treeLast : treeConn,
pathS
output.push_back(
fmt("%s%s%s%s" ANSI_NORMAL,
firstPad,
ref.second->visited ? "\e[38;5;244m" : "",
last ? treeLast : treeConn,
pathS)
);
node.visited = true;
}
printNode(
TRY_AWAIT(printNode(
*ref.second,
tailPad + (last ? treeNull : treeLine),
tailPad + (last ? treeNull : treeLine),
all,
precise,
store,
aio,
packagePath,
dependencyPath,
graph
);
graph,
output
));
}
co_return result::success();
} catch (...) {
co_return result::current_exception();
}
struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
@@ -356,23 +362,30 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
'dependency'). Print every edge on a path between `package`
and `dependency`. */
RunPager pager;
if (!precise) {
logger->cout("%s", store->printStorePath(graph.at(packagePath).path));
}
Strings output;
try {
if (!precise) {
logger->cout("%s", store->printStorePath(graph.at(packagePath).path));
}
printNode(
aio().blockOn(printNode(
graph.at(packagePath),
"",
"",
all,
precise,
*store,
aio(),
packagePath,
dependencyPath,
graph
);
} catch (BailOut & ) { }
graph,
output
));
} catch (BailOut &) {
}
for (auto & l : output) {
logger->cout("%s", l);
}
}
};
@@ -5,6 +5,7 @@ mkdir $out
echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
ln -s $input2 $out/reference-to-input-2
ln -s $input3 $out/reference-to-input-3
# Self-reference.
ln -s $out $out/self
+8
View File
@@ -20,9 +20,15 @@ let
head -c 100k /dev/zero > $out/filler
echo BAR > $out/bar
echo ${input0} > $out/input0
echo ${input3} > $out/input3
'';
};
input3 = mkDerivation {
name = "dependencies-input-3";
buildCommand = "mkdir $out; echo FOO > $out/foo";
};
fod_input = mkDerivation {
name = "fod-input";
buildCommand = ''
@@ -40,8 +46,10 @@ mkDerivation {
builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
input1 = input1 + "/.";
input2 = "${input2}/.";
input3 = "${input3}/.";
input1_drv = input1;
input2_drv = input2;
input3_drv = input3;
input0_drv = input0;
fod_input_drv = fod_input;
meta.description = "Random test package";
+1 -1
View File
@@ -11,7 +11,7 @@ checkRef() {
outPath=$(nix-build ./export-graph.nix -A 'foo."bar.runtimeGraph"' -o $TEST_ROOT/result)
test $(nix-store -q --references $TEST_ROOT/result | wc -l) = 3 || fail "bad nr of references"
test $(nix-store -q --references $TEST_ROOT/result | wc -l) = 4 || fail "bad nr of references"
checkRef input-2
for i in $(cat $outPath); do checkRef $i; done
+12
View File
@@ -10,6 +10,7 @@ nix why-depends --derivation --file ./dependencies.nix input2_drv input1_drv
nix why-depends --file ./dependencies.nix input2_drv input1_drv
nix-build ./dependencies.nix -A input0_drv -o dep
nix-build ./dependencies.nix -A input3_drv -o dep3
nix-build ./dependencies.nix -o toplevel
FAST_WHY_DEPENDS_OUTPUT=$(nix why-depends ./toplevel ./dep)
@@ -27,3 +28,14 @@ echo "$PRECISE_WHY_DEPENDS_OUTPUT" | grepQuiet reference-to-input-2
<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '3p' | grep " →" | grepQuiet "dependencies-input-2"
<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '4p' | grepQuiet " └───input0: …" # in input-2, file input0
<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '5p' | grep " →" | grepQuiet "dependencies-input-0" # is dependencies-input-0 referenced
WHY_DEPENDS_SELF_REF="$(nix why-depends ./toplevel ./toplevel)"
<<<"$WHY_DEPENDS_SELF_REF" sed -n '1p' | grepQuiet "dependencies-top"
<<<"$WHY_DEPENDS_SELF_REF" sed -n '2p' | grep "└─" | grepQuiet "dependencies-top"
test -z "$(<<<"$WHY_DEPENDS_SELF_REF" sed -n '3p')"
WHY_DEPENDS_ALL="$(nix why-depends ./toplevel ./dep3 --all)"
<<<"$WHY_DEPENDS_ALL" sed -n '1p' | grep -v "└─" | grepQuiet "dependencies-top"
<<<"$WHY_DEPENDS_ALL" sed -n '2p' | grep "├─" | grepQuiet "input-3"
<<<"$WHY_DEPENDS_ALL" sed -n '3p' | grep "└─" | grepQuiet "input-2"
<<<"$WHY_DEPENDS_ALL" sed -n '4p' | grep " └─" | grepQuiet "input-3"