diff --git a/contrib/bst-graph b/contrib/bst-graph index 3fe93e1ff..63c73bef6 100755 --- a/contrib/bst-graph +++ b/contrib/bst-graph @@ -29,6 +29,7 @@ installed. import argparse import subprocess import re +import urllib.parse from graphviz import Digraph from ruamel.yaml import YAML @@ -55,6 +56,22 @@ def parse_args(): return parser.parse_args() +def unique_node_name(s): + '''Generate unique node name for `s`. + + Graphviz node names cannot contain colons or backslashes so we use + url-encoding to generate the unique node name. (A cryptographic hash could + be used instead but that would make the graphviz file less readable.) + + Args: + s: element name + + Returns: + A string containing the unique node name + ''' + return urllib.parse.quote_plus(s) + + def parse_graph(lines): '''Return nodes and edges of the parsed grpah. @@ -99,12 +116,12 @@ def generate_graph(nodes, build_deps, runtime_deps): A graphviz.Digraph object ''' graph = Digraph() - for node in nodes: - graph.node(node) + for name in nodes: + graph.node(unique_node_name(name), label=name) for source, target in build_deps: - graph.edge(source, target, label='build-dep') + graph.edge(unique_node_name(source), unique_node_name(target), label='build-dep') for source, target in runtime_deps: - graph.edge(source, target, label='runtime-dep') + graph.edge(unique_node_name(source), unique_node_name(target), label='runtime-dep') return graph @@ -116,9 +133,11 @@ def main(): graph_lines = subprocess.check_output(cmd, universal_newlines=True) # NOTE: We generate nodes and edges before giving them to graphviz as # the library does not de-deuplicate them. - nodes, build_deps, runtime_deps = parse_graph(re.split("\|\|", graph_lines)) + nodes, build_deps, runtime_deps = parse_graph(re.split(r"\|\|", graph_lines)) graph = generate_graph(nodes, build_deps, runtime_deps) + print(graph.source) + if args.format: graph.render(cleanup=True, filename='bst-graph',