early-access version 1866
This commit is contained in:
246
externals/Vulkan-Headers/registry/generator.py
vendored
246
externals/Vulkan-Headers/registry/generator.py
vendored
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/python3 -i
|
||||
#
|
||||
# Copyright (c) 2013-2020 The Khronos Group Inc.
|
||||
# Copyright 2013-2021 The Khronos Group Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Base class for source/header/doc generators, as well as some utility functions."""
|
||||
@@ -118,6 +118,7 @@ class GeneratorOptions:
|
||||
addExtensions=None,
|
||||
removeExtensions=None,
|
||||
emitExtensions=None,
|
||||
emitSpirv=None,
|
||||
reparentEnums=True,
|
||||
sortProcedure=regSortFeatures):
|
||||
"""Constructor.
|
||||
@@ -148,6 +149,9 @@ class GeneratorOptions:
|
||||
- emitExtensions - regex matching names of extensions to actually emit
|
||||
interfaces for (though all requested versions are considered when
|
||||
deciding which interfaces to generate).
|
||||
to None.
|
||||
- emitSpirv - regex matching names of extensions and capabilities
|
||||
to actually emit interfaces for.
|
||||
- reparentEnums - move <enum> elements which extend an enumerated
|
||||
type from <feature> or <extension> elements to the target <enums>
|
||||
element. This is required for almost all purposes, but the
|
||||
@@ -209,6 +213,10 @@ class GeneratorOptions:
|
||||
interfaces for (though all requested versions are considered when
|
||||
deciding which interfaces to generate)."""
|
||||
|
||||
self.emitSpirv = self.emptyRegex(emitSpirv)
|
||||
"""regex matching names of extensions and capabilities
|
||||
to actually emit interfaces for."""
|
||||
|
||||
self.reparentEnums = reparentEnums
|
||||
"""boolean specifying whether to remove <enum> elements from
|
||||
<feature> or <extension> when extending an <enums> type."""
|
||||
@@ -298,7 +306,7 @@ class OutputGenerator:
|
||||
raise UserWarning(
|
||||
'*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
|
||||
|
||||
def enumToValue(self, elem, needsNum):
|
||||
def enumToValue(self, elem, needsNum, bitwidth = 32, forceSuffix = False):
|
||||
"""Parse and convert an `<enum>` tag into a value.
|
||||
|
||||
Returns a list:
|
||||
@@ -334,6 +342,11 @@ class OutputGenerator:
|
||||
# t = enuminfo.elem.get('type')
|
||||
# if t is not None and t != '' and t != 'i' and t != 's':
|
||||
# value += enuminfo.type
|
||||
if forceSuffix:
|
||||
if bitwidth == 64:
|
||||
value = value + 'ULL'
|
||||
else:
|
||||
value = value + 'U'
|
||||
self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
|
||||
return [numVal, value]
|
||||
if 'bitpos' in elem.keys():
|
||||
@@ -341,8 +354,10 @@ class OutputGenerator:
|
||||
bitpos = int(value, 0)
|
||||
numVal = 1 << bitpos
|
||||
value = '0x%08x' % numVal
|
||||
if bitpos >= 32:
|
||||
value = value + 'ULL'
|
||||
if bitwidth == 64:
|
||||
value = value + 'ULL'
|
||||
elif forceSuffix:
|
||||
value = value + 'U'
|
||||
self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
|
||||
return [numVal, value]
|
||||
if 'offset' in elem.keys():
|
||||
@@ -370,7 +385,7 @@ class OutputGenerator:
|
||||
return [None, None]
|
||||
|
||||
def checkDuplicateEnums(self, enums):
|
||||
"""Sanity check enumerated values.
|
||||
"""Check enumerated values for duplicates.
|
||||
|
||||
- enums - list of `<enum>` Elements
|
||||
|
||||
@@ -425,6 +440,12 @@ class OutputGenerator:
|
||||
# Return the list
|
||||
return stripped
|
||||
|
||||
def misracstyle(self):
|
||||
return False;
|
||||
|
||||
def misracppstyle(self):
|
||||
return False;
|
||||
|
||||
def buildEnumCDecl(self, expand, groupinfo, groupName):
|
||||
"""Generate the C declaration for an enum"""
|
||||
groupElem = groupinfo.elem
|
||||
@@ -432,11 +453,11 @@ class OutputGenerator:
|
||||
# Determine the required bit width for the enum group.
|
||||
# 32 is the default, which generates C enum types for the values.
|
||||
bitwidth = 32
|
||||
|
||||
|
||||
# If the constFlagBits preference is set, 64 is the default for bitmasks
|
||||
if self.genOpts.conventions.constFlagBits and groupElem.get('type') == 'bitmask':
|
||||
bitwidth = 64
|
||||
|
||||
|
||||
# Check for an explicitly defined bitwidth, which will override any defaults.
|
||||
if groupElem.get('bitwidth'):
|
||||
try:
|
||||
@@ -444,56 +465,107 @@ class OutputGenerator:
|
||||
except ValueError as ve:
|
||||
self.logMsg('error', 'Invalid value for bitwidth attribute (', groupElem.get('bitwidth'), ') for ', groupName, ' - must be an integer value\n')
|
||||
exit(1)
|
||||
|
||||
# Bitmask types support 64-bit flags, so have different handling
|
||||
|
||||
usebitmask = False
|
||||
usedefine = False
|
||||
|
||||
# Bitmask flags can be generated as either "static const uint{32,64}_t" values,
|
||||
# or as 32-bit C enums. 64-bit types must use uint64_t values.
|
||||
if groupElem.get('type') == 'bitmask':
|
||||
|
||||
if bitwidth > 32 or self.misracppstyle():
|
||||
usebitmask = True
|
||||
if self.misracstyle():
|
||||
usedefine = True
|
||||
|
||||
if usedefine or usebitmask:
|
||||
# Validate the bitwidth and generate values appropriately
|
||||
# Bitmask flags up to 64-bit are generated as static const uint64_t values
|
||||
# Bitmask flags up to 32-bit are generated as C enum values
|
||||
if bitwidth > 64:
|
||||
self.logMsg('error', 'Invalid value for bitwidth attribute (', groupElem.get('bitwidth'), ') for bitmask type ', groupName, ' - must be less than or equal to 64\n')
|
||||
exit(1)
|
||||
elif bitwidth > 32:
|
||||
return self.buildEnumCDecl_Bitmask(groupinfo, groupName)
|
||||
else:
|
||||
return self.buildEnumCDecl_Enum(expand, groupinfo, groupName)
|
||||
return self.buildEnumCDecl_BitmaskOrDefine(groupinfo, groupName, bitwidth, usedefine)
|
||||
else:
|
||||
# Validate the bitwidth and generate values appropriately
|
||||
# Enum group types up to 32-bit are generated as C enum values
|
||||
if bitwidth > 32:
|
||||
self.logMsg('error', 'Invalid value for bitwidth attribute (', groupElem.get('bitwidth'), ') for enum type ', groupName, ' - must be less than or equal to 32\n')
|
||||
exit(1)
|
||||
else:
|
||||
return self.buildEnumCDecl_Enum(expand, groupinfo, groupName)
|
||||
|
||||
def buildEnumCDecl_Bitmask(self, groupinfo, groupName):
|
||||
def buildEnumCDecl_BitmaskOrDefine(self, groupinfo, groupName, bitwidth, usedefine):
|
||||
"""Generate the C declaration for an "enum" that is actually a
|
||||
set of flag bits"""
|
||||
groupElem = groupinfo.elem
|
||||
flagTypeName = groupinfo.flagType.elem.get('name')
|
||||
flagTypeName = groupElem.get('name')
|
||||
|
||||
# Prefix
|
||||
body = "// Flag bits for " + flagTypeName + "\n"
|
||||
|
||||
|
||||
if bitwidth == 64:
|
||||
body += "typedef VkFlags64 %s;\n" % flagTypeName;
|
||||
else:
|
||||
body += "typedef VkFlags %s;\n" % flagTypeName;
|
||||
|
||||
# Maximum allowable value for a flag (unsigned 64-bit integer)
|
||||
maxValidValue = 2**(64) - 1
|
||||
minValidValue = 0
|
||||
|
||||
# Get a list of nested 'enum' tags.
|
||||
enums = groupElem.findall('enum')
|
||||
|
||||
# Check for and report duplicates, and return a list with them
|
||||
# removed.
|
||||
enums = self.checkDuplicateEnums(enums)
|
||||
|
||||
# Accumulate non-numeric enumerant values separately and append
|
||||
# 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 = ''
|
||||
|
||||
# Loop over the nested 'enum' tags.
|
||||
for elem in groupElem.findall('enum'):
|
||||
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, bitwidth, True)
|
||||
name = elem.get('name')
|
||||
|
||||
|
||||
# Range check for the enum value
|
||||
if numVal is not None and (numVal > maxValidValue or numVal < minValidValue):
|
||||
self.logMsg('error', 'Allowable range for flag types in C is [', minValidValue, ',', maxValidValue, '], but', name, 'flag has a value outside of this (', strVal, ')\n')
|
||||
exit(1)
|
||||
|
||||
body += "static const {} {} = {};\n".format(flagTypeName, name, strVal)
|
||||
|
||||
decl = self.genRequirements(name, mustBeFound = False)
|
||||
|
||||
if self.isEnumRequired(elem):
|
||||
protect = elem.get('protect')
|
||||
if protect is not None:
|
||||
body += '#ifdef {}\n'.format(protect)
|
||||
|
||||
if usedefine:
|
||||
decl += "#define {} {}\n".format(name, strVal)
|
||||
elif self.misracppstyle():
|
||||
decl += "static constexpr {} {} {{{}}};\n".format(flagTypeName, name, strVal)
|
||||
else:
|
||||
# Some C compilers only allow initializing a 'static const' variable with a literal value.
|
||||
# So initializing an alias from another 'static const' value would fail to compile.
|
||||
# Work around this by chasing the aliases to get the actual value.
|
||||
while numVal is None:
|
||||
alias = self.registry.tree.find("enums/enum[@name='" + strVal + "']")
|
||||
(numVal, strVal) = self.enumToValue(alias, True)
|
||||
decl += "static const {} {} = {};\n".format(flagTypeName, name, strVal)
|
||||
|
||||
if numVal is not None:
|
||||
body += decl
|
||||
else:
|
||||
aliasText += decl
|
||||
|
||||
if protect is not None:
|
||||
body += '#endif\n'
|
||||
|
||||
# Now append the non-numeric enumerant values
|
||||
body += aliasText
|
||||
|
||||
# Postfix
|
||||
|
||||
@@ -505,7 +577,7 @@ 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-9]+|[a-z_])([A-Z0-9])', r'\1_\2', groupName).upper()
|
||||
expandPrefix = expandName
|
||||
expandSuffix = ''
|
||||
expandSuffixMatch = re.search(r'[A-Z][A-Z]+$', groupName)
|
||||
@@ -519,11 +591,11 @@ class OutputGenerator:
|
||||
|
||||
# @@ Should use the type="bitmask" attribute instead
|
||||
isEnum = ('FLAG_BITS' not in expandPrefix)
|
||||
|
||||
|
||||
# Allowable range for a C enum - which is that of a signed 32-bit integer
|
||||
maxValidValue = 2**(32 - 1) - 1
|
||||
minValidValue = (maxValidValue * -1) - 1
|
||||
|
||||
|
||||
|
||||
# Get a list of nested 'enum' tags.
|
||||
enums = groupElem.findall('enum')
|
||||
@@ -553,7 +625,22 @@ class OutputGenerator:
|
||||
|
||||
# Extension enumerants are only included if they are required
|
||||
if self.isEnumRequired(elem):
|
||||
decl = " {} = {},".format(name, strVal)
|
||||
decl = ''
|
||||
|
||||
protect = elem.get('protect')
|
||||
if protect is not None:
|
||||
decl += '#ifdef {}\n'.format(protect)
|
||||
|
||||
# Indent requirements comment, if there is one
|
||||
requirements = self.genRequirements(name, mustBeFound = False)
|
||||
if requirements != '':
|
||||
requirements = ' ' + requirements
|
||||
decl += requirements
|
||||
decl += ' {} = {},'.format(name, strVal)
|
||||
|
||||
if protect is not None:
|
||||
decl += '\n#endif'
|
||||
|
||||
if numVal is not None:
|
||||
body.append(decl)
|
||||
else:
|
||||
@@ -563,7 +650,6 @@ class OutputGenerator:
|
||||
if numVal is not None and (numVal > maxValidValue or numVal < minValidValue):
|
||||
self.logMsg('error', 'Allowable range for C enum types is [', minValidValue, ',', maxValidValue, '], but', name, 'has a value outside of this (', strVal, ')\n')
|
||||
exit(1)
|
||||
|
||||
|
||||
# Don't track min/max for non-numbers (numVal is None)
|
||||
if isEnum and numVal is not None and elem.get('extends') is None:
|
||||
@@ -605,6 +691,47 @@ class OutputGenerator:
|
||||
|
||||
return (section, '\n'.join(body))
|
||||
|
||||
def buildConstantCDecl(self, enuminfo, name, alias):
|
||||
"""Generate the C declaration for a constant (a single <enum>
|
||||
value).
|
||||
|
||||
<enum> tags may specify their values in several ways, but are
|
||||
usually just integers or floating-point numbers."""
|
||||
|
||||
(_, strVal) = self.enumToValue(enuminfo.elem, False)
|
||||
|
||||
if self.misracppstyle() and enuminfo.elem.get('type') and not alias:
|
||||
# Generate e.g.: static constexpr uint32_t x = ~static_cast<uint32_t>(1U);
|
||||
# This appeases MISRA "underlying type" rules.
|
||||
typeStr = enuminfo.elem.get('type');
|
||||
invert = '~' in strVal
|
||||
number = strVal.strip("()~UL")
|
||||
if typeStr != "float":
|
||||
number += 'U'
|
||||
strVal = "~" if invert else ""
|
||||
strVal += "static_cast<" + typeStr + ">(" + number + ")"
|
||||
body = 'static constexpr ' + typeStr.ljust(9) + name.ljust(33) + ' {' + strVal + '};'
|
||||
elif enuminfo.elem.get('type') and not alias:
|
||||
# Generate e.g.: #define x (~0ULL)
|
||||
typeStr = enuminfo.elem.get('type');
|
||||
invert = '~' in strVal
|
||||
paren = '(' in strVal
|
||||
number = strVal.strip("()~UL")
|
||||
if typeStr != "float":
|
||||
if typeStr == "uint64_t":
|
||||
number += 'ULL'
|
||||
else:
|
||||
number += 'U'
|
||||
strVal = "~" if invert else ""
|
||||
strVal += number
|
||||
if paren:
|
||||
strVal = "(" + strVal + ")";
|
||||
body = '#define ' + name.ljust(33) + ' ' + strVal;
|
||||
else:
|
||||
body = '#define ' + name.ljust(33) + ' ' + strVal
|
||||
|
||||
return body
|
||||
|
||||
def makeDir(self, path):
|
||||
"""Create a directory, if not already done.
|
||||
|
||||
@@ -683,6 +810,20 @@ class OutputGenerator:
|
||||
self.featureName = None
|
||||
self.featureExtraProtect = None
|
||||
|
||||
def genRequirements(self, name, mustBeFound = True):
|
||||
"""Generate text showing what core versions and extensions introduce
|
||||
an API. This exists in the base Generator class because it's used by
|
||||
the shared enumerant-generating interfaces (buildEnumCDecl, etc.).
|
||||
Here it returns an empty string for most generators, but can be
|
||||
overridden by e.g. DocGenerator.
|
||||
|
||||
- name - name of the API
|
||||
- mustBeFound - If True, when requirements for 'name' cannot be
|
||||
determined, a warning comment is generated.
|
||||
"""
|
||||
|
||||
return ''
|
||||
|
||||
def validateFeature(self, featureType, featureName):
|
||||
"""Validate we're generating something only inside a `<feature>` tag"""
|
||||
if self.featureName is None:
|
||||
@@ -738,6 +879,14 @@ class OutputGenerator:
|
||||
Extend to generate as desired in your derived class."""
|
||||
self.validateFeature('command', cmdinfo)
|
||||
|
||||
def genSpirv(self, spirv, spirvinfo, alias):
|
||||
"""Generate interface for a spirv element.
|
||||
|
||||
- spirvinfo - SpirvInfo for a command
|
||||
|
||||
Extend to generate as desired in your derived class."""
|
||||
return
|
||||
|
||||
def makeProtoName(self, name, tail):
|
||||
"""Turn a `<proto>` `<name>` into C-language prototype
|
||||
and typedef declarations for that name.
|
||||
@@ -759,7 +908,9 @@ class OutputGenerator:
|
||||
- aligncol - if non-zero, attempt to align the nested `<name>` element
|
||||
at this column"""
|
||||
indent = ' '
|
||||
paramdecl = indent + noneStr(param.text)
|
||||
paramdecl = indent
|
||||
prefix = noneStr(param.text)
|
||||
|
||||
for elem in param:
|
||||
text = noneStr(elem.text)
|
||||
tail = noneStr(elem.tail)
|
||||
@@ -778,7 +929,16 @@ class OutputGenerator:
|
||||
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 (self.misracppstyle() and prefix.find('const ') != -1):
|
||||
# Change pointer type order from e.g. "const void *" to "void const *".
|
||||
# If the string starts with 'const', reorder it to be after the first type.
|
||||
paramdecl += prefix.replace('const ', '') + text + ' const' + tail
|
||||
else:
|
||||
paramdecl += prefix + text + tail
|
||||
|
||||
# Clear prefix for subsequent iterations
|
||||
prefix = ''
|
||||
if aligncol == 0:
|
||||
# Squeeze out multiple spaces other than the indentation
|
||||
paramdecl = indent + ' '.join(paramdecl.split())
|
||||
@@ -985,8 +1145,28 @@ class OutputGenerator:
|
||||
# Non-indented parameters
|
||||
paramdecl = '('
|
||||
if n > 0:
|
||||
paramnames = (''.join(t for t in p.itertext())
|
||||
for p in params)
|
||||
paramnames = []
|
||||
if self.misracppstyle():
|
||||
for p in params:
|
||||
param = ''
|
||||
firstIter = True;
|
||||
for t in p.itertext():
|
||||
if (firstIter):
|
||||
prefix = t
|
||||
firstIter = False
|
||||
else:
|
||||
# Change pointer type order from e.g. "const void *" to "void const *".
|
||||
# If the string starts with 'const', reorder it to be after the first type.
|
||||
if (prefix.find('const ') != -1):
|
||||
param += prefix.replace('const ', '') + t + ' const '
|
||||
else:
|
||||
param += prefix + t
|
||||
# Clear prefix for subsequent iterations
|
||||
prefix = ''
|
||||
paramnames.append(param);
|
||||
else:
|
||||
paramnames = (''.join(t for t in p.itertext())
|
||||
for p in params)
|
||||
paramdecl += ', '.join(paramnames)
|
||||
else:
|
||||
paramdecl += 'void'
|
||||
|
Reference in New Issue
Block a user