import io,os,re,string,sys,copy
import xml.etree.ElementTree as etree
from collections import defaultdict
def matchAPIProfile(api, profile, elem):
match = True
if ('api' in elem.attrib):
if (api == None):
raise UserWarning("No API requested, but 'api' attribute is present with value '" +
elem.get('api') + "'")
elif (api != elem.get('api')):
return False
if ('profile' in elem.attrib):
if (profile == None):
raise UserWarning("No profile requested, but 'profile' attribute is present with value '" +
elem.get('profile') + "'")
elif (profile != elem.get('profile')):
return False
return True
class BaseInfo:
def __init__(self, elem):
self.required = False
self.declared = False
self.elem = elem
def resetState(self):
self.required = False
self.declared = False
class TypeInfo(BaseInfo):
def __init__(self, elem):
BaseInfo.__init__(self, elem)
self.additionalValidity = []
self.removedValidity = []
def resetState(self):
BaseInfo.resetState(self)
self.additionalValidity = []
self.removedValidity = []
class GroupInfo(BaseInfo):
def __init__(self, elem):
BaseInfo.__init__(self, elem)
class EnumInfo(BaseInfo):
def __init__(self, elem):
BaseInfo.__init__(self, elem)
self.type = elem.get('type')
if (self.type == None):
self.type = ''
class CmdInfo(BaseInfo):
def __init__(self, elem):
BaseInfo.__init__(self, elem)
self.additionalValidity = []
self.removedValidity = []
def resetState(self):
BaseInfo.resetState(self)
self.additionalValidity = []
self.removedValidity = []
class FeatureInfo(BaseInfo):
def __init__(self, elem):
BaseInfo.__init__(self, elem)
self.name = elem.get('name')
if (elem.tag == 'feature'):
self.category = 'VERSION'
self.version = elem.get('number')
self.number = "0"
self.supported = None
else:
self.category = self.name.split('_', 2)[1]
self.version = "0"
self.number = elem.get('number')
self.supported = elem.get('supported')
self.emit = False
from generator import write, GeneratorOptions, OutputGenerator
class Registry:
def __init__(self):
self.tree = None
self.typedict = {}
self.groupdict = {}
self.enumdict = {}
self.cmddict = {}
self.apidict = {}
self.extensions = []
self.requiredextensions = [] self.validextensionstructs = defaultdict(list)
self.extdict = {}
self.gen = OutputGenerator()
self.genOpts = None
self.emitFeatures = False
def loadElementTree(self, tree):
self.tree = tree
self.parseTree()
def loadFile(self, file):
self.tree = etree.parse(file)
self.parseTree()
def setGenerator(self, gen):
self.gen = gen
self.gen.setRegistry(self)
def addElementInfo(self, elem, info, infoName, dictionary):
if ('api' in elem.attrib):
key = (elem.get('name'),elem.get('api'))
else:
key = elem.get('name')
if key in dictionary:
self.gen.logMsg('warn', '*** Attempt to redefine',
infoName, 'with key:', key)
else:
dictionary[key] = info
def lookupElementInfo(self, fname, dictionary):
key = (fname, self.genOpts.apiname)
if (key in dictionary):
return dictionary[key]
elif (fname in dictionary):
return dictionary[fname]
else:
return None
def parseTree(self):
self.reg = self.tree.getroot()
self.typedict = {}
for type in self.reg.findall('types/type'):
if (type.get('name') == None):
type.attrib['name'] = type.find('name').text
self.addElementInfo(type, TypeInfo(type), 'type', self.typedict)
self.groupdict = {}
for group in self.reg.findall('enums'):
self.addElementInfo(group, GroupInfo(group), 'group', self.groupdict)
self.enumdict = {}
for enums in self.reg.findall('enums'):
required = (enums.get('type') != None)
for enum in enums.findall('enum'):
enumInfo = EnumInfo(enum)
enumInfo.required = required
self.addElementInfo(enum, enumInfo, 'enum', self.enumdict)
self.cmddict = {}
for cmd in self.reg.findall('commands/command'):
if (cmd.get('name') == None):
cmd.attrib['name'] = cmd.find('proto/name').text
ci = CmdInfo(cmd)
self.addElementInfo(cmd, ci, 'command', self.cmddict)
self.apidict = {}
for feature in self.reg.findall('feature'):
featureInfo = FeatureInfo(feature)
self.addElementInfo(feature, featureInfo, 'feature', self.apidict)
self.extensions = self.reg.findall('extensions/extension')
self.extdict = {}
for feature in self.extensions:
featureInfo = FeatureInfo(feature)
self.addElementInfo(feature, featureInfo, 'extension', self.extdict)
for elem in feature.findall('require'):
for enum in elem.findall('enum'):
addEnumInfo = False
groupName = enum.get('extends')
if (groupName != None):
enum.attrib['extnumber'] = featureInfo.number
enum.attrib['extname'] = featureInfo.name
enum.attrib['supported'] = featureInfo.supported
if (groupName in self.groupdict.keys()):
gi = self.groupdict[groupName]
gi.elem.append(enum)
elem.remove(enum)
else:
self.gen.logMsg('warn', '*** NO matching group',
groupName, 'for enum', enum.get('name'), 'found.')
addEnumInfo = True
elif (enum.get('value') or enum.get('bitpos')):
addEnumInfo = True
if (addEnumInfo):
enumInfo = EnumInfo(enum)
self.addElementInfo(enum, enumInfo, 'enum', self.enumdict)
for type in self.reg.findall('types/type'):
parentStructs = type.get('structextends')
if (parentStructs != None):
for parent in parentStructs.split(','):
self.validextensionstructs[parent].append(type.get('name'))
for parent in self.validextensionstructs:
self.validextensionstructs[parent].sort()
def dumpReg(self, maxlen = 40, filehandle = sys.stdout):
write('***************************************', file=filehandle)
write(' ** Dumping Registry contents **', file=filehandle)
write('***************************************', file=filehandle)
write('// Types', file=filehandle)
for name in self.typedict:
tobj = self.typedict[name]
write(' Type', name, '->', etree.tostring(tobj.elem)[0:maxlen], file=filehandle)
write('// Groups', file=filehandle)
for name in self.groupdict:
gobj = self.groupdict[name]
write(' Group', name, '->', etree.tostring(gobj.elem)[0:maxlen], file=filehandle)
write('// Enums', file=filehandle)
for name in self.enumdict:
eobj = self.enumdict[name]
write(' Enum', name, '->', etree.tostring(eobj.elem)[0:maxlen], file=filehandle)
write('// Commands', file=filehandle)
for name in self.cmddict:
cobj = self.cmddict[name]
write(' Command', name, '->', etree.tostring(cobj.elem)[0:maxlen], file=filehandle)
write('// APIs', file=filehandle)
for key in self.apidict:
write(' API Version ', key, '->',
etree.tostring(self.apidict[key].elem)[0:maxlen], file=filehandle)
write('// Extensions', file=filehandle)
for key in self.extdict:
write(' Extension', key, '->',
etree.tostring(self.extdict[key].elem)[0:maxlen], file=filehandle)
def markTypeRequired(self, typename, required):
self.gen.logMsg('diag', '*** tagging type:', typename, '-> required =', required)
type = self.lookupElementInfo(typename, self.typedict)
if (type != None):
if (required):
if ('requires' in type.elem.attrib):
depType = type.elem.get('requires')
self.gen.logMsg('diag', '*** Generating dependent type',
depType, 'for type', typename)
self.markTypeRequired(depType, required)
for subtype in type.elem.findall('.//type'):
self.gen.logMsg('diag', '*** markRequired: type requires dependent <type>', subtype.text)
self.markTypeRequired(subtype.text, required)
for subenum in type.elem.findall('.//enum'):
self.gen.logMsg('diag', '*** markRequired: type requires dependent <enum>', subenum.text)
self.markEnumRequired(subenum.text, required)
type.required = required
else:
self.gen.logMsg('warn', '*** type:', typename , 'IS NOT DEFINED')
def markEnumRequired(self, enumname, required):
self.gen.logMsg('diag', '*** tagging enum:', enumname, '-> required =', required)
enum = self.lookupElementInfo(enumname, self.enumdict)
if (enum != None):
enum.required = required
else:
self.gen.logMsg('warn', '*** enum:', enumname , 'IS NOT DEFINED')
def markRequired(self, features, required):
self.gen.logMsg('diag', '*** markRequired (features = <too long to print>, required =', required, ')')
for typeElem in features.findall('type'):
self.markTypeRequired(typeElem.get('name'), required)
for enumElem in features.findall('enum'):
self.markEnumRequired(enumElem.get('name'), required)
for cmdElem in features.findall('command'):
name = cmdElem.get('name')
self.gen.logMsg('diag', '*** tagging command:', name, '-> required =', required)
cmd = self.lookupElementInfo(name, self.cmddict)
if (cmd != None):
cmd.required = required
if (required):
for type in cmd.elem.findall('.//type'):
self.gen.logMsg('diag', '*** markRequired: command implicitly requires dependent type', type.text)
self.markTypeRequired(type.text, required)
else:
self.gen.logMsg('warn', '*** command:', name, 'IS NOT DEFINED')
def requireAndRemoveFeatures(self, interface, api, profile):
for feature in interface.findall('require'):
if (matchAPIProfile(api, profile, feature)):
self.markRequired(feature,True)
for feature in interface.findall('remove'):
if (matchAPIProfile(api, profile, feature)):
self.markRequired(feature,False)
def assignAdditionalValidity(self, interface, api, profile):
for feature in interface.findall('require'):
if (matchAPIProfile(api, profile, feature)):
for v in feature.findall('usage'):
if v.get('command'):
self.cmddict[v.get('command')].additionalValidity.append(copy.deepcopy(v))
if v.get('struct'):
self.typedict[v.get('struct')].additionalValidity.append(copy.deepcopy(v))
for feature in interface.findall('remove'):
if (matchAPIProfile(api, profile, feature)):
for v in feature.findall('usage'):
if v.get('command'):
self.cmddict[v.get('command')].removedValidity.append(copy.deepcopy(v))
if v.get('struct'):
self.typedict[v.get('struct')].removedValidity.append(copy.deepcopy(v))
def generateFeature(self, fname, ftype, dictionary):
f = self.lookupElementInfo(fname, dictionary)
if (f == None):
self.gen.logMsg('diag', '*** No entry found for feature', fname,
'returning!')
return
if (not f.required):
self.gen.logMsg('diag', '*** Skipping', ftype, fname, '(not required)')
return
if (f.declared):
self.gen.logMsg('diag', '*** Skipping', ftype, fname, '(already declared)')
return
f.declared = True
genProc = None
if (ftype == 'type'):
genProc = self.gen.genType
if ('requires' in f.elem.attrib):
depname = f.elem.get('requires')
self.gen.logMsg('diag', '*** Generating required dependent type',
depname)
self.generateFeature(depname, 'type', self.typedict)
for subtype in f.elem.findall('.//type'):
self.gen.logMsg('diag', '*** Generating required dependent <type>',
subtype.text)
self.generateFeature(subtype.text, 'type', self.typedict)
for subtype in f.elem.findall('.//enum'):
self.gen.logMsg('diag', '*** Generating required dependent <enum>',
subtype.text)
self.generateFeature(subtype.text, 'enum', self.enumdict)
if (f.elem.get('category') == 'enum'):
self.gen.logMsg('diag', '*** Type', fname, 'is an enum group, so generate that instead')
group = self.lookupElementInfo(fname, self.groupdict)
if (group == None):
genProc = None
self.logMsg('warn', '*** NO MATCHING ENUM GROUP FOUND!!!')
else:
genProc = self.gen.genGroup
f = group
elif (ftype == 'command'):
genProc = self.gen.genCmd
for type in f.elem.findall('.//type'):
depname = type.text
self.gen.logMsg('diag', '*** Generating required parameter type',
depname)
self.generateFeature(depname, 'type', self.typedict)
elif (ftype == 'enum'):
genProc = self.gen.genEnum
if self.emitFeatures:
self.gen.logMsg('diag', '*** Emitting', ftype, 'decl for', fname)
genProc(f, fname)
else:
self.gen.logMsg('diag', '*** Skipping', ftype, fname,
'(not emitting this feature)')
def generateRequiredInterface(self, interface):
for features in interface.findall('require'):
for t in features.findall('type'):
self.generateFeature(t.get('name'), 'type', self.typedict)
for e in features.findall('enum'):
self.generateFeature(e.get('name'), 'enum', self.enumdict)
for c in features.findall('command'):
self.generateFeature(c.get('name'), 'command', self.cmddict)
def apiGen(self, genOpts):
self.gen.logMsg('diag', '*******************************************')
self.gen.logMsg('diag', ' Registry.apiGen file:', genOpts.filename,
'api:', genOpts.apiname,
'profile:', genOpts.profile)
self.gen.logMsg('diag', '*******************************************')
self.genOpts = genOpts
self.apiReset()
regVersions = re.compile(self.genOpts.versions)
regEmitVersions = re.compile(self.genOpts.emitversions)
regAddExtensions = re.compile(self.genOpts.addExtensions)
regRemoveExtensions = re.compile(self.genOpts.removeExtensions)
features = []
apiMatch = False
for key in self.apidict:
fi = self.apidict[key]
api = fi.elem.get('api')
if (api == self.genOpts.apiname):
apiMatch = True
if (regVersions.match(fi.version)):
fi.emit = (regEmitVersions.match(fi.version) != None)
features.append(fi)
if (not fi.emit):
self.gen.logMsg('diag', '*** NOT tagging feature api =', api,
'name =', fi.name, 'version =', fi.version,
'for emission (does not match emitversions pattern)')
else:
self.gen.logMsg('diag', '*** NOT including feature api =', api,
'name =', fi.name, 'version =', fi.version,
'(does not match requested versions)')
else:
self.gen.logMsg('diag', '*** NOT including feature api =', api,
'name =', fi.name,
'(does not match requested API)')
if (not apiMatch):
self.gen.logMsg('warn', '*** No matching API versions found!')
for (extName,ei) in sorted(self.extdict.items(),key = lambda x : x[1].number):
extName = ei.name
include = False
pat = '^(' + ei.elem.get('supported') + ')$'
if (self.genOpts.defaultExtensions and
re.match(pat, self.genOpts.defaultExtensions)):
self.gen.logMsg('diag', '*** Including extension',
extName, "(defaultExtensions matches the 'supported' attribute)")
include = True
if (regAddExtensions.match(extName) != None):
self.gen.logMsg('diag', '*** Including extension',
extName, '(matches explicitly requested extensions to add)')
include = True
if (regRemoveExtensions.match(extName) != None):
self.gen.logMsg('diag', '*** Removing extension',
extName, '(matches explicitly requested extensions to remove)')
include = False
if (include):
ei.emit = True
features.append(ei)
self.requiredextensions.append(extName)
else:
self.gen.logMsg('diag', '*** NOT including extension',
extName, '(does not match api attribute or explicitly requested extensions)')
if (self.genOpts.sortProcedure):
self.genOpts.sortProcedure(features)
self.gen.logMsg('diag', '*** PASS 1: TAG FEATURES ********************************************')
for f in features:
self.gen.logMsg('diag', '*** PASS 1: Tagging required and removed features for',
f.name)
self.requireAndRemoveFeatures(f.elem, self.genOpts.apiname, self.genOpts.profile)
self.assignAdditionalValidity(f.elem, self.genOpts.apiname, self.genOpts.profile)
self.gen.logMsg('diag', '*** PASS 2: GENERATE INTERFACES FOR FEATURES ************************')
self.gen.beginFile(self.genOpts)
for f in features:
self.gen.logMsg('diag', '*** PASS 2: Generating interface for',
f.name)
emit = self.emitFeatures = f.emit
if (not emit):
self.gen.logMsg('diag', '*** PASS 2: NOT declaring feature',
f.elem.get('name'), 'because it is not tagged for emission')
self.gen.beginFeature(f.elem, emit)
self.generateRequiredInterface(f.elem)
self.gen.endFeature()
self.gen.endFile()
def apiReset(self):
for type in self.typedict:
self.typedict[type].resetState()
for enum in self.enumdict:
self.enumdict[enum].resetState()
for cmd in self.cmddict:
self.cmddict[cmd].resetState()
for cmd in self.apidict:
self.apidict[cmd].resetState()
def validateGroups(self):
badGroup = {}
self.gen.logMsg('diag', '*** VALIDATING GROUP ATTRIBUTES ***')
for cmd in self.reg.findall('commands/command'):
proto = cmd.find('proto')
funcname = cmd.find('proto/name').text
if ('group' in proto.attrib.keys()):
group = proto.get('group')
if (group not in self.groupdict.keys()):
if (group not in badGroup.keys()):
badGroup[group] = 1
else:
badGroup[group] = badGroup[group] + 1
for param in cmd.findall('param'):
pname = param.find('name')
if (pname != None):
pname = pname.text
else:
pname = type.get('name')
if ('group' in param.attrib.keys()):
group = param.get('group')
if (group not in self.groupdict.keys()):
if (group not in badGroup.keys()):
badGroup[group] = 1
else:
badGroup[group] = badGroup[group] + 1
if (len(badGroup.keys()) > 0):
self.gen.logMsg('diag', '*** SUMMARY OF UNRECOGNIZED GROUPS ***')
for key in sorted(badGroup.keys()):
self.gen.logMsg('diag', ' ', key, ' occurred ', badGroup[key], ' times')