feat: spack graph --mermaid

This commit is contained in:
Wouter Deconinck
2024-05-04 16:13:12 -07:00
committed by GitHub
parent eac5ea869f
commit 23197b78f9
3 changed files with 60 additions and 20 deletions

View File

@@ -9,7 +9,7 @@
import spack.environment as ev
import spack.store
from spack.cmd.common import arguments
from spack.graph import DAGWithDependencyTypes, SimpleDAG, graph_ascii, graph_dot, static_graph_dot
from spack.graph import DotGraph, MermaidGraph, DAGWithDependencyTypes, SimpleDAG, graph_ascii, graph_dot, static_graph_dot
description = "generate graphs of package dependency relationships"
section = "basic"
@@ -33,6 +33,9 @@ def setup_parser(subparser):
method.add_argument(
"-d", "--dot", action="store_true", help="generate graph in dot format and print to stdout"
)
method.add_argument(
"-m", "--mermaid", action="store_true", help="generate graph in mermaid format and print to stdout"
)
subparser.add_argument(
"-s",
@@ -85,10 +88,14 @@ def graph(parser, args):
static_graph_dot(specs, depflag=args.deptype)
return
if args.dot:
builder = SimpleDAG()
if args.dot or args.mermaid:
if args.dot:
graph = DotGraph()
if args.mermaid:
graph = MermaidGraph()
builder = SimpleDAG(graph=graph)
if args.color:
builder = DAGWithDependencyTypes()
builder = DAGWithDependencyTypes(graph=graph)
graph_dot(specs, builder=builder, depflag=args.deptype)
return

View File

@@ -34,7 +34,7 @@
/
o boost
graph_dot() will output a graph of a spec (or multiple specs) in dot format.
graph_dot() will output a graph of a spec (or multiple specs) in dot or mermaid format.
"""
import enum
import sys
@@ -446,10 +446,27 @@ def graph_ascii(
graph.write(spec, color=color, out=out)
class DotGraphBuilder:
"""Visit edges of a graph a build DOT options for nodes and edges"""
class DotGraph:
"""Configuration for DOT graphs"""
def __init__(self):
self.label = "label="
self.template = "misc/graph.dot"
class MermaidGraph:
"""Configuration for Mermaid graphs"""
def __init__(self):
self.label = ""
self.template = "misc/graph.md"
class GraphBuilder:
"""Visit edges of a graph a build options for nodes and edges"""
def __init__(self, graph = DotGraph()):
self.graph: Union[DotGraph, MermaidGraph] = graph
self.nodes: Set[Tuple[str, str]] = set()
self.edges: Set[Tuple[str, str, str]] = set()
@@ -472,40 +489,40 @@ def edge_entry(self, edge: spack.spec.DependencySpec) -> Tuple[str, str, str]:
raise NotImplementedError("Need to be implemented by derived classes")
def context(self):
"""Return the context to be used to render the DOT graph template"""
"""Return the context to be used to render the graph template"""
result = {"nodes": self.nodes, "edges": self.edges}
return result
def render(self) -> str:
"""Return a string with the output in DOT format"""
"""Return a string with the output in format"""
environment = spack.tengine.make_environment()
template = environment.get_template("misc/graph.dot")
template = environment.get_template(self.graph.template)
return template.render(self.context())
class SimpleDAG(DotGraphBuilder):
"""Simple DOT graph, with nodes colored uniformly and edges without properties"""
class SimpleDAG(GraphBuilder):
"""Simple graph, with nodes colored uniformly and edges without properties"""
def node_entry(self, node):
format_option = "{name}{@version}{%compiler}{/hash:7}"
return node.dag_hash(), f'[label="{node.format(format_option)}"]'
return node.dag_hash(), f'[{self.graph.label}"{node.format(format_option)}"]'
def edge_entry(self, edge):
return edge.parent.dag_hash(), edge.spec.dag_hash(), None
class StaticDag(DotGraphBuilder):
"""DOT graph for possible dependencies"""
class StaticDag(GraphBuilder):
"""Graph for possible dependencies"""
def node_entry(self, node):
return node.name, f'[label="{node.name}"]'
return node.name, f'[{self.graph.label}"{node.name}"]'
def edge_entry(self, edge):
return edge.parent.name, edge.spec.name, None
class DAGWithDependencyTypes(DotGraphBuilder):
"""DOT graph with link,run nodes grouped together and edges colored according to
class DAGWithDependencyTypes(GraphBuilder):
"""Graph with link,run nodes grouped together and edges colored according to
the dependency types.
"""
@@ -521,7 +538,7 @@ def visit(self, edge):
def node_entry(self, node):
node_str = node.format("{name}{@version}{%compiler}{/hash:7}")
options = f'[label="{node_str}", group="build_dependencies", fillcolor="coral"]'
options = f'[{self.graph.label}"{node_str}", group="build_dependencies", fillcolor="coral"]'
if node.dag_hash() in self.main_unified_space:
options = f'[label="{node_str}", group="main_psid"]'
return node.dag_hash(), options
@@ -574,7 +591,7 @@ def static_graph_dot(
def graph_dot(
specs: List[spack.spec.Spec],
builder: Optional[DotGraphBuilder] = None,
builder: Optional[GraphBuilder] = None,
depflag: dt.DepFlag = dt.ALL,
out: Optional[TextIO] = None,
):

View File

@@ -0,0 +1,16 @@
flowchart TD
{% for node, node_options in nodes %}
{% if node_options %}
{{ node }}{{ node_options }}
{% else %}
{{ node }}
{% endif %}
{% endfor %}
{% for edge_parent, edge_child, edge_options in edges %}
{% if edge_options %}
{{ edge_parent }} --> {{ edge_child }}{{ edge_options }}
{% else %}
{{ edge_parent }} --> {{ edge_child }}
{% endif %}
{% endfor %}