conandeps: spell out each branch's path on indirect nodes in the tree

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
This commit is contained in:
Ole-Morten Duesund 2026-08-25 15:45:48 +02:00
commit a7db279fb0
3 changed files with 11 additions and 4 deletions

View file

@ -116,10 +116,10 @@ Dependency hierarchy
MyProject/conanfile.py
|-- MyDepA/1.0
| `-- boost/1.8
| `-- boost/1.8 [MyDepA -> boost]
`-- MyDepB/1.0
`-- Zigma/1.0 [MISSING: RelWithDebInfo]
`-- boost/1.8 [overrides boost/1.7 (implicit by your conanfile)]
`-- Zigma/1.0 [MyDepB -> Zigma; MISSING: RelWithDebInfo]
`-- boost/1.8 [MyDepB -> Zigma -> boost; overrides boost/1.7 (implicit by your conanfile)]
```
On a terminal the tables are coloured (green = on the remote, red = missing,
@ -135,7 +135,8 @@ package required by several others gets one line per parent (boost above is
pulled in by both MyDepA and Zigma), shortest path first. Rows are sorted
direct-first, then by path, so a branch's transitive dependencies stay
together; the dependency hierarchy section at the end shows the same
information as a tree.
information as a tree, and every indirect node there also spells out the
path of its branch so deep indentation never has to be counted.
* a remote name (e.g. `myremote`) the remote has a binary for the package_id your

View file

@ -450,6 +450,8 @@ def _tree_lines(report: Report) -> List[str]:
lines.append(prefix + branch + name + " (not in graph)")
return
tags = []
if seen: # indirect: spell out this branch's path so indentation need not be counted
tags.append(" -> ".join(seen + (name,)))
if dep.build_require:
tags.append("build-require")
ov = edge_overrides.get((parent, name))

View file

@ -337,6 +337,10 @@ def test_text_and_html_render(report, tmp_path):
assert "package wanted forced to by kind" in text
assert "libfoo/1.0 libbar/1.0 libbar/1.1 your conanfile explicit (override=True)" in text
assert "overrides libbar/1.0 (explicit" in text # tree edge annotation
# Tree lines carry the full path of their branch; direct nodes do not.
tree = text.split("Dependency hierarchy")[1]
assert re.search(r"`-- libdeep/1\.0 \[libfoo -> libbar -> libdeep\]", tree)
assert re.search(r"\|-- libfoo/1\.0\n", tree)
coloured = conandeps.render_text(report, color=True)
assert "\033[1;31mMISSING\033[0m" in coloured and "\033[32mtest\033[0m" in coloured