Back to home page

Project CMSSW displayed by LXR

 
 

    


Warning, /FWCore/Reflection/scripts/edmCheckClassVersion is written in an unsupported language. File is not indexed.

0001 #!  /usr/bin/env python3
0002 
0003 import sys
0004 try:
0005   import FWCore.Reflection.ClassesDefXmlUtils as ClassesDefUtils
0006 except:
0007   import os
0008   sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)),"python"))
0009   import ClassesDefXmlUtils as ClassesDefUtils
0010 
0011 # recursively check the base classes for a class pointer
0012 # as building the streamer will crash if base classes are
0013 # incomplete
0014 def verifyBaseClasses(c) :
0015     missingBase = 0
0016 
0017     # check that all bases are loaded
0018     bases = c.GetListOfBases()
0019     if not bases :
0020         print ("Incomplete class ", c.GetName())
0021         return 1
0022 
0023     for b in bases :
0024         bc = b.GetClassPointer()
0025         if bc :
0026             missingBase += verifyBaseClasses(bc)
0027         else :
0028             print ("Incomplete base class for ", c.GetName(), ": ", b.GetName())
0029             missingBase += 1
0030 
0031     return missingBase
0032 
0033 def checkDictionaries(name):
0034     c = ROOT.TClass.GetClass(name)
0035     if not c:
0036         raise RuntimeError("failed to load dictionary for class '"+name+"'")
0037 
0038     missingDict = verifyBaseClasses(c)
0039     if missingDict == 0 :
0040         si = c.GetStreamerInfo()
0041         if si :
0042             ts = si.GetElements()
0043             for telem in ts :
0044                 clm = telem.GetClassPointer()
0045                 if clm and not clm.IsLoaded() :
0046                     print ("Missing dictionary for ", telem.GetName(), " type ", clm.GetName())
0047                     missingDict += 1
0048         else :
0049             print ("No streamer info for ", c.GetName())
0050             missingDict += 1
0051 
0052     return missingDict
0053 
0054 def checkClassDefinitions(classes, checkdict):
0055     missingDict = 0
0056     foundErrors = dict()
0057     for name,info in classes.items():
0058         errorCode,rootClassVersion,classChecksum = ClassesDefUtils.checkClass(name,info[ClassesDefUtils.XmlParser.classVersionIndex],info[ClassesDefUtils.XmlParser.versionsToChecksumIndex])
0059         if errorCode != ClassesDefUtils.noError:
0060             foundErrors[name]=(errorCode,classChecksum,rootClassVersion)
0061         if checkdict :
0062             missingDict += checkDictionaries(name)
0063     return (missingDict, foundErrors)
0064 
0065 def checkErrors(foundErrors, classes, generate):
0066     foundRootDoesNotMatchError = False
0067     originalToNormalizedNames = dict()
0068     for name,retValues in foundErrors.items():
0069         origName = classes[name][ClassesDefUtils.XmlParser.originalNameIndex]
0070         originalToNormalizedNames[origName]=name
0071         code = retValues[0]
0072         classVersion = classes[name][ClassesDefUtils.XmlParser.classVersionIndex]
0073         classChecksum = retValues[1]
0074         rootClassVersion = retValues[2]
0075         if code == ClassesDefUtils.errorRootDoesNotMatchClassDef:
0076             foundRootDoesNotMatchError=True
0077             print ("error: for class '"+name+"' ROOT says the ClassVersion is "+str(rootClassVersion)+" but classes_def.xml says it is "+str(classVersion)+". Are you sure everything compiled correctly?")
0078         elif code == ClassesDefUtils.errorMustUpdateClassVersion and not generate:
0079             print ("error: class '"+name+"' has a different checksum for ClassVersion "+str(classVersion)+". Increment ClassVersion to "+str(classVersion+1)+" and assign it to checksum "+str(classChecksum))
0080         elif not generate:
0081             print ("error:class '"+name+"' needs to include the following as part of its 'class' declaration")
0082             print ('   <version ClassVersion="'+str(classVersion)+'" checksum="'+str(classChecksum)+'"/>')
0083     return (foundRootDoesNotMatchError, originalToNormalizedNames)
0084 
0085 
0086 def generate(oldfile, newfile, originalToNormalizedNames, classes, foundErrors):
0087     with open(oldfile) as f, open(newfile, 'w') as outFile:
0088         out = ''
0089         for l in f.readlines():
0090             newLine = l
0091             if -1 != l.find('<class') and -1 != l.find('ClassVersion'):
0092                 splitArgs = l.split('"')
0093                 name = splitArgs[1]
0094                 normName = originalToNormalizedNames.get(name,None)
0095                 if normName is not None:
0096                     indent = l.find('<')
0097                     #this is a class with a problem
0098                     classVersion = classes[normName][ClassesDefUtils.XmlParser.classVersionIndex]
0099                     code,checksum,rootClassVersion = foundErrors[normName]
0100                     hasNoSubElements = (-1 != l.find('/>'))
0101                     if code == ClassesDefUtils.errorMustUpdateClassVersion:
0102                         classVersion += 1
0103                         parts = splitArgs[:]
0104                         indexToClassVersion = 0
0105                         for pt in parts:
0106                             indexToClassVersion +=1
0107                             if -1 != pt.find('ClassVersion'):
0108                                 break
0109                         parts[indexToClassVersion]=str(classVersion)
0110                         newLine = '"'.join(parts)
0111 
0112                     if hasNoSubElements:
0113                         newLine = newLine.replace('/','')
0114                     out +=newLine
0115                     newLine =' '*indent+' <version ClassVersion="'+str(classVersion)+'" checksum="'+str(checksum)+'"/>\n'
0116                     if hasNoSubElements:
0117                         out += newLine
0118                         newLine=' '*indent+'</class>\n'
0119             out +=newLine
0120 
0121         outFile.writelines(out)
0122 
0123 def main(args):
0124     ClassesDefUtils.initROOT(args.library)
0125     if args.library is None and args.checkdict:
0126         print ("Dictionary checks require a specific library")
0127     ClassesDefUtils.initCheckClass()
0128 
0129     try:
0130         p = ClassesDefUtils.XmlParser(args.xmlfile)
0131     except RuntimeError as e:
0132         print(f"Parsing {args.xmlfile} failed: {e}")
0133         return(1)
0134 
0135     (missingDict, foundErrors) = checkClassDefinitions(p.classes, args.checkdict)
0136     (foundRootDoesNotMatchError, originalToNormalizedNames) = checkErrors(foundErrors, p.classes, args.generate)
0137 
0138     if (len(foundErrors)>0 and not args.generate) or (args.generate and foundRootDoesNotMatchError) or missingDict:
0139         return 1
0140 
0141     if args.generate:
0142         generate(args.xmlfile, 'classes_def.xml.generated', originalToNormalizedNames, p.classes, foundErrors)
0143 
0144     return 0
0145 
0146 if __name__ == "__main__":
0147     from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
0148     parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
0149     parser.add_argument("-d","--check_dictionaries", dest="checkdict",action="store_true",default=False,
0150                          help="check that all required dictionaries are loaded")
0151     parser.add_argument("-l","--lib", dest="library", type=str,
0152                          help="specify the library to load. If not set classes are found using the PluginManager")
0153     parser.add_argument("-x","--xml_file", dest="xmlfile",default="./classes_def.xml", type=str,
0154                          help="the classes_def.xml file to read")
0155     parser.add_argument("-g","--generate_new",dest="generate", action="store_true",default=False,
0156                          help="instead of issuing errors, generate a new classes_def.xml file.")
0157 
0158     args = parser.parse_args()
0159     sys.exit(main(args))
0160