cutekit/cutekit/graph.py
2023-12-09 13:18:48 +01:00

95 lines
3 KiB
Python

import os
from typing import Optional, cast
from . import vt100, cli, model
def view(
registry: model.Registry,
target: model.Target,
scope: Optional[str] = None,
showExe: bool = True,
showDisabled: bool = False,
):
from graphviz import Digraph # type: ignore
g = Digraph(target.id, filename="graph.gv")
g.attr("graph", splines="ortho", rankdir="BT", ranksep="1.5")
g.attr("node", shape="ellipse")
g.attr(
"graph",
label=f"<<B>{scope or 'Full Dependency Graph'}</B><BR/>{target.id}>",
labelloc="t",
)
scopeInstance = None
if scope is not None:
scopeInstance = registry.lookup(scope, model.Component)
for component in registry.iterEnabled(target):
if not component.type == model.Kind.LIB and not showExe:
continue
if (
scopeInstance is not None
and component.id != scope
and component.id not in scopeInstance.resolved[target.id].required
):
continue
if component.resolved[target.id].enabled:
fillcolor = "lightgrey" if component.type == model.Kind.LIB else "lightblue"
shape = "plaintext" if not scope == component.id else "box"
g.node(
component.id,
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.description, 40,newline='<BR/>')}>",
shape=shape,
style="filled",
fillcolor=fillcolor,
)
for req in component.requires:
g.edge(component.id, req)
for req in component.provides:
isChosen = target.routing.get(req, None) == component.id
g.edge(
req,
component.id,
arrowhead="none",
color=("blue" if isChosen else "black"),
)
elif showDisabled:
g.node(
component.id,
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.description, 40,newline='<BR/>')}<BR/><BR/><I>{vt100.wordwrap(str(component.resolved[target.id].reason), 40,newline='<BR/>')}</I>>",
shape="plaintext",
style="filled",
fontcolor="#999999",
fillcolor="#eeeeee",
)
for req in component.requires:
g.edge(component.id, req, color="#aaaaaa")
for req in component.provides:
g.edge(req, component.id, arrowhead="none", color="#aaaaaa")
g.view(filename=os.path.join(target.builddir, "graph.gv"))
@cli.command("g", "graph", "Show the dependency graph")
def _(args: cli.Args):
registry = model.Registry.use(args)
target = model.Target.use(args)
scope = cast(Optional[str], args.tryConsumeOpt("scope"))
onlyLibs = args.consumeOpt("only-libs", False) is True
showDisabled = args.consumeOpt("show-disabled", False) is True
view(registry, target, scope=scope, showExe=not onlyLibs, showDisabled=showDisabled)