Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
#!  /usr/bin/env python3
import string
import re
import collections 

classtransients = collections.defaultdict(list)

classtransients['edm::AssociationMap'].append('transientMap_')
classtransients['edm::AssociationVector'].append('transientVector_')



class RMParser(object):
    """Parses the rootmap file(s) for a package looking for class declarations of edm template classes
    which contain member(s) which must be labeled transient="true" in classes_def.xml
    """
    
    def __init__(self,filelist):
        self._files = filelist
        self.cnames = collections.defaultdict(list)
        self._presentClass = None
        self.readRootMap()
    def readRootMap(self):
        for filename in self._files:
          f = open(filename)
          for line in f:
            cname=""
            if re.search("^class",line): cname = re.sub("class ","",line)
            if re.search("^struct",line): cname = re.sub("struct ","",line)
            for key in classtransients.keys():
               if re.search(r'^%s<' % key, cname):
                   n_name = " ".join(line.split())
                   self.cnames[key].append(n_name)
          f.close()

def checkTrans(templname,name):
    c = ROOT.TClass.GetClass(name)
    if not c:
        print ("Info: Could no load dictionary for class '"+name+"' because of typedef(s) in the name.")
        return 0
    nerrs=0
    for trans in classtransients[templname]:
      tdm = c.GetDataMember(trans)
      retval = False
      if tdm : retval = tdm.IsPersistent()
      if retval == True : 
          print ("Error: field='"+trans+"' must be labeled transient=\"true\" in classes_def.xml for "+name+" or the equivalent class name with typedefs.")
          nerrs+=1
    return nerrs

#Setup the options
from argparse import ArgumentParser
oparser = ArgumentParser()
oparser.add_argument("-l","--lib", dest="library", type=str,
                     help="specify the library to load")
oparser.add_argument("-f","--rootmap", dest="rmfiles", action="append", type=str, default=[],
                     help="specify the rootmap file(s) to read")

options=oparser.parse_args()

#Need to not have ROOT load .rootlogon.(C|py) since it can cause interference.
import ROOT
ROOT.PyConfig.DisableRootLogon = True

#Keep ROOT from trying to use X11
ROOT.gROOT.SetBatch(True)
ROOT.gROOT.ProcessLine(".autodict")
if options.library is None:
    print ("Transient member check requires a specific library")
else:
    if ROOT.gSystem.Load(options.library) < 0 :
        raise RuntimeError("failed to load library '"+options.library+"'")

p = RMParser(options.rmfiles)

nerrs = 0
for key in p.cnames.keys():
    for value in p.cnames[key]:
        nerrs += checkTrans(key,value)

if nerrs > 0 : 
    print ("Error: edm template class member must be labeled transient=\"true\" in classes_def.xml. Check log for specific template and member name.")
    import sys
    sys.exit(1)