10 Given a directory, will look into lib*.pkg files to produce a dependency graph
11 of libraries (and DA if there are some)
14 __author__ = "L. Aphecetche aphecetc_at_in2p3_dot_fr"
17 notassociatedfiles = {}
19 #_______________________________________________________________________________
21 """Describe usage of script
23 print "Usage: %s [-h | --help] [-d | --debug] [--da] directory_to_scan" % sys.argv[0]
26 #_______________________________________________________________________________
28 """ append a to list, if a not there yet
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.
41 # list of possible .pkg variables
42 pkgkeys = [ "SRCS","EINCLUDE","HDRS","FSRCS","DHDR","CSRCS","CHDRS","ELIBS","EDEFINE","PACKFFLAGS","PACKCXXFLAGS","PACKCFLAGS","PACKSOFLAGS","EXPORT","EHDRS" ]
45 keyEINCLUDE = pkgkeys[1]
48 pkg = getLibPackage(lib)
49 einclude = [ "%s/include" % rootsys, "%s/STEER" % alice_root, "%s/%s" % (alice_root,pkg) ]
51 dir = os.path.dirname(lib)
56 print "getSourceFiles : could not open package file %s" % lib
57 return sourcefiles, einclude
68 if re.search("^%s" % keySRCS,l):
72 if re.search("^%s" % keyEINCLUDE,l):
73 l = re.sub(keyEINCLUDE,' ',l)
76 l = re.sub('\+',' ',l)
79 append(einclude,os.path.join(alice_root,i))
82 if re.search('Ali',l) and ( re.search('.cxx',l) or re.search('.h',l) ):
83 l = re.sub(keySRCS,' ',l)
86 l = re.sub('\+',' ',l)
87 l = re.sub("\\\\",' ',l)
89 append(sourcefiles,os.path.join(dir,i))
92 return sourcefiles,einclude
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...
105 package = getFilePackage(srcfile,alice_root)
106 file = re.sub("%s/%s" % (alice_root,package)," ",srcfile).strip()
109 depfile = "%s/%s/tgt_%s/%s" % (alice_root,package,alice_target,file)
110 depfile = re.sub("\.cxx",".d",depfile)
115 print "Could not open file %s" % depfile
124 parts = line.strip().split()
126 if re.search(rootsys,p):
130 p = "%s/%s" % (alice_root,p)
131 p = re.sub("%s/include" % alice_root,"%s/STEER" % alice_root,p)
138 #_______________________________________________________________________________
139 def getIncludeFiles(srcfile,eincludes,rootsys):
140 """Extract the list of included classes from a class, using :
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
153 # print "Could not open file %s" % srcfile
162 incdir = "%s -I%s" % (incdir,i)
164 cmd = "gcc %s -MM %s -MG" % (incdir,srcfile)
170 line = re.sub("\\\\"," ",line)
175 if len(line) > 0 and line != srcfile:
176 if line.find('/') < 0:
177 print "Got no path for file",srcfile," line=",line
179 if re.search(rootsys,line):
181 append(includes,line)
186 #_______________________________________________________________________________
188 """Extract a unique list from list
195 #_______________________________________________________________________________
196 def libshorten(libname):
197 """From libYYYxxx.pkg to YYYxxx
200 s = os.path.basename(libname)
201 if re.search("^lib",s):
202 s = re.sub("^lib","",s)
203 s = re.sub("\.pkg","",s)
207 #_______________________________________________________________________________
208 def fileshorten(file,path):
209 """From path/toto/file to toto/file
212 s = re.sub(path," ",file).strip()
218 #_______________________________________________________________________________
219 def getFilePackage(file,alice_root):
220 """ Get the package in which this file is defined
223 f = re.sub(alice_root,"/",file)
229 #_______________________________________________________________________________
230 def getLibPackage(libname):
231 """ Get the package in which this library is defined
234 p = libname.split('/')
237 #_______________________________________________________________________________
238 def tryRecover(f,inc2src,src2lib,alice_root):
239 """ This method should try to recover the "father" of file f (most probably
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...
244 Is it needed really ?
248 print "tryRecover:",f
250 if not f.find('\.h'):
253 p = getFilePackage(f,alice_root)
255 cxxfiles = inc2src.get(f,[])
257 for file in cxxfiles:
258 libs = src2lib.get(file,[])
261 pl = getLibPackage(l)
267 #_______________________________________________________________________________
268 #_______________________________________________________________________________
269 #_______________________________________________________________________________
272 # we cannot work w/o those environement variables, so check them...
273 requiredVariables = [ "ROOTSYS", "ALICE_ROOT", "ALICE_TARGET" ]
275 for r in requiredVariables:
276 if not r in os.environ:
277 print "%s is not defined. Cannot work." % r
280 alice_root = os.environ.get("ALICE_ROOT")
281 rootsys = os.environ.get("ROOTSYS")
282 alice_target = os.environ.get("ALICE_TARGET")
288 opts, args = getopt.getopt(sys.argv[1:],"hd",["help", "debug","da"])
289 except getopt.GetoptError:
290 print "Error in options"
294 if o in ( "-d","--debug" ):
296 elif o in ( "-h","--help" ):
302 assert False, "unhandled option"
304 dir = os.path.abspath(args[0])
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):
314 requestedPackages = [ "MUON", "STEER", "RAW", "ITS", "TRD", "VZERO", "TPC", "PHOS", "TOF", "ZDC", "EMCAL", "HMPID", "SHUTTLE", "ACORDE" ];
316 # find the libraries defined in this directory (looking for libXXXX.pkg files)
320 for f in os.listdir(d):
321 fulllib = os.path.join(d,f)
322 p = getLibPackage(fulllib)
323 if not p in requestedPackages:
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)
331 libraries.append(os.path.join(d,tmp))
333 # from list of library files (libXXXyyy.pkg), try to find back the list of
343 packages[p].append(l)
345 # src2inc[file.cxx] -> { all included files of that file }
348 # inc2src[file.h] -> { all files that include that one }
351 # lib2src[libXXX.pkg] -> { list of files of that library }
354 # src2lib[file.cxx] -> { list of libraries including that file }
357 # eincludes[libXXX.pkg] -> list of directories to be included to be able to compile the files
360 # lib2inc[libXXX.pkg] -> { list of all included files of that library }
363 # inc2lib[file.h] -> { list of libraries that include that file }
370 for lib in packages[p]:
374 print " ",libshorten(lib),"..."
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)
381 l = re.sub("lib","",l)
382 l = re.sub("\.pkg","",l)
383 lib2src[lib] = [ "%s.cxx" % l ]
388 for src in lib2src[lib]:
389 # inc = getIncludeFiles(src,eincludes[lib],rootsys)
390 inc = getIncludeFiles2(src,alice_root,alice_target,rootsys)
393 if not src in src2lib:
396 append(src2lib[src],lib)
399 if not i in inc2src.keys():
401 append(inc2src[i],src)
402 append(lib2inc[lib],i)
403 if not i in inc2lib.keys():
405 append(inc2lib[i],lib)
407 # some debug at this point...
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]:
423 print "src2lib relationship"
424 for src,lib in src2lib.items():
425 print fileshorten(src,alice_root),"(",
427 print libshorten(l)," ",
430 # now fills the ultimate array, lib2lib
431 # lib2lib[libXXX.pkg] -> { libYYY.pkg }, list of libraries that lib depends on
435 for lib in libraries:
439 for hfile in lib2inc[lib]:
443 # start simple : is f contains ROOTSYS, it's ROOT.
444 if re.search(rootsys,hfile):
447 # not that simple, let's try to find out...
448 cxx = re.sub("\.h",".cxx",hfile)
449 dl = src2lib.get(cxx,[])
453 print "Got several libs(",len(dl),"for ",hfile,":"
457 notassociatedfiles[hfile] = 1
459 append(lib2lib[lib],l)
461 ###################### Debug parts...
463 for lib in libraries:
464 print libshorten(lib),"depends on"
465 for f in lib2lib[lib]:
466 print " ",libshorten(f)
470 print "From source files to include files : "
471 for cxxfile, hfile in src2inc.items():
472 print fileshorten(cxxfile,alice_root)
474 print " ",fileshorten(h,alice_root)
478 print "From include files to source files : "
479 for i,sources in inc2src.items():
480 print fileshorten(i,alice_root), len(sources)
482 print " ",fileshorten(s,alice_root),"(",
486 ######################
488 if len(notassociatedfiles) > 0:
489 print "The following files could not be associated with any library..."
490 for f in notassociatedfiles.keys():
492 t=tryRecover(f,inc2src,src2lib,alice_root)
495 # output the dot file that will have to be processed by the dot program
497 ofile = "%s.dot" % os.path.splitext(os.path.basename(sys.argv[0]))[0]
501 f.write("digraph G {\n")
502 f.write("rankdir=BT;\n")
504 defaultcolor = "lightblue"
508 colors["MUON"] = "lightyellow"
509 colors["STEER"] = "lightgray"
511 for l,d in lib2lib.items():
513 f.write("%s->%s;\n" %(libshorten(l),libshorten(dl)))
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))
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)
531 if __name__ == "__main__":