summaryrefslogtreecommitdiffhomepage
path: root/registry/generator.py
diff options
context:
space:
mode:
Diffstat (limited to 'registry/generator.py')
-rw-r--r--registry/generator.py244
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)