]> git.uio.no Git - u/mrichter/AliRoot.git/blob - MUON/checkDeps.py
A step forward towards accuracy : now using dep files generated during compilation...
[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
14 __author__ = "L. Aphecetche aphecetc_at_in2p3_dot_fr"
15 __version__ = "$Id$"
16
17 notassociatedfiles = {}
18
19 #_______________________________________________________________________________
20 def usage():
21   """Describe usage of script
22   """
23   print "Usage: %s [-h | --help] [-d | --debug] [--da] directory_to_scan" % sys.argv[0]
24   sys.exit(1)
25   
26 #_______________________________________________________________________________
27 def append(list,a):
28   """ append a to list, if a not there yet
29   """
30   if not a in list:
31     list.append(a)
32     
33
34 #_______________________________________________________________________________
35 def getSourceFiles(lib,rootsys,alice_root):
36   """Extract the list of files from a libXXX.pkg file
37     Return a pair of list (sourceFiles,einclude), where einclude
38     is the list of directories needed to be included compile the files.
39   """
40   
41   # list of possible .pkg variables
42   pkgkeys = [ "SRCS","EINCLUDE","HDRS","FSRCS","DHDR","CSRCS","CHDRS","ELIBS","EDEFINE","PACKFFLAGS","PACKCXXFLAGS","PACKCFLAGS","PACKSOFLAGS","EXPORT","EHDRS" ]
43   
44   keySRCS = pkgkeys[0]
45   keyEINCLUDE = pkgkeys[1]
46   
47   sourcefiles = []
48   pkg = getLibPackage(lib)
49   einclude = [ "%s/include" % rootsys, "%s/STEER"  % alice_root, "%s/%s" % (alice_root,pkg) ]
50
51   dir = os.path.dirname(lib)
52   
53   try:
54     f = open(lib)
55   except:
56     print "getSourceFiles : could not open package file %s" % lib
57     return sourcefiles, einclude
58     
59   src = False
60   
61   for line in f:
62     l = line.strip()
63     key = False
64     for k in pkgkeys:
65       if re.search(k,l):
66         key = True
67     if key:
68       if re.search("^%s" % keySRCS,l):
69         src = True
70       else:
71         src = False
72       if re.search("^%s" % keyEINCLUDE,l):
73         l = re.sub(keyEINCLUDE,' ',l)
74         l = re.sub(':',' ',l)
75         l = re.sub('=',' ',l)
76         l = re.sub('\+',' ',l)
77         a = l.split()
78         for i in a:
79           append(einclude,os.path.join(alice_root,i))
80         
81     if src:
82       if re.search('Ali',l) and ( re.search('.cxx',l) or re.search('.h',l) ):
83         l = re.sub(keySRCS,' ',l)
84         l = re.sub(':',' ',l)
85         l = re.sub('=',' ',l)
86         l = re.sub('\+',' ',l)
87         l = re.sub("\\\\",' ',l)
88         for i in l.split():
89           append(sourcefiles,os.path.join(dir,i))
90
91   f.close()
92   return sourcefiles,einclude
93   
94 #_______________________________________________________________________________
95 def getIncludeFiles2(srcfile,alice_root,alice_target,rootsys):
96   """Extract the list of included classes from a class, using the dep
97    files generated in $ALICE_ROOT/package/tgt_ALICE_TARGET/*.d files
98   It is much faster than getIncludeFile, as it reuses the output of 
99   previously preprocessing part. Drawback is that it will only work
100   on a compiled version of aliroot...
101   """
102
103   includes = []
104
105   package = getFilePackage(srcfile,alice_root)
106   file = re.sub("%s/%s" % (alice_root,package)," ",srcfile).strip()
107   if file[0] == '/':
108     file = file[1:]
109   depfile = "%s/%s/tgt_%s/%s" % (alice_root,package,alice_target,file)
110   depfile = re.sub("\.cxx",".d",depfile)
111   
112   try:
113     f = open(depfile)
114   except:
115     print "Could not open file %s" % depfile
116     print "From",srcfile
117     return includes
118     
119   for line in f:
120     line = line.strip()
121     i = line.find(":")
122     if i > 0:
123       line = line[i+1:]
124       parts = line.strip().split()
125       for p in parts:
126         if re.search(rootsys,p):
127           p = rootsys
128         else:
129           if p[0] != '/':
130             p = "%s/%s" % (alice_root,p)
131           p = re.sub("%s/include" % alice_root,"%s/STEER" % alice_root,p)
132         append(includes,p)
133
134   f.close()
135   
136   return includes
137
138 #_______________________________________________________________________________
139 def getIncludeFiles(srcfile,eincludes,rootsys):
140   """Extract the list of included classes from a class, using :
141     gcc -MM srcfile -MG 
142     and then parses the output...
143     This version is quite slow as we're (re-)doing the preprocessing of
144     all the files, but the advantage is that it'll work for a fresh checkout
145     of aliroot, i.e. even before compilation
146   """
147
148   includes = []
149
150 #  try:
151 #    f = open(srcfile)
152 #  except:
153 #    print "Could not open file %s" % srcfile
154 #    return includes
155 #    
156 #  f.close()
157   
158
159   incdir = ""
160   
161   for i in eincludes:
162     incdir = "%s -I%s" % (incdir,i)
163     
164   cmd = "gcc %s -MM %s -MG" % (incdir,srcfile)
165   
166   pre = os.popen(cmd)
167   
168   for line in pre:
169     line = line.strip()
170     line = re.sub("\\\\"," ",line)
171     i = line.find(":")
172     if i > 0:
173       line = line[i+1:]
174     line = line.strip()
175     if len(line) > 0 and line != srcfile:
176       if line.find('/') < 0:
177         print "Got no path for file",srcfile," line=",line
178         print "cmd was",cmd
179       if re.search(rootsys,line):
180         line = rootsys
181       append(includes,line)
182   pre.close()
183           
184   return includes
185   
186 #_______________________________________________________________________________
187 def unique(list):
188   """Extract a unique list from list
189   """
190   d = {}
191   for l in list:
192     d[l] = 1
193   return d.keys()
194   
195 #_______________________________________________________________________________
196 def libshorten(libname):
197   """From libYYYxxx.pkg to YYYxxx
198   """
199   
200   s = os.path.basename(libname)
201   if re.search("^lib",s):
202     s = re.sub("^lib","",s)
203     s = re.sub("\.pkg","",s)
204     
205   return s
206
207 #_______________________________________________________________________________
208 def fileshorten(file,path):
209   """From path/toto/file to toto/file
210   """
211   
212   s = re.sub(path," ",file).strip()
213   if s[0] == '/':
214     s = s[1:]
215     
216   return s
217
218 #_______________________________________________________________________________
219 def getFilePackage(file,alice_root):
220   """ Get the package in which this file is defined
221   """
222   
223   f = re.sub(alice_root,"/",file)
224   while f[0] == '/':
225     f = f[1:]
226   p = f.split('/')
227   return p[0]
228   
229 #_______________________________________________________________________________
230 def getLibPackage(libname):
231   """ Get the package in which this library is defined
232   """
233   
234   p = libname.split('/')
235   return p[len(p)-2]
236
237 #_______________________________________________________________________________
238 def tryRecover(f,inc2src,src2lib,alice_root):
239   """ This method should try to recover the "father" of file f (most probably
240   f is an include file
241   The idea would be to find a cxx file that *directly* includes f, and take
242   the lib of that cxx file as the source of f...
243   Would that work ?
244   Is it needed really ?
245   """
246   
247   """
248   print "tryRecover:",f
249   
250   if not f.find('\.h'): 
251     return ""
252     
253   p = getFilePackage(f,alice_root)
254   
255   cxxfiles = inc2src.get(f,[])
256   
257   for file in cxxfiles:
258     libs = src2lib.get(file,[])
259   
260     for l in libs:
261       pl = getLibPackage(l)
262       print f,file,p,pl
263   """
264   
265   return ""
266   
267 #_______________________________________________________________________________
268 #_______________________________________________________________________________
269 #_______________________________________________________________________________
270 def main():
271
272   # we cannot work w/o those environement variables, so check them...
273   requiredVariables = [ "ROOTSYS", "ALICE_ROOT", "ALICE_TARGET" ]
274   
275   for r in requiredVariables:
276     if not r in os.environ:
277       print "%s is not defined. Cannot work." % r
278       sys.exit(1)
279       
280   alice_root = os.environ.get("ALICE_ROOT")
281   rootsys = os.environ.get("ROOTSYS")
282   alice_target = os.environ.get("ALICE_TARGET")
283
284   debug = 0
285   noda = True
286   
287   try:
288     opts, args = getopt.getopt(sys.argv[1:],"hd",["help", "debug","da"])
289   except getopt.GetoptError:
290     print "Error in options"
291     usage()
292
293   for o, a in opts:
294     if o in ( "-d","--debug" ):
295       debug = debug + 1
296     elif o in ( "-h","--help" ):
297       usage()
298       sys.exit()
299     elif o == "--da":
300       noda = False
301     else:
302       assert False, "unhandled option"
303
304   dir = os.path.abspath(args[0])  
305   dirs = []
306   
307   for sd in os.listdir(dir):
308     ld = os.path.join(dir,sd)
309     if os.path.isdir(ld) and not os.path.islink(ld):
310       dirs.append(ld)
311
312   dirs.append(dir)
313     
314   requestedPackages = [ "MUON", "STEER", "RAW", "ITS", "TRD", "VZERO", "TPC", "PHOS", "TOF", "ZDC", "EMCAL", "HMPID", "SHUTTLE", "ACORDE" ];
315   
316   # find the libraries defined in this directory (looking for libXXXX.pkg files)  
317   libraries = []
318   
319   for d in dirs:
320     for f in os.listdir(d):
321       fulllib = os.path.join(d,f)
322       p = getLibPackage(fulllib)
323       if not p in requestedPackages:
324         continue
325       if re.search('^lib',f) and re.search('.pkg$',f):
326         libraries.append(fulllib)
327       if not noda and re.search('da.cxx',f) and not re.search('.svn',f):
328         # append fake libraries for DAs
329         tmp = re.sub("cxx","pkg",f)
330         tmp = "lib%s" % tmp
331         libraries.append(os.path.join(d,tmp))
332       
333   # from list of library files (libXXXyyy.pkg), try to find back the list of
334   # "packages" = XXX
335   packages = {}
336   
337   for l in libraries:
338     p = getLibPackage(l)
339     packages[p] = []
340     
341   for l in libraries:
342     p = getLibPackage(l)
343     packages[p].append(l)
344     
345   # src2inc[file.cxx] -> { all included files of that file }    
346   src2inc = {}
347
348   # inc2src[file.h] -> { all files that include that one }
349   inc2src = {}
350   
351   # lib2src[libXXX.pkg] -> { list of files of that library }  
352   lib2src = {}
353
354   # src2lib[file.cxx] -> { list of libraries including that file }
355   src2lib = {}
356   
357   # eincludes[libXXX.pkg] -> list of directories to be included to be able to compile the files
358   eincludes = {} 
359   
360   # lib2inc[libXXX.pkg] -> { list of all included files of that library }
361   lib2inc = {}
362   
363   # inc2lib[file.h] -> { list of libraries that include that file }
364   inc2lib = {}
365   
366   for p in packages:
367
368     print "Scanning ",p
369
370     for lib in packages[p]:
371
372       lib2inc[lib] = []
373       
374       print "  ",libshorten(lib),"..."
375
376       if not re.search("da.pkg",lib):
377         # handle the special case of DAs which are not part of libs, really
378         lib2src[lib], eincludes[lib] = getSourceFiles(lib,rootsys,alice_root)
379       else:
380         l = lib
381         l = re.sub("lib","",l)
382         l = re.sub("\.pkg","",l)
383         lib2src[lib] = [ "%s.cxx" % l ]
384         eincludes[lib] = []
385   
386       files = []
387
388       for src in lib2src[lib]:
389 #        inc = getIncludeFiles(src,eincludes[lib],rootsys)
390         inc = getIncludeFiles2(src,alice_root,alice_target,rootsys)
391         src2inc[src] = inc
392         
393         if not src in src2lib:
394           src2lib[src] = []
395           
396         append(src2lib[src],lib)
397         
398         for i in inc:
399           if not i in inc2src.keys():
400             inc2src[i] = []
401           append(inc2src[i],src)
402           append(lib2inc[lib],i)
403           if not i in inc2lib.keys():
404             inc2lib[i] = []
405           append(inc2lib[i],lib)
406   
407   # some debug at this point...
408   
409   if debug>=1:
410     for lib in libraries:
411       print lib," is made of "
412       for f in lib2src[lib]:
413         print "   ",fileshorten(f,alice_root)
414       print " and includes "
415       for h in lib2inc[lib]:
416         print "   ",fileshorten(h,alice_root)
417       if len(eincludes[lib]) > 0:
418         print " and needs the following directories to be compiled "
419         for f in eincludes[lib]:
420           print "    ",f
421       
422   if debug>=2:
423     print "src2lib relationship"
424     for src,lib in src2lib.items():
425       print fileshorten(src,alice_root),"(",
426       for l in lib:
427         print libshorten(l)," ",
428       print ")"
429       
430   # now fills the ultimate array, lib2lib
431   # lib2lib[libXXX.pkg] -> { libYYY.pkg }, list of libraries that lib depends on
432   
433   lib2lib = {}
434   
435   for lib in libraries:
436
437     lib2lib[lib] = []
438
439     for hfile in lib2inc[lib]:
440       
441       l = "external"
442       
443       # start simple : is f contains ROOTSYS, it's ROOT.
444       if re.search(rootsys,hfile):
445         l = "ROOT"
446       else:
447         # not that simple, let's try to find out...
448         cxx = re.sub("\.h",".cxx",hfile)
449         dl = src2lib.get(cxx,[])
450         if len(dl)==1:
451           l = dl[0]
452         elif len(dl)>1:
453           print "Got several libs(",len(dl),"for ",hfile,":"
454           print dl
455           
456       if l =="external":
457         notassociatedfiles[hfile] = 1
458       else:
459         append(lib2lib[lib],l)
460
461   ###################### Debug parts...
462   if debug>=1:
463     for lib in libraries:
464       print libshorten(lib),"depends on"
465       for f in lib2lib[lib]:
466         print "   ",libshorten(f)
467   
468   if debug>=2:
469       print 
470       print "From source files to include files : "
471       for cxxfile, hfile in src2inc.items():
472         print fileshorten(cxxfile,alice_root)
473         for h in hfile:
474           print "    ",fileshorten(h,alice_root)
475
476   if debug>=3:
477       print
478       print "From include files to source files : "
479       for i,sources in inc2src.items():
480         print fileshorten(i,alice_root), len(sources)
481         for s in sources:
482           print "   ",fileshorten(s,alice_root),"(",
483           for l in src2lib[s]:
484             print libshorten(l),
485           print ")"
486   ###################### 
487   
488   if len(notassociatedfiles) > 0:
489     print "The following files could not be associated with any library..."
490     for f in notassociatedfiles.keys():
491       print f,
492       t=tryRecover(f,inc2src,src2lib,alice_root)
493       print t
494       
495   # output the dot file that will have to be processed by the dot program
496   
497   ofile = "%s.dot" % os.path.splitext(os.path.basename(sys.argv[0]))[0]
498   
499   f = open(ofile,"w")
500   
501   f.write("digraph G {\n")
502   f.write("rankdir=BT;\n")
503
504   defaultcolor = "lightblue"
505   
506   colors = {}
507   
508   colors["MUON"] = "lightyellow"
509   colors["STEER"] = "lightgray"
510   
511   for l,d in lib2lib.items():
512     for dl in d:
513       f.write("%s->%s;\n" %(libshorten(l),libshorten(dl)))
514       
515   for p in packages:
516     f.write("subgraph cluster_%s {\n" % p.lower())
517     color = colors.get(p,defaultcolor)      
518     f.write("style=filled;\n")
519     f.write("color=%s;\n" % color)
520     f.write('label="%s";\n' % p)
521     for lib in packages[p]:
522       f.write("%s\n" % libshorten(lib))
523     f.write("}\n")
524     
525   f.write("}\n")
526     
527   print "You should now do :"
528   print "tred %s > %s.bis" % ( ofile, ofile )
529   print "dot -Tpng %s.bis -o %s.png" % (ofile,ofile)
530   
531 if __name__ == "__main__":
532   main()
533