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