Script to produce a figure with library dependencies (and detect circular libs also)
[u/mrichter/AliRoot.git] / MUON / checkDeps.py
1 #!/usr/bin/python
2
3 import sys
4 import os
5 import re
6 import string
7 import getopt
8
9 """
10 Given a directory, will look into lib*.pkg files to produce a dependency graph
11  of libraries (and DA if there are some)
12  
13 $Id$
14 """
15
16 __author__ = "L. Aphecetche aphecetc_at_in2p3_dot_fr"
17 __date__ = "April 2008"
18 __version__ = "0.3"
19
20 #_______________________________________________________________________________
21 def usage():
22   """Describe usage of script
23   """
24   print "Usage: %s [-h | --help] [-d | --debug] [--noda] directory_to_scan" % sys.argv[0]
25   sys.exit(1)
26   
27 #_______________________________________________________________________________
28 def getSourceFiles(lib):
29   """Extract the list of classes from a libXXX.pkg file
30   """
31
32   f = open(lib)
33   sourcefiles = []
34   for line in f:
35     l = line.strip()
36     if re.search('Ali',l) and re.search('.cxx',l):
37       l = re.sub('SRCS',' ',l)
38       l = re.sub(':=',' ',l)
39       l = re.sub('=',' ',l)
40       l = re.sub("\\\\",' ',l)
41       l = re.sub(".cxx",' ',l)
42       for i in l.split():
43         sourcefiles.append(i)
44   f.close()
45   return sourcefiles
46   
47 #_______________________________________________________________________________
48 def getIncludeFiles(srcfile):
49   """Extract the list of included classes from a class
50   """
51
52   includes = []
53
54   try:
55     f = open("%s.cxx" % srcfile)
56   except:
57     print "Could not open file %s.cxx" % srcfile
58     return includes
59     
60   for line in f:
61     line = line.strip()
62     if re.search("^#",line) and re.search("#include",line) and re.search('Ali',line):
63       line = re.sub("#include",' ',line)
64       i = line.index(".h")
65       line = line[:i]
66       line = re.sub("\"",' ',line)
67       line = line.strip()
68       includes.append(line)
69   f.close()
70   return includes
71   
72 #_______________________________________________________________________________
73 def unique(list):
74   """Extract a unique list from list
75   """
76   d = {}
77   for l in list:
78     d[l] = 1
79   return d.keys()
80   
81 #_______________________________________________________________________________
82 def findLibrary(file,allfiles):
83   """Find in which library a given class is defined
84   """
85   for k,v in allfiles.items():
86     for f in v:
87       a,f = os.path.split(f)
88       if file == f:
89         return k
90   return "external"
91   
92 #_______________________________________________________________________________
93 def shorten(libname):
94   """From libMUONxxx.pkg to xxx
95   """
96   s = libname
97   if re.search("libMUON",libname):
98     s = re.sub("libMUON","",s)
99     s = re.sub("\.pkg","",s)
100   return s
101   
102 #_______________________________________________________________________________
103 #_______________________________________________________________________________
104 #_______________________________________________________________________________
105 def main():
106
107   debug = False
108   noda = False
109   
110   try:
111     opts, args = getopt.getopt(sys.argv[1:],"hd",["help", "debug","noda"])
112   except getopt.GetoptError:
113     print "Error in options"
114     usage()
115
116   for o, a in opts:
117     if o in ( "-d","--debug" ):
118       debug = True
119     elif o in ( "-h","--help" ):
120       usage()
121       sys.exit()
122     elif o == "--noda":
123       noda = True
124     else:
125       assert False, "unhandled option"
126
127   dir = args[0]
128   
129   # find the libraries defined in this directory (looking for libXXXX.pkg files)  
130   libraries = []
131   
132   for file in os.listdir(dir):
133     if re.search('^lib',file) and re.search('.pkg$',file):
134       libraries.append(file)
135
136   # append fake libraries for DAs
137   if not noda:
138     libraries.append("libMUONTRKda.pkg")
139     libraries.append("libMUONTRGda.pkg")
140   
141   # srcfiles is a dictionary :
142   # srcfiles[libXXX.pkg] -> { list of classes (inferred from list of .cxx files) }
143   #
144   srcfiles = {}
145   
146   # allfiles is a dictonary :
147   # allfiles[libXXX.pkg] -> { list of all included files of that library }
148   allfiles = {}
149   
150   for lib in libraries:
151     if not re.search("da",lib):
152       # handle the special case of DAs which are not part of libs, really
153       srcfiles[lib] = getSourceFiles(lib)
154     else:
155       l = lib
156       l = re.sub("lib","",l)
157       l = re.sub("\.pkg","",l)
158       srcfiles[lib] = [ l ]
159     files = []
160     for src in srcfiles[lib]:
161       files.extend(getIncludeFiles(src))
162     allfiles[lib] = unique(files)
163   
164   if debug:
165     for lib in libraries:
166       print lib
167       for f in allfiles[lib]:
168         l = findLibrary(f,srcfiles)
169         print "   ",f,"(",l,")"
170       print
171       
172   # deps is a dictionary
173   # deps[libXXX.pkg] -> { list of libraries libXXX.pkg directly depends upon }
174   
175   deps = {}
176   
177   for lib,files in allfiles.items():
178     d = []
179     for f in files:
180       l = findLibrary(f,srcfiles)
181       if l != lib:
182         d.append(l)
183     deps[lib] = unique(d)
184   
185   if debug:
186     for lib in deps:
187       print lib, " depends on "
188       for l in deps[lib]:
189         print "   ",l
190       print
191         
192   ofile = "%s.dot" % os.path.splitext(os.path.basename(sys.argv[0]))[0]
193   
194   f = open(ofile,"w")
195   
196   f.write("digraph G {")
197   f.write("rankdir=BT;")
198
199   for l,d in deps.items():
200     for dl in d:
201       if re.search("MUON",dl):
202         f.write("node [shape=box,style=filled,fillcolor=yellow];")
203       else:
204         f.write("node [shape=ellipse,style=filled,fillcolor=lightgray];")
205       f.write("%s -> %s;\n" %(shorten(l),shorten(dl)))
206       
207   f.write("}")
208     
209   print "You should now do :"
210   print "tred %s > %s.bis" % ( ofile, ofile )
211   print "dot -Tpng %s.bis > %s.png" % (ofile,ofile)
212   
213 if __name__ == "__main__":
214   main()
215