1154 lines
50 KiB
Python
1154 lines
50 KiB
Python
#!/usr/bin/python3 -i
|
|
#
|
|
# Copyright (c) 2013-2018 The Khronos Group Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import os,re,sys
|
|
from generator import *
|
|
|
|
# ValidityOutputGenerator - subclass of OutputGenerator.
|
|
# Generates AsciiDoc includes of valid usage information, for reference
|
|
# pages and the Vulkan specification. Similar to DocOutputGenerator.
|
|
#
|
|
# ---- methods ----
|
|
# ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
|
|
# OutputGenerator. Defines additional internal state.
|
|
# ---- methods overriding base class ----
|
|
# beginFile(genOpts)
|
|
# endFile()
|
|
# beginFeature(interface, emit)
|
|
# endFeature()
|
|
# genCmd(cmdinfo)
|
|
class ValidityOutputGenerator(OutputGenerator):
|
|
"""Generate specified API interfaces in a specific style, such as a C header"""
|
|
def __init__(self,
|
|
errFile = sys.stderr,
|
|
warnFile = sys.stderr,
|
|
diagFile = sys.stdout):
|
|
OutputGenerator.__init__(self, errFile, warnFile, diagFile)
|
|
|
|
def beginFile(self, genOpts):
|
|
OutputGenerator.beginFile(self, genOpts)
|
|
def endFile(self):
|
|
OutputGenerator.endFile(self)
|
|
def beginFeature(self, interface, emit):
|
|
# Start processing in superclass
|
|
OutputGenerator.beginFeature(self, interface, emit)
|
|
def endFeature(self):
|
|
# Finish processing in superclass
|
|
OutputGenerator.endFeature(self)
|
|
|
|
def makeParameterName(self, name):
|
|
return 'pname:' + name
|
|
|
|
def makeStructName(self, name):
|
|
return 'sname:' + name
|
|
|
|
def makeBaseTypeName(self, name):
|
|
return 'basetype:' + name
|
|
|
|
def makeEnumerationName(self, name):
|
|
return 'elink:' + name
|
|
|
|
def makeEnumerantName(self, name):
|
|
return 'ename:' + name
|
|
|
|
def makeFLink(self, name):
|
|
return 'flink:' + name
|
|
|
|
# Create a unique namespaced Valid Usage anchor name given a
|
|
# blockname - command or structure
|
|
# pname - parameter or member (may be None)
|
|
# category - distinct implicit VU type
|
|
def makeAnchor(self, blockname, pname, category):
|
|
# For debugging
|
|
# return '* '
|
|
if pname != None:
|
|
return '* [[VUID-%s-%s-%s]] ' % (blockname, pname, category)
|
|
else:
|
|
return '* [[VUID-%s-%s]] ' % (blockname, category)
|
|
|
|
#
|
|
# Generate an include file
|
|
#
|
|
# directory - subdirectory to put file in
|
|
# basename - base name of the file
|
|
# contents - contents of the file (Asciidoc boilerplate aside)
|
|
def writeInclude(self, directory, basename, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes):
|
|
# Create subdirectory, if needed
|
|
directory = self.genOpts.directory + '/' + directory
|
|
self.makeDir(directory)
|
|
|
|
# Create validity file
|
|
filename = directory + '/' + basename + '.txt'
|
|
self.logMsg('diag', '# Generating include file:', filename)
|
|
|
|
fp = open(filename, 'w', encoding='utf-8')
|
|
# Asciidoc anchor
|
|
write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
|
|
|
|
# Valid Usage
|
|
if validity is not None:
|
|
write('.Valid Usage (Implicit)', file=fp)
|
|
write('****', file=fp)
|
|
write(validity, file=fp, end='')
|
|
write('****', file=fp)
|
|
write('', file=fp)
|
|
|
|
# Host Synchronization
|
|
if threadsafety is not None:
|
|
write('.Host Synchronization', file=fp)
|
|
write('****', file=fp)
|
|
write(threadsafety, file=fp, end='')
|
|
write('****', file=fp)
|
|
write('', file=fp)
|
|
|
|
# Command Properties - contained within a block, to avoid table numbering
|
|
if commandpropertiesentry is not None:
|
|
write('.Command Properties', file=fp)
|
|
write('****', file=fp)
|
|
write('[options="header", width="100%"]', file=fp)
|
|
write('|====', file=fp)
|
|
write('|<<VkCommandBufferLevel,Command Buffer Levels>>|<<vkCmdBeginRenderPass,Render Pass Scope>>|<<VkQueueFlagBits,Supported Queue Types>>|<<synchronization-pipeline-stages-types,Pipeline Type>>', file=fp)
|
|
write(commandpropertiesentry, file=fp)
|
|
write('|====', file=fp)
|
|
write('****', file=fp)
|
|
write('', file=fp)
|
|
|
|
# Success Codes - contained within a block, to avoid table numbering
|
|
if successcodes is not None or errorcodes is not None:
|
|
write('.Return Codes', file=fp)
|
|
write('****', file=fp)
|
|
if successcodes is not None:
|
|
write('ifndef::doctype-manpage[]', file=fp)
|
|
write('<<fundamentals-successcodes,Success>>::', file=fp)
|
|
write('endif::doctype-manpage[]', file=fp)
|
|
write('ifdef::doctype-manpage[]', file=fp)
|
|
write('On success, this command returns::', file=fp)
|
|
write('endif::doctype-manpage[]', file=fp)
|
|
write(successcodes, file=fp)
|
|
if errorcodes is not None:
|
|
write('ifndef::doctype-manpage[]', file=fp)
|
|
write('<<fundamentals-errorcodes,Failure>>::', file=fp)
|
|
write('endif::doctype-manpage[]', file=fp)
|
|
write('ifdef::doctype-manpage[]', file=fp)
|
|
write('On failure, this command returns::', file=fp)
|
|
write('endif::doctype-manpage[]', file=fp)
|
|
write(errorcodes, file=fp)
|
|
write('****', file=fp)
|
|
write('', file=fp)
|
|
|
|
fp.close()
|
|
|
|
#
|
|
# Check if the parameter passed in is a pointer
|
|
def paramIsPointer(self, param):
|
|
ispointer = False
|
|
paramtype = param.find('type')
|
|
if paramtype.tail is not None and '*' in paramtype.tail:
|
|
ispointer = True
|
|
|
|
return ispointer
|
|
|
|
#
|
|
# Check if the parameter passed in is a static array
|
|
def paramIsStaticArray(self, param):
|
|
if param.find('name').tail is not None:
|
|
if param.find('name').tail[0] == '[':
|
|
return True
|
|
|
|
#
|
|
# Get the length of a parameter that's been identified as a static array
|
|
def staticArrayLength(self, param):
|
|
paramname = param.find('name')
|
|
paramenumsize = param.find('enum')
|
|
|
|
if paramenumsize is not None:
|
|
return paramenumsize.text
|
|
else:
|
|
return paramname.tail[1:-1]
|
|
|
|
#
|
|
# Check if the parameter passed in is a pointer to an array
|
|
def paramIsArray(self, param):
|
|
return param.attrib.get('len') is not None
|
|
|
|
#
|
|
# Get the parent of a handle object
|
|
def getHandleParent(self, typename):
|
|
types = self.registry.tree.findall("types/type")
|
|
for elem in types:
|
|
if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
|
|
return elem.attrib.get('parent')
|
|
|
|
return None
|
|
|
|
#
|
|
# Get the ancestors of a handle object
|
|
def getHandleAncestors(self, typename):
|
|
ancestors = []
|
|
current = typename
|
|
while True:
|
|
current = self.getHandleParent(current)
|
|
if current is None:
|
|
return ancestors
|
|
ancestors.append(current)
|
|
|
|
#
|
|
# Get the ancestors of a handle object
|
|
def getHandleDispatchableAncestors(self, typename):
|
|
ancestors = []
|
|
current = typename
|
|
while True:
|
|
current = self.getHandleParent(current)
|
|
if current is None:
|
|
return ancestors
|
|
if self.isHandleTypeDispatchable(current):
|
|
ancestors.append(current)
|
|
|
|
#
|
|
# Check if a parent object is dispatchable or not
|
|
def isHandleTypeDispatchable(self, handlename):
|
|
handle = self.registry.tree.find("types/type/[name='" + handlename + "'][@category='handle']")
|
|
if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def isHandleOptional(self, param, params):
|
|
|
|
# See if the handle is optional
|
|
isOptional = False
|
|
|
|
# Simple, if it's optional, return true
|
|
if param.attrib.get('optional') is not None:
|
|
return True
|
|
|
|
# If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
|
|
if param.attrib.get('noautovalidity') is not None:
|
|
return True
|
|
|
|
# If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
|
|
if self.paramIsArray(param):
|
|
lengths = param.attrib.get('len').split(',')
|
|
for length in lengths:
|
|
if (length) != 'null-terminated' and (length) != '1':
|
|
for otherparam in params:
|
|
if otherparam.find('name').text == length:
|
|
if otherparam.attrib.get('optional') is not None:
|
|
return True
|
|
|
|
return False
|
|
#
|
|
# Get the category of a type
|
|
def getTypeCategory(self, typename):
|
|
types = self.registry.tree.findall("types/type")
|
|
for elem in types:
|
|
if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
|
|
return elem.attrib.get('category')
|
|
|
|
#
|
|
# Make a chunk of text for the end of a parameter if it is an array
|
|
def makeAsciiDocPreChunk(self, blockname, param, params):
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
# General pre-amble. Check optionality and add stuff.
|
|
asciidoc = self.makeAnchor(blockname, paramname.text, 'parameter')
|
|
|
|
if self.paramIsStaticArray(param):
|
|
asciidoc += 'Any given element of '
|
|
|
|
elif self.paramIsArray(param):
|
|
lengths = param.attrib.get('len').split(',')
|
|
|
|
# Find all the parameters that are called out as optional, so we can document that they might be zero, and the array may be ignored
|
|
optionallengths = []
|
|
for length in lengths:
|
|
if (length) != 'null-terminated' and (length) != '1':
|
|
for otherparam in params:
|
|
if otherparam.find('name').text == length:
|
|
if otherparam.attrib.get('optional') is not None:
|
|
if self.paramIsPointer(otherparam):
|
|
optionallengths.append('the value referenced by ' + self.makeParameterName(length))
|
|
else:
|
|
optionallengths.append(self.makeParameterName(length))
|
|
|
|
# Document that these arrays may be ignored if any of the length values are 0
|
|
if len(optionallengths) != 0 or param.attrib.get('optional') is not None:
|
|
asciidoc += 'If '
|
|
|
|
|
|
if len(optionallengths) != 0:
|
|
if len(optionallengths) == 1:
|
|
|
|
asciidoc += optionallengths[0]
|
|
asciidoc += ' is '
|
|
|
|
else:
|
|
asciidoc += ', or '.join(optionallengths)
|
|
asciidoc += ' are '
|
|
|
|
asciidoc += 'not `0`, '
|
|
|
|
if len(optionallengths) != 0 and param.attrib.get('optional') is not None:
|
|
asciidoc += 'and '
|
|
|
|
if param.attrib.get('optional') is not None:
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' is not `NULL`, '
|
|
|
|
elif param.attrib.get('optional') is not None:
|
|
# Don't generate this stub for bitflags
|
|
if self.getTypeCategory(paramtype.text) != 'bitmask':
|
|
if param.attrib.get('optional').split(',')[0] == 'true':
|
|
asciidoc += 'If '
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' is not '
|
|
if self.paramIsArray(param) or self.paramIsPointer(param) or self.isHandleTypeDispatchable(paramtype.text):
|
|
asciidoc += '`NULL`'
|
|
elif self.getTypeCategory(paramtype.text) == 'handle':
|
|
asciidoc += 'dlink:VK_NULL_HANDLE'
|
|
else:
|
|
asciidoc += '`0`'
|
|
|
|
asciidoc += ', '
|
|
|
|
return asciidoc
|
|
|
|
#
|
|
# Make the generic asciidoc line chunk portion used for all parameters.
|
|
# May return an empty string if nothing to validate.
|
|
def createValidationLineForParameterIntroChunk(self, blockname, param, params, typetext):
|
|
asciidoc = ''
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
asciidoc += self.makeAsciiDocPreChunk(blockname, param, params)
|
|
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' must: be '
|
|
|
|
if self.paramIsArray(param):
|
|
# Arrays. These are hard to get right, apparently
|
|
|
|
lengths = param.attrib.get('len').split(',')
|
|
|
|
if (lengths[0]) == 'null-terminated':
|
|
asciidoc += 'a null-terminated '
|
|
elif (lengths[0]) == '1':
|
|
asciidoc += 'a valid pointer to '
|
|
else:
|
|
asciidoc += 'a valid pointer to an array of '
|
|
|
|
# Handle equations, which are currently denoted with latex
|
|
if 'latexmath:' in lengths[0]:
|
|
asciidoc += lengths[0]
|
|
else:
|
|
asciidoc += self.makeParameterName(lengths[0])
|
|
asciidoc += ' '
|
|
|
|
for length in lengths[1:]:
|
|
if (length) == 'null-terminated': # This should always be the last thing. If it ever isn't for some bizarre reason, then this will need some massaging.
|
|
asciidoc += 'null-terminated '
|
|
elif (length) == '1':
|
|
asciidoc += 'valid pointers to '
|
|
else:
|
|
asciidoc += 'valid pointers to arrays of '
|
|
# Handle equations, which are currently denoted with latex
|
|
if 'latexmath:' in length:
|
|
asciidoc += length
|
|
else:
|
|
asciidoc += self.makeParameterName(length)
|
|
asciidoc += ' '
|
|
|
|
# Void pointers don't actually point at anything - remove the word "to"
|
|
if paramtype.text == 'void':
|
|
if lengths[-1] == '1':
|
|
if len(lengths) > 1:
|
|
asciidoc = asciidoc[:-5] # Take care of the extra s added by the post array chunk function. #HACK#
|
|
else:
|
|
asciidoc = asciidoc[:-4]
|
|
else:
|
|
# An array of void values is a byte array.
|
|
asciidoc += 'byte'
|
|
|
|
elif paramtype.text == 'char':
|
|
# A null terminated array of chars is a string
|
|
if lengths[-1] == 'null-terminated':
|
|
asciidoc += 'UTF-8 string'
|
|
else:
|
|
# Else it's just a bunch of chars
|
|
asciidoc += 'char value'
|
|
elif param.text is not None:
|
|
# If a value is "const" that means it won't get modified, so it must be valid going into the function.
|
|
if 'const' in param.text:
|
|
typecategory = self.getTypeCategory(paramtype.text)
|
|
if (typecategory != 'struct' and typecategory != 'union' and typecategory != 'basetype' and typecategory is not None) or not self.isStructAlwaysValid(blockname, paramtype.text):
|
|
asciidoc += 'valid '
|
|
|
|
asciidoc += typetext
|
|
|
|
# pluralize
|
|
if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'):
|
|
asciidoc += 's'
|
|
|
|
elif self.paramIsPointer(param):
|
|
# Handle pointers - which are really special case arrays (i.e. they don't have a length)
|
|
#TODO should do something here if someone ever uses some intricate comma-separated `optional`
|
|
pointercount = paramtype.tail.count('*')
|
|
|
|
# Treat void* as an int
|
|
if paramtype.text == 'void':
|
|
pointercount -= 1
|
|
|
|
# Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that.
|
|
asciidoc += 'a '
|
|
for i in range(0, pointercount):
|
|
asciidoc += 'valid pointer to a '
|
|
|
|
# Handle void* and pointers to it
|
|
if paramtype.text == 'void':
|
|
# If there is only void*, it is just optional int - we don't need any language.
|
|
if pointercount == 0 and param.attrib.get('optional') is not None:
|
|
return '' # early return
|
|
else:
|
|
if param.attrib.get('optional').split(',')[pointercount] is not None:
|
|
# The last void* is just optional int (e.g. to be filled by the impl.)
|
|
typetext = 'pointer value'
|
|
|
|
|
|
# If a value is "const" that means it won't get modified, so it must be valid going into the function.
|
|
if param.text is not None and paramtype.text != 'void':
|
|
if 'const' in param.text:
|
|
asciidoc += 'valid '
|
|
|
|
asciidoc += typetext
|
|
|
|
else:
|
|
# Non-pointer, non-optional things must be valid
|
|
asciidoc += 'a valid '
|
|
asciidoc += typetext
|
|
|
|
if asciidoc != '':
|
|
asciidoc += '\n'
|
|
|
|
# Add additional line for non-optional bitmasks
|
|
if self.getTypeCategory(paramtype.text) == 'bitmask':
|
|
isMandatory = param.attrib.get('optional') is None #TODO does not really handle if someone tries something like optional="true,false"
|
|
if isMandatory:
|
|
asciidoc += self.makeAnchor(blockname, paramname.text, 'requiredbitmask')
|
|
if self.paramIsArray(param):
|
|
asciidoc += 'Each element of '
|
|
asciidoc += 'pname:'
|
|
asciidoc += paramname.text
|
|
asciidoc += ' must: not be `0`'
|
|
asciidoc += '\n'
|
|
|
|
return asciidoc
|
|
|
|
def makeAsciiDocLineForParameter(self, blockname, param, params, typetext):
|
|
if param.attrib.get('noautovalidity') is not None:
|
|
return ''
|
|
asciidoc = self.createValidationLineForParameterIntroChunk(blockname, param, params, typetext)
|
|
|
|
return asciidoc
|
|
|
|
# Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance)
|
|
def isStructAlwaysValid(self, blockname, structname):
|
|
|
|
struct = self.registry.tree.find("types/type[@name='" + structname + "']")
|
|
|
|
params = struct.findall('member')
|
|
|
|
for param in params:
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
typecategory = self.getTypeCategory(paramtype.text)
|
|
|
|
if paramname.text == 'pNext':
|
|
return False
|
|
|
|
if paramname.text == 'sType':
|
|
return False
|
|
|
|
if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param):
|
|
if self.makeAsciiDocLineForParameter(blockname, param, params, '') != '':
|
|
return False
|
|
elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask':
|
|
return False
|
|
elif typecategory == 'struct' or typecategory == 'union':
|
|
if self.isStructAlwaysValid(blockname, paramtype.text) is False:
|
|
return False
|
|
|
|
return True
|
|
|
|
#
|
|
# Make an entire asciidoc line for a given parameter
|
|
def createValidationLineForParameter(self, blockname, param, params, typecategory):
|
|
asciidoc = ''
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
if paramtype.text == 'void' or paramtype.text == 'char':
|
|
# Chars and void are special cases - needs care inside the generator functions
|
|
# A null-terminated char array is a string, else it's chars.
|
|
# An array of void values is a byte array, a void pointer is just a pointer to nothing in particular
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, '')
|
|
elif typecategory == 'bitmask':
|
|
bitsname = paramtype.text.replace('Flags', 'FlagBits')
|
|
if self.registry.tree.find("enums[@name='" + bitsname + "']") is None:
|
|
asciidoc += self.makeAnchor(blockname, paramname.text, 'zerobitmask')
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' must: be `0`'
|
|
asciidoc += '\n'
|
|
else:
|
|
if self.paramIsArray(param):
|
|
#
|
|
if param.text is not None and 'const' in param.text:
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value')
|
|
else:
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, self.makeEnumerationName(paramtype.text) + ' value')
|
|
elif self.paramIsPointer(param):
|
|
if param.text is not None and 'const' in param.text:
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values')
|
|
else:
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, self.makeEnumerationName(paramtype.text) + ' value')
|
|
else:
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values')
|
|
elif typecategory == 'handle':
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, self.makeStructName(paramtype.text) + ' handle')
|
|
elif typecategory == 'enum':
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, self.makeEnumerationName(paramtype.text) + ' value')
|
|
elif typecategory == 'struct':
|
|
if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(blockname, paramtype.text):
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, self.makeStructName(paramtype.text) + ' structure')
|
|
elif typecategory == 'union':
|
|
if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(blockname, paramtype.text):
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, self.makeStructName(paramtype.text) + ' union')
|
|
elif self.paramIsArray(param) or self.paramIsPointer(param):
|
|
asciidoc += self.makeAsciiDocLineForParameter(blockname, param, params, self.makeBaseTypeName(paramtype.text) + ' value')
|
|
|
|
return asciidoc
|
|
|
|
#
|
|
# Make an asciidoc validity entry for a handle's parent object
|
|
def makeAsciiDocHandleParent(self, blockname, param, params):
|
|
asciidoc = ''
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
# Deal with handle parents
|
|
handleparent = self.getHandleParent(paramtype.text)
|
|
if handleparent is not None:
|
|
parentreference = None
|
|
for otherparam in params:
|
|
if otherparam.find('type').text == handleparent:
|
|
parentreference = otherparam.find('name').text
|
|
if parentreference is not None:
|
|
asciidoc += self.makeAnchor(blockname, paramname.text, 'parent')
|
|
|
|
if self.isHandleOptional(param, params):
|
|
if self.paramIsArray(param):
|
|
asciidoc += 'Each element of '
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' that is a valid handle'
|
|
else:
|
|
asciidoc += 'If '
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' is a valid handle, it'
|
|
else:
|
|
if self.paramIsArray(param):
|
|
asciidoc += 'Each element of '
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' must: have been created, allocated, or retrieved from '
|
|
asciidoc += self.makeParameterName(parentreference)
|
|
|
|
asciidoc += '\n'
|
|
return asciidoc
|
|
|
|
#
|
|
# Make an asciidoc validity entry for a common ancestors between handles
|
|
def makeAsciiDocHandlesCommonAncestor(self, blockname, handles, params):
|
|
asciidoc = ''
|
|
|
|
if len(handles) > 1:
|
|
ancestormap = {}
|
|
anyoptional = False
|
|
|
|
# Find all the ancestors
|
|
for param in handles:
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
|
|
ancestors = self.getHandleDispatchableAncestors(paramtype.text)
|
|
|
|
ancestormap[param] = ancestors
|
|
|
|
anyoptional |= self.isHandleOptional(param, params)
|
|
|
|
# Remove redundant ancestor lists
|
|
for param in handles:
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
removals = []
|
|
for ancestors in ancestormap.items():
|
|
if paramtype.text in ancestors[1]:
|
|
removals.append(ancestors[0])
|
|
|
|
if removals != []:
|
|
for removal in removals:
|
|
del(ancestormap[removal])
|
|
|
|
# Intersect
|
|
|
|
if len(ancestormap.values()) > 1:
|
|
current = list(ancestormap.values())[0]
|
|
for ancestors in list(ancestormap.values())[1:]:
|
|
current = [val for val in current if val in ancestors]
|
|
|
|
if len(current) > 0:
|
|
commonancestor = current[0]
|
|
|
|
if len(ancestormap.keys()) > 1:
|
|
|
|
asciidoc += self.makeAnchor(blockname, None, 'commonparent')
|
|
|
|
parametertexts = []
|
|
for param in ancestormap.keys():
|
|
paramname = param.find('name')
|
|
parametertext = self.makeParameterName(paramname.text)
|
|
if self.paramIsArray(param):
|
|
parametertext = 'the elements of ' + parametertext
|
|
parametertexts.append(parametertext)
|
|
|
|
parametertexts.sort()
|
|
|
|
if len(parametertexts) > 2:
|
|
asciidoc += 'Each of '
|
|
else:
|
|
asciidoc += 'Both of '
|
|
|
|
asciidoc += ", ".join(parametertexts[:-1])
|
|
asciidoc += ', and '
|
|
asciidoc += parametertexts[-1]
|
|
if anyoptional is True:
|
|
asciidoc += ' that are valid handles'
|
|
asciidoc += ' must: have been created, allocated, or retrieved from the same '
|
|
asciidoc += self.makeStructName(commonancestor)
|
|
asciidoc += '\n'
|
|
|
|
return asciidoc
|
|
|
|
#
|
|
# Generate an asciidoc validity line for the sType value of a struct
|
|
def makeStructureType(self, blockname, param):
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
asciidoc = self.makeAnchor(blockname, paramname.text, 'sType')
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' must: be '
|
|
|
|
values = param.attrib.get('values')
|
|
if values:
|
|
# Extract each enumerant value. They could be validated in the
|
|
# same fashion as validextensionstructs in
|
|
# makeStructureExtensionPointer, although that's not relevant in
|
|
# the current extension struct model.
|
|
valuelist = [ self.makeEnumerantName(v) for v in values.split(',') ]
|
|
else:
|
|
structuretype = ''
|
|
for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname):
|
|
if elem[0] == 'Vk':
|
|
structuretype += 'VK_STRUCTURE_TYPE_'
|
|
else:
|
|
structuretype += elem[0].upper()
|
|
structuretype += '_'
|
|
valuelist = [ self.makeEnumerantName(structuretype[:-1]) ]
|
|
|
|
if len(valuelist) > 0:
|
|
if len(valuelist) == 1:
|
|
asciidoc += valuelist[0]
|
|
else:
|
|
asciidoc += (', ').join(valuelist[:-1]) + ', or ' + valuelist[-1]
|
|
asciidoc += '\n'
|
|
|
|
return asciidoc
|
|
|
|
#
|
|
# Generate an asciidoc validity line for the pNext value of a struct
|
|
def makeStructureExtensionPointer(self, blockname, param):
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
if (param.attrib.get('validextensionstructs') is not None):
|
|
self.logMsg('warn', blockname, 'validextensionstructs is deprecated/removed', '\n')
|
|
|
|
asciidoc = self.makeAnchor(blockname, paramname.text, 'pNext')
|
|
validextensionstructs = self.registry.validextensionstructs.get(blockname)
|
|
extensionstructs = []
|
|
|
|
if validextensionstructs is not None:
|
|
# Check each structure name and skip it if not required by the
|
|
# generator. This allows tagging extension structs in the XML
|
|
# that are only included in validity when needed for the spec
|
|
# being targeted.
|
|
for struct in validextensionstructs:
|
|
# Unpleasantly breaks encapsulation. Should be a method in the registry class
|
|
type = self.registry.lookupElementInfo(struct, self.registry.typedict)
|
|
if (type.required):
|
|
extensionstructs.append('slink:' + struct)
|
|
else:
|
|
self.logMsg('diag', 'makeStructureExtensionPointer: struct', struct, 'IS NOT required')
|
|
|
|
if len(extensionstructs) == 0:
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' must: be `NULL`'
|
|
elif len(extensionstructs) == 1:
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' must: be `NULL` or a pointer to a valid instance of '
|
|
asciidoc += extensionstructs[0]
|
|
else:
|
|
asciidoc += 'Each '
|
|
asciidoc += self.makeParameterName(paramname.text)
|
|
asciidoc += ' member of any structure (including this one) in the pname:pNext chain must: be either `NULL` or a pointer to a valid instance of '
|
|
|
|
if len(extensionstructs) == 2:
|
|
asciidoc += extensionstructs[0] + ' or ' + extensionstructs[1]
|
|
else:
|
|
asciidoc += (', ').join(extensionstructs[:-1]) + ', or ' + extensionstructs[-1]
|
|
asciidoc += '\n'
|
|
|
|
asciidoc += self.makeAnchor(blockname, 'sType', 'unique')
|
|
asciidoc += 'Each pname:sType member in the pname:pNext chain must: be unique'
|
|
|
|
asciidoc += '\n'
|
|
|
|
return asciidoc
|
|
|
|
#
|
|
# Generate all the valid usage information for a given struct that's only ever filled out by the implementation other than sType and pNext
|
|
def makeValidUsageStatementsReturnedOnly(self, cmd, blockname, params):
|
|
# Start the asciidoc block for this
|
|
asciidoc = ''
|
|
|
|
for param in params:
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
# Valid usage ID tags (VUID) are generated for various
|
|
# conditions based on the name of the block (structure or
|
|
# command), name of the element (member or parameter), and type
|
|
# of VU statement.
|
|
|
|
# Get the type's category
|
|
typecategory = self.getTypeCategory(paramtype.text)
|
|
|
|
if param.attrib.get('noautovalidity') is None:
|
|
# Generate language to independently validate a parameter
|
|
if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
|
|
asciidoc += self.makeStructureType(blockname, param)
|
|
elif paramname.text == 'pNext' and paramtype.text == 'void' and cmd.attrib.get('structextends') is None:
|
|
asciidoc += self.makeStructureExtensionPointer(blockname, param)
|
|
|
|
# In case there's nothing to report, return None
|
|
if asciidoc == '':
|
|
return None
|
|
|
|
return asciidoc
|
|
|
|
#
|
|
# Generate all the valid usage information for a given struct or command
|
|
def makeValidUsageStatements(self, cmd, blockname, params):
|
|
# Start the asciidoc block for this
|
|
asciidoc = ''
|
|
|
|
handles = []
|
|
anyparentedhandlesoptional = False
|
|
parentdictionary = {}
|
|
arraylengths = set()
|
|
for param in params:
|
|
paramname = param.find('name')
|
|
paramtype = param.find('type')
|
|
|
|
# Valid usage ID tags (VUID) are generated for various
|
|
# conditions based on the name of the block (structure or
|
|
# command), name of the element (member or parameter), and type
|
|
# of VU statement.
|
|
|
|
# Get the type's category
|
|
typecategory = self.getTypeCategory(paramtype.text)
|
|
|
|
if param.attrib.get('noautovalidity') is None:
|
|
# Generate language to independently validate a parameter
|
|
if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
|
|
asciidoc += self.makeStructureType(blockname, param)
|
|
elif paramtype.text == 'void' and paramname.text == 'pNext':
|
|
if cmd.attrib.get('structextends') is None:
|
|
asciidoc += self.makeStructureExtensionPointer(blockname, param)
|
|
else:
|
|
asciidoc += self.createValidationLineForParameter(blockname, param, params, typecategory)
|
|
|
|
# Ensure that any parenting is properly validated, and list that a handle was found
|
|
if typecategory == 'handle':
|
|
handles.append(param)
|
|
|
|
# Get the array length for this parameter
|
|
arraylength = param.attrib.get('len')
|
|
if arraylength is not None:
|
|
for onelength in arraylength.split(','):
|
|
arraylengths.add(onelength)
|
|
|
|
# For any vkQueue* functions, there might be queue type data
|
|
if 'vkQueue' in blockname:
|
|
# The queue type must be valid
|
|
queuetypes = cmd.attrib.get('queues')
|
|
if queuetypes is not None:
|
|
queuebits = []
|
|
for queuetype in re.findall(r'([^,]+)', queuetypes):
|
|
queuebits.append(queuetype.replace('_',' '))
|
|
|
|
asciidoc += self.makeAnchor(blockname, None, 'queuetype')
|
|
asciidoc += 'The pname:queue must: support '
|
|
if len(queuebits) == 1:
|
|
asciidoc += queuebits[0]
|
|
else:
|
|
asciidoc += (', ').join(queuebits[:-1])
|
|
asciidoc += ', or '
|
|
asciidoc += queuebits[-1]
|
|
asciidoc += ' operations'
|
|
asciidoc += '\n'
|
|
|
|
if 'vkCmd' in blockname:
|
|
# The commandBuffer parameter must be being recorded
|
|
asciidoc += self.makeAnchor(blockname, 'commandBuffer', 'recording')
|
|
asciidoc += 'pname:commandBuffer must: be in the <<commandbuffers-lifecycle, recording state>>'
|
|
asciidoc += '\n'
|
|
|
|
#
|
|
# Start of valid queue type validation - command pool must have been
|
|
# allocated against a queue with at least one of the valid queue types
|
|
asciidoc += self.makeAnchor(blockname, 'commandBuffer', 'cmdpool')
|
|
|
|
#
|
|
# This test for vkCmdFillBuffer is a hack, since we have no path
|
|
# to conditionally have queues enabled or disabled by an extension.
|
|
# As the VU stuff is all moving out (hopefully soon), this hack solves the issue for now
|
|
if blockname == 'vkCmdFillBuffer':
|
|
if 'VK_KHR_maintenance1' in self.registry.requiredextensions:
|
|
asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support transfer, graphics or compute operations\n'
|
|
else:
|
|
asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support graphics or compute operations\n'
|
|
else:
|
|
# The queue type must be valid
|
|
queuetypes = cmd.attrib.get('queues')
|
|
queuebits = []
|
|
for queuetype in re.findall(r'([^,]+)', queuetypes):
|
|
queuebits.append(queuetype.replace('_',' '))
|
|
|
|
asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
|
|
if len(queuebits) == 1:
|
|
asciidoc += queuebits[0]
|
|
else:
|
|
asciidoc += (', ').join(queuebits[:-1])
|
|
asciidoc += ', or '
|
|
asciidoc += queuebits[-1]
|
|
asciidoc += ' operations'
|
|
asciidoc += '\n'
|
|
|
|
# Must be called inside/outside a renderpass appropriately
|
|
renderpass = cmd.attrib.get('renderpass')
|
|
|
|
if renderpass != 'both':
|
|
asciidoc += self.makeAnchor(blockname, None, 'renderpass')
|
|
asciidoc += 'This command must: only be called '
|
|
asciidoc += renderpass
|
|
asciidoc += ' of a render pass instance'
|
|
asciidoc += '\n'
|
|
|
|
# Must be in the right level command buffer
|
|
cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
|
|
|
|
if cmdbufferlevel != 'primary,secondary':
|
|
asciidoc += self.makeAnchor(blockname, None, 'bufferlevel')
|
|
asciidoc += 'pname:commandBuffer must: be a '
|
|
asciidoc += cmdbufferlevel
|
|
asciidoc += ' sname:VkCommandBuffer'
|
|
asciidoc += '\n'
|
|
|
|
# Any non-optional arraylengths should specify they must be greater than 0
|
|
for param in params:
|
|
paramname = param.find('name')
|
|
|
|
for arraylength in arraylengths:
|
|
if paramname.text == arraylength and param.attrib.get('optional') is None:
|
|
# Get all the array dependencies
|
|
arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
|
|
|
|
# Get all the optional array dependencies, including those not generating validity for some reason
|
|
optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
|
|
optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']"))
|
|
|
|
# If arraylength can ever be not a legal part of an
|
|
# asciidoc anchor name, this will need to be altered.
|
|
asciidoc += self.makeAnchor(blockname, arraylength, 'arraylength')
|
|
|
|
# Allow lengths to be arbitrary if all their dependents are optional
|
|
if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
|
|
asciidoc += 'If '
|
|
if len(optionalarrays) > 1:
|
|
asciidoc += 'any of '
|
|
|
|
for array in optionalarrays[:-1]:
|
|
asciidoc += self.makeParameterName(optionalarrays.find('name').text)
|
|
asciidoc += ', '
|
|
|
|
if len(optionalarrays) > 1:
|
|
asciidoc += 'and '
|
|
asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
|
|
asciidoc += ' are '
|
|
else:
|
|
asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
|
|
asciidoc += ' is '
|
|
|
|
asciidoc += 'not `NULL`, '
|
|
|
|
if self.paramIsPointer(param):
|
|
asciidoc += 'the value referenced by '
|
|
|
|
elif self.paramIsPointer(param):
|
|
asciidoc += 'The value referenced by '
|
|
|
|
asciidoc += self.makeParameterName(arraylength)
|
|
asciidoc += ' must: be greater than `0`'
|
|
asciidoc += '\n'
|
|
|
|
# Find the parents of all objects referenced in this command
|
|
for param in handles:
|
|
paramtype = param.find('type')
|
|
# Don't detect a parent for return values!
|
|
if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
|
|
|
|
parent = self.getHandleParent(paramtype.text)
|
|
|
|
if parent is not None:
|
|
asciidoc += self.makeAsciiDocHandleParent(blockname, param, params)
|
|
|
|
# Find the common ancestor of all objects referenced in this command
|
|
asciidoc += self.makeAsciiDocHandlesCommonAncestor(blockname, handles, params)
|
|
|
|
# In case there's nothing to report, return None
|
|
if asciidoc == '':
|
|
return None
|
|
# Delimit the asciidoc block
|
|
return asciidoc
|
|
|
|
def makeThreadSafetyBlock(self, cmd, paramtext):
|
|
"""Generate C function pointer typedef for <command> Element"""
|
|
paramdecl = ''
|
|
|
|
# Find and add any parameters that are thread unsafe
|
|
explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
|
|
if (explicitexternsyncparams is not None):
|
|
for param in explicitexternsyncparams:
|
|
externsyncattribs = param.attrib.get('externsync')
|
|
paramname = param.find('name')
|
|
for externsyncattrib in externsyncattribs.split(','):
|
|
paramdecl += '* '
|
|
paramdecl += 'Host access to '
|
|
if externsyncattrib == 'true':
|
|
if self.paramIsArray(param):
|
|
paramdecl += 'each member of ' + self.makeParameterName(paramname.text)
|
|
elif self.paramIsPointer(param):
|
|
paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text)
|
|
else:
|
|
paramdecl += self.makeParameterName(paramname.text)
|
|
else:
|
|
paramdecl += 'pname:'
|
|
paramdecl += externsyncattrib
|
|
paramdecl += ' must: be externally synchronized\n'
|
|
|
|
# For any vkCmd* functions, the command pool is externally synchronized
|
|
if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name').text:
|
|
paramdecl += '* '
|
|
paramdecl += 'Host access to the sname:VkCommandPool that pname:commandBuffer was allocated from must: be externally synchronized'
|
|
paramdecl += '\n'
|
|
|
|
# Find and add any "implicit" parameters that are thread unsafe
|
|
implicitexternsyncparams = cmd.find('implicitexternsyncparams')
|
|
if (implicitexternsyncparams is not None):
|
|
for elem in implicitexternsyncparams:
|
|
paramdecl += '* '
|
|
paramdecl += 'Host access to '
|
|
paramdecl += elem.text
|
|
paramdecl += ' must: be externally synchronized\n'
|
|
|
|
if (paramdecl == ''):
|
|
return None
|
|
else:
|
|
return paramdecl
|
|
|
|
def makeCommandPropertiesTableEntry(self, cmd, name):
|
|
|
|
if 'vkCmd' in name:
|
|
# Must be called inside/outside a renderpass appropriately
|
|
cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
|
|
cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
|
|
|
|
renderpass = cmd.attrib.get('renderpass')
|
|
renderpass = renderpass.capitalize()
|
|
|
|
#
|
|
# This test for vkCmdFillBuffer is a hack, since we have no path
|
|
# to conditionally have queues enabled or disabled by an extension.
|
|
# As the VU stuff is all moving out (hopefully soon), this hack solves the issue for now
|
|
if name == 'vkCmdFillBuffer':
|
|
if 'VK_KHR_maintenance1' in self.registry.requiredextensions:
|
|
queues = 'Transfer + \nGraphics + \nCompute'
|
|
else:
|
|
queues = 'Graphics + \nCompute'
|
|
else:
|
|
queues = cmd.attrib.get('queues')
|
|
queues = (' + \n').join(queues.title().split(','))
|
|
|
|
pipeline = cmd.attrib.get('pipeline')
|
|
if pipeline:
|
|
pipeline = pipeline.capitalize()
|
|
else:
|
|
pipeline = ''
|
|
|
|
return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues + '|' + pipeline
|
|
elif 'vkQueue' in name:
|
|
# Must be called inside/outside a renderpass appropriately
|
|
|
|
queues = cmd.attrib.get('queues')
|
|
if queues is None:
|
|
queues = 'Any'
|
|
else:
|
|
queues = (' + \n').join(queues.upper().split(','))
|
|
|
|
return '|-|-|' + queues + '|-'
|
|
|
|
return None
|
|
|
|
# Check each enumerant name in the enums list and remove it if not
|
|
# required by the generator. This allows specifying success and error
|
|
# codes for extensions that are only included in validity when needed
|
|
# for the spec being targeted.
|
|
def findRequiredEnums(self, enums):
|
|
out = []
|
|
for enum in enums:
|
|
# Unpleasantly breaks encapsulation. Should be a method in the registry class
|
|
ei = self.registry.lookupElementInfo(enum, self.registry.enumdict)
|
|
if (ei == None):
|
|
self.logMsg('warn', 'findRequiredEnums: enum', enum, 'is in an attribute list but is not in the registry')
|
|
elif (ei.required):
|
|
out.append(enum)
|
|
else:
|
|
self.logMsg('diag', 'findRequiredEnums: enum', enum, 'IS NOT required, skipping')
|
|
return out
|
|
|
|
def makeSuccessCodes(self, cmd, name):
|
|
successcodes = cmd.attrib.get('successcodes')
|
|
if successcodes is not None:
|
|
successcodes = self.findRequiredEnums(successcodes.split(','))
|
|
if len(successcodes) > 0:
|
|
return '* ename:' + '\n* ename:'.join(successcodes)
|
|
return None
|
|
|
|
def makeErrorCodes(self, cmd, name):
|
|
errorcodes = cmd.attrib.get('errorcodes')
|
|
if errorcodes is not None:
|
|
errorcodes = self.findRequiredEnums(errorcodes.split(','))
|
|
if len(errorcodes) > 0:
|
|
return '* ename:' + '\n* ename:'.join(errorcodes)
|
|
return None
|
|
|
|
#
|
|
# Command generation
|
|
def genCmd(self, cmdinfo, name, alias):
|
|
OutputGenerator.genCmd(self, cmdinfo, name, alias)
|
|
|
|
# @@@ (Jon) something needs to be done here to handle aliases, probably
|
|
|
|
#
|
|
# Get all the parameters
|
|
params = cmdinfo.elem.findall('param')
|
|
|
|
validity = self.makeValidUsageStatements(cmdinfo.elem, name, params)
|
|
threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
|
|
commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name)
|
|
successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
|
|
errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
|
|
|
|
self.writeInclude('protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
|
|
|
|
#
|
|
# Struct Generation
|
|
def genStruct(self, typeinfo, typename, alias):
|
|
OutputGenerator.genStruct(self, typeinfo, typename, alias)
|
|
|
|
# @@@ (Jon) something needs to be done here to handle aliases, probably
|
|
|
|
# Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
|
|
if typeinfo.elem.attrib.get('returnedonly') is None:
|
|
params = typeinfo.elem.findall('member')
|
|
|
|
validity = self.makeValidUsageStatements(typeinfo.elem, typename, params)
|
|
threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
|
|
|
|
self.writeInclude('structs', typename, validity, threadsafety, None, None, None)
|
|
else:
|
|
# Need to generate sType and pNext validation
|
|
params = typeinfo.elem.findall('member')
|
|
|
|
validity = self.makeValidUsageStatementsReturnedOnly(typeinfo.elem, typename, params)
|
|
|
|
self.writeInclude('structs', typename, validity, None, None, None, None)
|
|
|
|
#
|
|
# Group (e.g. C "enum" type) generation.
|
|
# For the validity generator, this just tags individual enumerants
|
|
# as required or not.
|
|
def genGroup(self, groupinfo, groupName, alias):
|
|
OutputGenerator.genGroup(self, groupinfo, groupName, alias)
|
|
|
|
# @@@ (Jon) something needs to be done here to handle aliases, probably
|
|
|
|
groupElem = groupinfo.elem
|
|
|
|
# Loop over the nested 'enum' tags. Keep track of the minimum and
|
|
# maximum numeric values, if they can be determined; but only for
|
|
# core API enumerants, not extension enumerants. This is inferred
|
|
# by looking for 'extends' attributes.
|
|
for elem in groupElem.findall('enum'):
|
|
name = elem.get('name')
|
|
ei = self.registry.lookupElementInfo(name, self.registry.enumdict)
|
|
|
|
# Tag enumerant as required or not
|
|
ei.required = self.isEnumRequired(elem)
|
|
#
|
|
# Type Generation
|
|
def genType(self, typeinfo, typename, alias):
|
|
OutputGenerator.genType(self, typeinfo, typename, alias)
|
|
|
|
# @@@ (Jon) something needs to be done here to handle aliases, probably
|
|
|
|
category = typeinfo.elem.get('category')
|
|
if (category == 'struct' or category == 'union'):
|
|
self.genStruct(typeinfo, typename, alias)
|