diff --git a/srcxray.py b/srcxray.py index f7b85d7..4727412 100755 --- a/srcxray.py +++ b/srcxray.py @@ -33,6 +33,7 @@ import difflib import glob from pathlib import * import pygraphviz # python3-pygraphviz +import graphviz import unittest import types from xml.dom.minidom import parse @@ -857,6 +858,10 @@ def write_dot(g, dot): return s + if isinstance(g, graphviz.Digraph): + g.save(dot) + print(dot) + return dot = str(dot) dot = open(dot, 'w') dot.write('strict digraph "None" {\n') @@ -865,7 +870,8 @@ def write_dot(g, dot): dot.write('node [fontname=Ubuntu,shape=none];\n') # dot.write('edge [width=10000];\n') dot.write('edge [width=1];\n') - g.remove_nodes_from(ignores) + if isinstance(g, nx.DiGraph): + g.remove_nodes_from(ignores) ranks = collections.defaultdict(list) for n in g.nodes(): r = rank(g, n) @@ -921,7 +927,7 @@ def read_dot(dot): dg.add_edges_from([(m.group(1), b.strip('"')) for b in m.group(2).split() if b != m.group(1)]) else: - m = re.match('"?([^"]+)"? -> "?([^"]*)"?;', a) + m = re.match('"?([^"]+)"? -> "?([^"]*)"?;?', a) if m: if m.group(1) != m.group(2): dg.add_edge(m.group(1), m.group(2)) @@ -1165,10 +1171,11 @@ def dir_tree(path='.'): return g -def doxygen(*sources): +def doxygen(*sources, output_dir='xml2'): ''' extracts call graph from sources with doxygen Ex: *.c + Ex2: "doxygen('init','xml2')" ''' log(' '.join([i for i in sources])) p = run(['doxygen', '-'], stdout=PIPE, @@ -1194,8 +1201,13 @@ def doxygen(*sources): GENERATE_LATEX = NO #QUIET = NO GENERATE_XML=YES - XML_OUTPUT=xml2""", encoding='ascii') - write_dot(doxygen_xml('xml2'), 'doxygen.dot') + MACRO_EXPANSION = YES + PREDEFINED = "DECLARE_COMPLETION(work)=struct completion work" \\ + "__initdata= " + XML_OUTPUT=""" + output_dir + """ + """, encoding='ascii') + print(output_dir) + #write_dot(doxygen_xml(output_dir), 'doxygen.dot') def doxygen_xml(a): @@ -1213,7 +1225,7 @@ def doxygen_xml(a): file = (m.getElementsByTagName("location")[0] .getAttribute('file')) if file not in files: - print(file) + log(file) if n == 'main': n = file + '::' + n files[file].append(n) @@ -1226,6 +1238,46 @@ def doxygen_xml(a): return g +def doxygen_xml_files(a): + ''' + extracts call graph from xml directory generated by doxygen + with files as clusters + Ex2: \"write_dot(doxygen_xml_files('xml'), 'doxygen.dot')\" + ''' + g = graphviz.Digraph() + g.attr('graph', rankdir='LR', concentrate='true', ranksep='4') + g.attr('node', shape='plaintext') + clusters = collections.defaultdict(list) + edges = list() + for x in list(glob.glob(os.path.join(a, "*.xml")) + [a]): + # print(x) + if os.path.isfile(x): + d = xml.dom.minidom.parse(x) + for m in d.getElementsByTagName("memberdef"): + n = m.getElementsByTagName("name")[0].firstChild.data + file = (m.getElementsByTagName("location")[0] + .getAttribute('file')) + if file not in files: + log(file) + if n == 'main': + n = file + '::' + n + files[file].append(n) + if not clusters.get(file): + clusters[file] = g.subgraph( + name='cluster_' + file.replace('.', '_8')) + clusters[file].graph.attr( + 'graph', label=file, fontsize="50") + with clusters[file] as c: + c.node(n) + for r in m.getElementsByTagName("references"): + edges.append((n, r.firstChild.data)) + for r in m.getElementsByTagName("ref"): + edges.append((n, r.firstChild.data)) + for (a, b) in edges: + g.edge(a, b.replace('::', '__')) + return g + + def doxygen_length(a): ''' calculates length of functions using doxygen xml @@ -1299,7 +1351,15 @@ class _unittest_autotest(unittest.TestCase): 'srcxray.py "nx.DiGraph([{1,2},{2,3},{2,4}])"')[-1], "\t\t4.*") os.system('srcxray.py doxygen init') - self.assertTrue(read_dot("doxygen.dot").number_of_edges() > 400) + os.system( + "srcxray.py \"write_dot(doxygen_xml('xml2'), 'call_graph_dx.dot')\"") + self.assertTrue(read_dot("call_graph_dx.dot").number_of_edges() > 400) + os.system( + "srcxray.py \"write_dot(doxygen_xml_files('xml2'), 'call_graph_dx_files.dot')\"") + self.assertTrue( + read_dot("call_graph_dx_files.dot").number_of_edges() > 400) + self.assertFalse(0 == os.system( + "grep DECLARE_COMPLETION call_graph_dx_files.dot")) def main():