1#! /usr/bin/python2
2import os.path
3import sys
4import shlex
5import re
6
7from headerutils import *
8
9header_roots = { }
10extra_edges = list()
11verbose = False
12verbosity = 0
13nodes = list()
14
15def unpretty (name):
16  if name[-2:] == "_h":
17    name = name[:-2] + ".h"
18  return name.replace("_", "-")
19
20def pretty_name (name):
21  name = os.path.basename (name)
22  return name.replace(".","_").replace("-","_").replace("/","_").replace("+","_");
23
24depstring = ("In file included from", "                 from")
25
26# indentation indicates nesting levels of included files
27ignore = [ "coretypes_h",
28             "insn_modes_h",
29             "signop_h",
30             "wide_int_h",
31             "wide_int_print_h",
32             "insn_modes_inline_h",
33             "machmode_h",
34             "double_int_h",
35             "real_h",
36             "fixed_value_h",
37             "hash_table_h",
38               "statistics_h",
39               "ggc_h",
40               "vec_h",
41               "hashtab_h",
42               "inchash_h",
43               "mem_stats_traits_h",
44               "hash_map_traits_h",
45               "mem_stats_h",
46               "hash_map_h",
47             "hash_set_h",
48             "input_h",
49               "line_map_h",
50             "is_a_h",
51           "system_h",
52           "config_h" ]
53
54def process_log_file (header, logfile):
55  if header_roots.get (header) != None:
56    print "Error: already processed log file: " + header + ".log"
57    return
58  hname = pretty_name (header)
59  header_roots[hname] = { }
60
61  sline = list();
62  incfrom = list()
63  newinc = True
64  for line in logfile:
65    if len (line) > 21 and line[:21] in depstring:
66      if newinc:
67        incfrom = list()
68        newinc = False
69      fn = re.findall(ur".*/(.*?):", line)
70      if len(fn) != 1:
71        continue
72      if fn[0][-2:] != ".h":
73        continue
74      n = pretty_name (fn[0])
75      if n not in ignore:
76        incfrom.append (n)
77      continue
78    newinc = True
79    note = re.findall (ur"^.*note: (.*)", line)
80    if len(note) > 0:
81      sline.append (("note", note[0]))
82    else:
83      err_msg = re.findall (ur"^.*: error: (.*)", line)
84      if len(err_msg) == 1:
85        msg = err_msg[0]
86        if (len (re.findall("error: forward declaration", line))) != 0:
87          continue
88        path = re.findall (ur"^(.*?):.*error: ", line)
89        if len(path) != 1:
90          continue
91        if path[0][-2:] != ".h":
92          continue
93        fname = pretty_name (path[0])
94        if fname in ignore or fname[0:3] == "gt_":
95          continue
96        sline.append (("error", msg, fname, incfrom))
97
98  print str(len(sline)) + " lines to process"
99  lastline = "note"
100  for line in sline:
101    if line[0] != "note" and lastline[0] == "error":
102      fname = lastline[2]
103      msg = lastline[1]
104      incfrom = lastline[3]
105      string = ""
106      ofname = fname
107      if len(incfrom) != 0:
108        for t in incfrom:
109          string = string + t + " : "
110          ee = (fname, t)
111          if ee not in extra_edges:
112            extra_edges.append (ee)
113          fname = t
114          print string
115
116      if hname not in nodes:
117        nodes.append(hname)
118      if fname not in nodes:
119        nodes.append (ofname)
120      for y in incfrom:
121        if y not in nodes:
122          nodes.append (y)
123
124
125      if header_roots[hname].get(fname) == None:
126        header_roots[hname][fname] = list()
127      if msg not in header_roots[hname][fname]:
128        print string + ofname + " : " +msg
129        header_roots[hname][fname].append (msg)
130    lastline = line;
131
132
133dotname = "graph.dot"
134graphname = "graph.png"
135
136
137def build_dot_file (file_list):
138  output = open(dotname, "w")
139  output.write ("digraph incweb {\n");
140  for x in file_list:
141    if os.path.exists (x) and x[-4:] == ".log":
142      header =  x[:-4]
143      logfile = open(x).read().splitlines()
144      process_log_file (header, logfile)
145    elif os.path.exists (x + ".log"):
146      logfile = open(x + ".log").read().splitlines()
147      process_log_file (x, logfile)
148
149  for n in nodes:
150    fn = unpretty(n)
151    label = n + " [ label = \"" + fn  + "\" ];"
152    output.write (label + "\n")
153    if os.path.exists (fn):
154      h = open(fn).read().splitlines()
155      for l in h:
156        t = find_pound_include (l, True, False)
157        if t != "":
158          t = pretty_name (t)
159          if t in ignore or t[-2:] != "_h":
160            continue
161          if t not in nodes:
162            nodes.append (t)
163          ee = (t, n)
164          if ee not in extra_edges:
165            extra_edges.append (ee)
166
167  depcount = list()
168  for h in header_roots:
169    for dep in header_roots[h]:
170      label = " [ label = "+ str(len(header_roots[h][dep])) + " ];"
171      string = h + " -> " + dep + label
172      output.write (string + "\n");
173      if verbose:
174        depcount.append ((h, dep, len(header_roots[h][dep])))
175
176  for ee in extra_edges:
177    string = ee[0] + " -> " + ee[1] + "[ color=red ];"
178    output.write (string + "\n");
179
180
181  if verbose:
182    depcount.sort(key=lambda tup:tup[2])
183    for x in depcount:
184      print " ("+str(x[2])+ ") : " + x[0] + " -> " + x[1]
185      if (x[2] <= verbosity):
186        for l in header_roots[x[0]][x[1]]:
187          print "            " + l
188
189  output.write ("}\n");
190
191
192files = list()
193dohelp = False
194edge_thresh = 0
195for arg in sys.argv[1:]:
196  if arg[0:2] == "-o":
197    dotname = arg[2:]+".dot"
198    graphname = arg[2:]+".png"
199  elif arg[0:2] == "-h":
200    dohelp = True
201  elif arg[0:2] == "-v":
202    verbose = True
203    if len(arg) > 2:
204      verbosity = int (arg[2:])
205      if (verbosity == 9):
206        verbosity = 9999
207  elif arg[0:1] == "-":
208    print "Unrecognized option " + arg
209    dohelp = True
210  else:
211    files.append (arg)
212
213if len(sys.argv) == 1:
214  dohelp = True
215
216if dohelp:
217  print "Parses the log files from the reduce-headers tool to generate"
218  print "dependency graphs for the include web for specified files."
219  print "Usage:  [-nnum] [-h] [-v[n]] [-ooutput] file1 [[file2] ... [filen]]"
220  print "       -ooutput : Specifies output to output.dot and output.png"
221  print "                  Defaults to 'graph.dot and graph.png"
222  print "       -vn : verbose mode, shows the number of connections, and if n"
223  print "             is specified, show the messages if # < n. 9 is infinity"
224  print "       -h : help"
225else:
226  print files
227  build_dot_file (files)
228  os.system ("dot -Tpng " + dotname + " -o" + graphname)
229
230
231