diff options
author | Jon Leech <[email protected]> | 2019-08-17 15:58:46 -0700 |
---|---|---|
committer | Jon Leech <[email protected]> | 2019-08-17 15:59:53 -0700 |
commit | 4ee33d2fbd1e7a072c4d8a6cf8f01c4f96d7483d (patch) | |
tree | 267945d47d5ce0a4ccff68e1a6523aad4f766f5a /registry/generator.py | |
parent | 23b2e8e64bdf3f25b3d73f1593e72977ebfcd39b (diff) | |
download | Vulkan-Headers-4ee33d2fbd1e7a072c4d8a6cf8f01c4f96d7483d.tar.gz Vulkan-Headers-4ee33d2fbd1e7a072c4d8a6cf8f01c4f96d7483d.zip |
Update for Vulkan-Docs 1.1.120v1.1.120
Diffstat (limited to 'registry/generator.py')
-rw-r--r-- | registry/generator.py | 244 |
1 files changed, 177 insertions, 67 deletions
diff --git a/registry/generator.py b/registry/generator.py index f62b71f..8573655 100644 --- a/registry/generator.py +++ b/registry/generator.py @@ -18,17 +18,20 @@ from __future__ import unicode_literals import io import os -import re import pdb +import re import sys try: from pathlib import Path except ImportError: from pathlib2 import Path -def write( *args, **kwargs ): - file = kwargs.pop('file',sys.stdout) - end = kwargs.pop('end','\n') +from spec_tools.util import getElemName, getElemType + + +def write(*args, **kwargs): + file = kwargs.pop('file', sys.stdout) + end = kwargs.pop('end', '\n') file.write(' '.join(str(arg) for arg in args)) file.write(end) @@ -83,9 +86,10 @@ def regSortExtensionNumberKey(feature): # then by version number (for features) # then by extension number (for extensions) def regSortFeatures(featureList): - featureList.sort(key = regSortExtensionNumberKey) - featureList.sort(key = regSortFeatureVersionKey) - featureList.sort(key = regSortCategoryKey) + featureList.sort(key=regSortExtensionNumberKey) + featureList.sort(key=regSortFeatureVersionKey) + featureList.sort(key=regSortCategoryKey) + # GeneratorOptions - base class for options used during header production # These options are target language independent, and used by @@ -125,18 +129,18 @@ class GeneratorOptions: """Represents options during header production from an API registry""" def __init__(self, - conventions = None, - filename = None, - directory = '.', - apiname = None, - profile = None, - versions = '.*', - emitversions = '.*', - defaultExtensions = None, - addExtensions = None, - removeExtensions = None, - emitExtensions = None, - sortProcedure = regSortFeatures): + conventions=None, + filename=None, + directory='.', + apiname=None, + profile=None, + versions='.*', + emitversions='.*', + defaultExtensions=None, + addExtensions=None, + removeExtensions=None, + emitExtensions=None, + sortProcedure=regSortFeatures): self.conventions = conventions self.filename = filename self.directory = directory @@ -205,19 +209,19 @@ class OutputGenerator: # categoryToPath - map XML 'category' to include file directory name categoryToPath = { - 'bitmask' : 'flags', - 'enum' : 'enums', - 'funcpointer' : 'funcpointers', - 'handle' : 'handles', - 'define' : 'defines', - 'basetype' : 'basetypes', + 'bitmask': 'flags', + 'enum': 'enums', + 'funcpointer': 'funcpointers', + 'handle': 'handles', + 'define': 'defines', + 'basetype': 'basetypes', } # Constructor def __init__(self, - errFile = sys.stderr, - warnFile = sys.stderr, - diagFile = sys.stdout): + errFile=sys.stderr, + warnFile=sys.stderr, + diagFile=sys.stdout): self.outFile = None self.errFile = errFile self.warnFile = warnFile @@ -227,7 +231,7 @@ class OutputGenerator: self.genOpts = None self.registry = None # Used for extension enum value generation - self.extBase = 1000000000 + self.extBase = 1000000000 self.extBlockSize = 1000 self.madeDirs = {} @@ -295,21 +299,21 @@ class OutputGenerator: bitpos = int(value, 0) numVal = 1 << bitpos value = '0x%08x' % numVal - if( bitpos >= 32 ): + if bitpos >= 32: value = value + 'ULL' self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']') return [numVal, value] if 'offset' in elem.keys(): # Obtain values in the mapping from the attributes enumNegative = False - offset = int(elem.get('offset'),0) - extnumber = int(elem.get('extnumber'),0) + offset = int(elem.get('offset'), 0) + extnumber = int(elem.get('extnumber'), 0) extends = elem.get('extends') if 'dir' in elem.keys(): enumNegative = True self.logMsg('diag', 'Enum', name, 'offset =', offset, - 'extnumber =', extnumber, 'extends =', extends, - 'enumNegative =', enumNegative) + 'extnumber =', extnumber, 'extends =', extends, + 'enumNegative =', enumNegative) # Now determine the actual enumerant value, as defined # in the "Layers and Extensions" appendix of the spec. numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset @@ -346,7 +350,7 @@ class OutputGenerator: # happens when defining the same enum conditionally in # several extension blocks. if (strVal2 == strVal or (numVal is not None and - numVal == numVal2)): + numVal == numVal2)): True # self.logMsg('info', 'checkDuplicateEnums: Duplicate enum (' + name + # ') found with the same value:' + strVal) @@ -364,14 +368,14 @@ class OutputGenerator: try: self.logMsg('warn', 'Two enums found with the same value: ' - + name + ' = ' + name2.get('name') + ' = ' + strVal) + + name + ' = ' + name2.get('name') + ' = ' + strVal) except: pdb.set_trace() # Track this enum to detect followon duplicates - nameMap[name] = [ elem, numVal, strVal ] + nameMap[name] = [elem, numVal, strVal] if numVal is not None: - valueMap[numVal] = [ elem, numVal, strVal ] + valueMap[numVal] = [elem, numVal, strVal] # Add this enum to the list stripped.append(elem) @@ -385,7 +389,7 @@ class OutputGenerator: groupElem = groupinfo.elem if self.genOpts.conventions.constFlagBits and groupElem.get('type') == 'bitmask': - return self.buildEnumCDecl_Bitmask( groupinfo, groupName) + return self.buildEnumCDecl_Bitmask(groupinfo, groupName) else: return self.buildEnumCDecl_Enum(expand, groupinfo, groupName) @@ -406,7 +410,7 @@ class OutputGenerator: # Should catch exceptions here for more complex constructs. Not yet. (_, strVal) = self.enumToValue(elem, True) name = elem.get('name') - body += "static const " + flagTypeName + " " + name + " = " + strVal + ";\n" + body += "static const {} {} = {};\n".format(flagTypeName, name, strVal) # Postfix @@ -418,17 +422,17 @@ class OutputGenerator: # Break the group name into prefix and suffix portions for range # enum generation - expandName = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2',groupName).upper() + expandName = re.sub(r'([0-9a-z_])([A-Z0-9])', r'\1_\2', groupName).upper() expandPrefix = expandName expandSuffix = '' - expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName) + expandSuffixMatch = re.search(r'[A-Z][A-Z]+$', groupName) if expandSuffixMatch: expandSuffix = '_' + expandSuffixMatch.group() # Strip off the suffix from the prefix expandPrefix = expandName.rsplit(expandSuffix, 1)[0] # Prefix - body = "typedef enum " + groupName + " {\n" + body = ["typedef enum %s {" % groupName] # @@ Should use the type="bitmask" attribute instead isEnum = ('FLAG_BITS' not in expandPrefix) @@ -450,22 +454,22 @@ class OutputGenerator: # them following the numeric values, to allow for aliases. # NOTE: this doesn't do a topological sort yet, so aliases of # aliases can still get in the wrong order. - aliasText = "" + aliasText = [] for elem in enums: # Convert the value to an integer and use that to track min/max. # Values of form -(number) are accepted but nothing more complex. # Should catch exceptions here for more complex constructs. Not yet. - (numVal,strVal) = self.enumToValue(elem, True) + (numVal, strVal) = self.enumToValue(elem, True) name = elem.get('name') # Extension enumerants are only included if they are required if self.isEnumRequired(elem): - decl = " " + name + " = " + strVal + ",\n" + decl = " {} = {},".format(name, strVal) if numVal is not None: - body += decl + body.append(decl) else: - aliasText += decl + aliasText.append(decl) # Don't track min/max for non-numbers (numVal is None) if isEnum and numVal is not None and elem.get('extends') is None: @@ -480,20 +484,21 @@ class OutputGenerator: maxValue = numVal # Now append the non-numeric enumerant values - body += aliasText + body.extend(aliasText) # Generate min/max value tokens and a range-padding enum. Need some # additional padding to generate correct names... if isEnum and expand: - body += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n" - body += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n" - body += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n" + body.extend((" {}_BEGIN_RANGE{} = {},".format(expandPrefix, expandSuffix, minName), + " {}_END_RANGE{} = {},".format( + expandPrefix, expandSuffix, maxName), + " {}_RANGE_SIZE{} = ({} - {} + 1),".format(expandPrefix, expandSuffix, maxName, minName))) - # Always generate this to make sure the enumerated type is 32 bits - body += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n" + body.append(" {}_MAX_ENUM{} = 0x7FFFFFFF".format( + expandPrefix, expandSuffix)) # Postfix - body += "} " + groupName + ";" + body.append("} %s;" % groupName) # Determine appropriate section for this declaration if groupElem.get('type') == 'bitmask': @@ -501,7 +506,7 @@ class OutputGenerator: else: section = 'group' - return (section, body) + return (section, '\n'.join(body)) def makeDir(self, path): self.logMsg('diag', 'OutputGenerator::makeDir(' + path + ')') @@ -514,6 +519,10 @@ class OutputGenerator: def beginFile(self, genOpts): self.genOpts = genOpts + self.should_insert_may_alias_macro = \ + self.genOpts.conventions.should_insert_may_alias_macro(self.genOpts) + + self.conventions = genOpts.conventions # Open specified output file. Not done in constructor since a # Generator can be used without writing to a file. @@ -603,13 +612,14 @@ class OutputGenerator: # aligncol - if non-zero, attempt to align the nested <name> element # at this column def makeCParamDecl(self, param, aligncol): - paramdecl = ' ' + noneStr(param.text) + indent = ' ' + paramdecl = indent + noneStr(param.text) for elem in param: text = noneStr(elem.text) tail = noneStr(elem.tail) - if self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail): - # OpenXR-specific macro insertion + if self.should_insert_may_alias_macro and self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail): + # OpenXR-specific macro insertion - but not in apiinc for the spec tail = self.genOpts.conventions.make_voidpointer_alias(tail) if elem.tag == 'name' and aligncol > 0: self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam) @@ -619,17 +629,22 @@ class OutputGenerator: # This works around a problem where very long type names - # longer than the alignment column - would run into the tail # text. - paramdecl = paramdecl.ljust(aligncol-1) + ' ' + paramdecl = paramdecl.ljust(aligncol - 1) + ' ' newLen = len(paramdecl) self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl) paramdecl += text + tail + if aligncol == 0: + # Squeeze out multiple spaces other than the identation + paramdecl = indent + ' '.join(paramdecl.split()) return paramdecl - # getCParamTypeLength - return the length of the type field is an indented, formatted - # declaration for a <param> or <member> block (e.g. function parameter - # or structure/union member). + # getCParamTypeLength - return the length of the type field in an + # indented, formatted declaration for a <param> or <member> block (e.g. + # function parameter or structure/union member). This relies on the + # presence of the <name> tag; if not present, return zero. # param - Element (<param> or <member>) to identify def getCParamTypeLength(self, param): + newLen = 0 paramdecl = ' ' + noneStr(param.text) for elem in param: text = noneStr(elem.text) @@ -646,17 +661,106 @@ class OutputGenerator: return newLen + def getMaxCParamTypeLength(self, info): + """Return the length of the longest type field for a member/parameter. + + info - TypeInfo or CommandInfo. + """ + lengths = (self.getCParamTypeLength(member) + for member in info.getMembers()) + return max(lengths) + + def getHandleParent(self, typename): + """Get the parent of a handle object.""" + info = self.registry.typedict.get(typename) + if info is None: + return None + + elem = info.elem + if elem is not None: + return elem.get('parent') + + return None + + def iterateHandleAncestors(self, typename): + """Iterate through the ancestors of a handle type.""" + current = self.getHandleParent(typename) + while current is not None: + yield current + current = self.getHandleParent(current) + + def getHandleAncestors(self, typename): + """Get the ancestors of a handle object.""" + return list(self.iterateHandleAncestors(typename)) + + def getTypeCategory(self, typename): + """Get the category of a type.""" + info = self.registry.typedict.get(typename) + if info is None: + return None + + elem = info.elem + if elem is not None: + return elem.get('category') + return None + + def isStructAlwaysValid(self, structname): + """Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance).""" + # A conventions object is required for this call. + if not self.conventions: + raise RuntimeError("To use isStructAlwaysValid, be sure your options include a Conventions object.") + + if self.conventions.type_always_valid(structname): + return True + + category = self.getTypeCategory(structname) + if self.conventions.category_requires_validation(category): + return False + + info = self.registry.typedict.get(structname) + assert(info is not None) + + members = info.getMembers() + + for member in members: + member_name = getElemName(member) + if member_name in (self.conventions.structtype_member_name, + self.conventions.nextpointer_member_name): + return False + + if member.get('noautovalidity'): + return False + + member_type = getElemType(member) + + if member_type in ('void', 'char') or self.paramIsArray(member) or self.paramIsPointer(member): + return False + + if self.conventions.type_always_valid(member_type): + continue + + member_category = self.getTypeCategory(member_type) + + if self.conventions.category_requires_validation(member_category): + return False + + if member_category in ('struct', 'union'): + if self.isStructAlwaysValid(member_type) is False: + return False + + return True + # isEnumRequired(elem) - return True if this <enum> element is # required, False otherwise # elem - <enum> element to test def isEnumRequired(self, elem): required = elem.get('required') is not None self.logMsg('diag', 'isEnumRequired:', elem.get('name'), - '->', required) + '->', required) return required - #@@@ This code is overridden by equivalent code now run in - #@@@ Registry.generateFeature + # @@@ This code is overridden by equivalent code now run in + # @@@ Registry.generateFeature required = False @@ -707,6 +811,12 @@ class OutputGenerator: else: pdecl += text + tail tdecl += text + tail + + if self.genOpts.alignFuncParam == 0: + # Squeeze out multiple spaces - there is no indentation + pdecl = ' '.join(pdecl.split()) + tdecl = ' '.join(tdecl.split()) + # Now add the parameter declaration list, which is identical # for prototypes and typedefs. Concatenate all the text from # a <param> node without the tags. No tree walking required @@ -732,7 +842,7 @@ class OutputGenerator: else: paramdecl += 'void' paramdecl += ");" - return [ pdecl + indentdecl, tdecl + paramdecl ] + return [pdecl + indentdecl, tdecl + paramdecl] def newline(self): write('', file=self.outFile) |