#!/usr/bin/env python3 import sys from os.path import basename from graphviz import Digraph class Message: def __init__(self, fname, name, attrs): self.fname = basename(fname) self.name = name if len(attrs) == 0: raise ValueError(f"message {name} has no attributes") t = attrs[0][0] if t in ["start", "end", "auxstart", "auxend", "embed", "ignore"]: self.typ = t attrs = attrs[1:] elif t == "next": self.typ = "normal" attrs = attrs else: raise ValueError(f"wrong message type in message {name}") self.next = [] for a in attrs: if a[0] == "next": self.next.append(a[1]) def __repr__(self): return f'{self.name}(type={self.typ}, fname="{self.fname}", next={self.next})' def generate_messages(files): attrs = [] msgs = {} for f in files: for line in open(f, "rt").readlines(): line = line.rstrip() if line.startswith(" * @"): attrs.append(line[4:].split(" ")) elif line.startswith("message "): name = line[8:-2] msgs[name] = Message(f, name, attrs) attrs = [] return msgs def generate_graph(msgs, fn): dot = Digraph() dot.attr(rankdir="LR") for m in msgs.values(): if m.typ == "start": dot.node(m.name, shape="box", color="blue") elif m.typ == "end": dot.node(m.name, shape="box", color="green3") elif m.typ == "auxstart": dot.node(m.name, shape="diamond", color="blue") elif m.typ == "auxend": dot.node(m.name, shape="diamond", color="green3") elif m.typ == "normal": dot.node(m.name) for m in msgs.values(): for n in m.next: dot.edge(m.name, n) dot.render(fn) def main(): proto_files = sys.argv msgs = generate_messages(proto_files) generate_graph(msgs, "proto.gv") if __name__ == "__main__": main()