Compare commits
1 Commits
develop
...
feat/merma
Author | SHA1 | Date | |
---|---|---|---|
![]() |
23197b78f9 |
@@ -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
|
||||
|
||||
|
@@ -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,
|
||||
):
|
||||
|
16
share/spack/templates/misc/graph.md
Normal file
16
share/spack/templates/misc/graph.md
Normal 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 %}
|
Reference in New Issue
Block a user