v0.3.0
Large trees used to take tens of seconds — or never finish. This release makes
them near-instant and reworks the interface around them.
Performance
-
Dependency trees are linear, not exponential. A ROS dependency graph is a
DAG:rcutilssits under nearly every branch. Expanding every path separately
produced tens of thousands of duplicate nodes. Each package is now expanded once,
where it first appears, and marked↩ see aboveelsewhere — the same convention
cargo treeuses. Measured on a 135-package install space plus a 24-package
source workspace, resolvingnav2_bringupwith-r:depth before after 5 4,152 nodes / 1.02 s 475 nodes / 0.02 s 6 18,273 nodes / 4.44 s 548 nodes / 0.02 s 7 68,081 nodes / 17.41 s 552 nodes / 0.02 s unlimited (the CLI default) did not finish in 4 minutes 552 nodes / 0.02 s Pass
--fulltorostree treefor the old fully-expanded behaviour. -
Package discovery happens once. New
rostree.core.index.PackageIndexscans
every install prefix and source tree a single time and resolves names from memory.
Previously every node of a tree could trigger a fresh recursiveos.walk. -
package.xml parsing is memoized by path, mtime and size.
-
Source scans prune
build/,install/,log/, VCS metadata and directories
markedCOLCON_IGNORE/AMENT_IGNORE, and stop descending once a manifest is found. -
rostree graphresolves the whole DAG in one breadth-first pass instead of
building a separate full tree per package.
Added
rostree why <package> <dependency>— shortest dependency paths between two
packages, answering "why is this in my tree at all?".rostree rdeps <package>— reverse dependency lookup, with--transitive
and--workspace-only.rostree check— reports dependency cycles and unresolved dependencies and
exits non-zero, so it can gate CI.rostree list --filter TEXTto narrow the package list.rostree tree --full,--expand-repeats,--max-nodesand-vfor descriptions.--no-coloron all commands (NO_COLORis honoured too).NodeStatusenum onDependencyNode(ok,repeat,cycle,missing,
parse_error,truncated) plusis_error/is_placeholder, replacing string
markers stuffed intodescription.to_dict()now includesstatus.- Public API:
build_graph(),reverse_dependencies(),get_index(),tree_stats(). - TUI: live filter over every package, reverse-dependency view (
v), dependency
scope toggle (t), and a help screen (?).
Fixed
- Graphs no longer drop edges to unresolved packages. Dependencies without a
manifest are drawn dashed and grey instead of being silently removed, which used
to leave workspace graphs as a field of unconnected boxes.--hide-missing
restores the old behaviour. rostree graph -w PATHnow works on a workspace that is not sourced — the
workspace's own packages are added to the search path.- Unbuilt packages in a sourced workspace are found again. The source root was
derived as<ws>/install/srcinstead of<ws>/src, sosrc-only packages were
invisible whenever the workspace was discovered throughAMENT_PREFIX_PATHor
COLCON_PREFIX_PATH. Both the merged (<ws>/install) and isolated
(<ws>/install/<pkg>) colcon layouts are now handled. - Packages named
lib*are no longer discarded.libstatistics_collectorand
libyaml_vendorare real ROS 2 packages; only dashed rosdep keys
(libboost-dev,python3-numpy) are treated as system dependencies, and those
are now kept onPackageInfo.system_dependenciesrather than dropped. - The TUI no longer freezes while a tree is built: resolution runs on a worker
thread and rows are created only as nodes are expanded. - The TUI package list is no longer capped at 80 entries per source.
- Text trees use correct box-drawing characters (
└──for last children, proper
vertical guides); previously every child was drawn as├──. - An unknown package name is now an error with suggestions and a non-zero exit,
instead of a one-node tree and exit 0. - The five duplicated hand-rolled
<name>XML scrapers were replaced by a single
quick_package_name()helper. - Worker results in the TUI are routed to the callback that asked for them, rather
than to whichever handler saw the completion event first. ↩ see abovenow always refers to something printed earlier. Expansion was
chosen by shortest distance from the root while the tree renders depth-first, so
a back-reference could point at a subtree printed below it. Each package is now
expanded where the tree first prints it, which also removes a whole breadth-first
pass from tree building.- Reverse dependencies were cached without keying on the dependency tag set, so a
runtime-only lookup and a full lookup returned whichever ran first. rostree why <pkg> <pkg>reported a dependency path for a package that does not
exist; the validation loop skipped both arguments when they were equal.- Global flags such as
--no-colorare accepted after the subcommand too
(rostree tree rclcpp --no-colorused to be a usage error). - TUI: pressing
e(expand all) or running a search no longer duplicates every
row of an already-expanded tree. - TUI: selecting a second package while a tree is still building no longer leaves
the app pointing at a tree it never rendered. - TUI: adding a source path while a tree is open now rescans, instead of leaving
the new packages invisible. - TUI:
Escleaves the dependents view when it was opened from the package list. --openno longer goes through a shell on Windows: it usesos.startfile, so a
path containing shell metacharacters cannot be misinterpreted. External tools
(dot,open,xdg-open) are resolved to absolute paths before being executed.
Changed
rostree treegroups sibling back-references onto one line
(↩ 5 already shown above: …);--expand-repeatslists them individually.- Depth-limited nodes report how many dependencies are hidden (
… 5 more). richis now a direct dependency (it was already installed viatextual).- Lint rules are pinned in
pyproject.tomlso a new ruff release cannot change
what CI enforces.