504 lines
24 KiB
Python
504 lines
24 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,functools
|
|
from generator import *
|
|
from functools import total_ordering
|
|
|
|
# ExtensionMetaDocGeneratorOptions - subclass of GeneratorOptions.
|
|
class ExtensionMetaDocGeneratorOptions(GeneratorOptions):
|
|
"""Represents options during extension metainformation generation for Asciidoc"""
|
|
def __init__(self,
|
|
filename = None,
|
|
directory = '.',
|
|
apiname = None,
|
|
profile = None,
|
|
versions = '.*',
|
|
emitversions = '.*',
|
|
defaultExtensions = None,
|
|
addExtensions = None,
|
|
removeExtensions = None,
|
|
emitExtensions = None,
|
|
sortProcedure = regSortFeatures):
|
|
GeneratorOptions.__init__(self, filename, directory, apiname, profile,
|
|
versions, emitversions, defaultExtensions,
|
|
addExtensions, removeExtensions,
|
|
emitExtensions, sortProcedure)
|
|
|
|
@total_ordering
|
|
class Extension:
|
|
def __init__(self,
|
|
generator, # needed for logging
|
|
filename,
|
|
name,
|
|
number,
|
|
type,
|
|
requires,
|
|
requiresCore,
|
|
contact,
|
|
promotedTo,
|
|
deprecatedBy,
|
|
obsoletedBy,
|
|
revision ):
|
|
self.generator = generator
|
|
self.filename = filename
|
|
self.name = name
|
|
self.number = number
|
|
self.type = type
|
|
self.requires = requires
|
|
self.requiresCore = requiresCore
|
|
self.contact = contact
|
|
self.promotedTo = promotedTo
|
|
self.deprecatedBy = deprecatedBy
|
|
self.obsoletedBy = obsoletedBy
|
|
self.revision = revision
|
|
|
|
self.deprecationType = None
|
|
self.supercedingVkVersion = None
|
|
self.supercedingExtension = None
|
|
|
|
if self.promotedTo is not None and self.deprecatedBy is not None and self.obsoletedBy is not None:
|
|
self.generator.logMsg('warn', 'All \'promotedto\', \'deprecatedby\' and \'obsoletedby\' attributes used on extension ' + self.name + '! Ignoring \'promotedto\' and \'deprecatedby\'.')
|
|
elif self.promotedTo is not None and self.deprecatedBy is not None:
|
|
self.generator.logMsg('warn', 'Both \'promotedto\' and \'deprecatedby\' attributes used on extension ' + self.name + '! Ignoring \'deprecatedby\'.')
|
|
elif self.promotedTo is not None and self.obsoletedBy is not None:
|
|
self.generator.logMsg('warn', 'Both \'promotedto\' and \'obsoletedby\' attributes used on extension ' + self.name + '! Ignoring \'promotedto\'.')
|
|
elif self.deprecatedBy is not None and self.obsoletedBy is not None:
|
|
self.generator.logMsg('warn', 'Both \'deprecatedby\' and \'obsoletedby\' attributes used on extension ' + self.name + '! Ignoring \'deprecatedby\'.')
|
|
|
|
superceededBy = None
|
|
if self.promotedTo is not None:
|
|
self.deprecationType = 'promotion'
|
|
superceededBy = promotedTo
|
|
elif self.deprecatedBy is not None:
|
|
self.deprecationType = 'deprecation'
|
|
superceededBy = deprecatedBy
|
|
elif self.obsoletedBy is not None:
|
|
self.deprecationType = 'obsoletion'
|
|
superceededBy = obsoletedBy
|
|
|
|
if superceededBy is not None:
|
|
if superceededBy == '' and not self.deprecationType == 'promotion':
|
|
pass # supercedingVkVersion, supercedingExtension == None
|
|
elif superceededBy.startswith('VK_VERSION_'):
|
|
self.supercedingVkVersion = superceededBy
|
|
elif superceededBy.startswith('VK_'):
|
|
self.supercedingExtension = superceededBy
|
|
else:
|
|
self.generator.logMsg('error', 'Unrecognized ' + self.deprecationType + ' attribute value \'' + superceededBy + '\'!')
|
|
|
|
def __str__(self): return self.name
|
|
|
|
def __eq__(self, other): return self.name == other.name
|
|
def __ne__(self, other): return self.name != other.name
|
|
def __lt__(self, other):
|
|
me_is_KHR = self.name.startswith( 'VK_KHR' )
|
|
me_is_EXT = self.name.startswith( 'VK_EXT' )
|
|
he_is_KHR = other.name.startswith( 'VK_KHR' )
|
|
he_is_EXT = other.name.startswith( 'VK_EXT' )
|
|
|
|
swap = False
|
|
if me_is_KHR and not he_is_KHR:
|
|
return not swap
|
|
elif he_is_KHR and not me_is_KHR:
|
|
return swap
|
|
elif me_is_EXT and not he_is_EXT:
|
|
return not swap
|
|
elif he_is_EXT and not me_is_EXT:
|
|
return swap
|
|
else:
|
|
return self.name < other.name
|
|
|
|
def typeToStr(self):
|
|
if self.type == 'instance':
|
|
return 'Instance extension'
|
|
elif self.type == 'device':
|
|
return 'Device extension'
|
|
elif self.type != None:
|
|
self.generator.logMsg('warn', 'The type attribute of ' + self.name + ' extension is neither \'instance\' nor \'device\'. That is invalid (at the time this script was written).')
|
|
write(' ' + type.capitalize(), file=fp)
|
|
else: # should be unreachable
|
|
self.generator.logMsg('error', 'Logic error in typeToStr(): Missing type attribute!')
|
|
|
|
def conditionalLinkCoreVk(self, vulkanVersion, linkSuffix):
|
|
versionMatch = re.match(r'VK_VERSION_(\d+)_(\d+)', vulkanVersion)
|
|
major = versionMatch.group(1)
|
|
minor = versionMatch.group(2)
|
|
|
|
dottedVersion = major + '.' + minor
|
|
|
|
doc = 'ifdef::' + vulkanVersion + '[]\n'
|
|
doc += ' <<versions-' + dottedVersion + linkSuffix + ', Vulkan ' + dottedVersion + '>>\n'
|
|
doc += 'endif::' + vulkanVersion + '[]\n'
|
|
doc += 'ifndef::' + vulkanVersion + '[]\n'
|
|
doc += ' Vulkan ' + dottedVersion + '\n'
|
|
doc += 'endif::' + vulkanVersion + '[]\n'
|
|
|
|
return doc
|
|
|
|
def conditionalLinkExt(self, extName, indent = ' '):
|
|
doc = 'ifdef::' + extName + '[]\n'
|
|
doc += indent + '<<' + extName + '>>\n'
|
|
doc += 'endif::' + extName + '[]\n'
|
|
doc += 'ifndef::' + extName + '[]\n'
|
|
doc += indent + '`' + extName + '`\n'
|
|
doc += 'endif::' + extName + '[]\n'
|
|
|
|
return doc
|
|
|
|
def resolveDeprecationChain(self, extensionsList, succeededBy, file):
|
|
ext = next(x for x in extensionsList if x.name == succeededBy)
|
|
|
|
if ext.deprecationType:
|
|
if ext.deprecationType == 'promotion':
|
|
if ext.supercedingVkVersion:
|
|
write(' ** Which in turn was _promoted_ to\n' + ext.conditionalLinkCoreVk(ext.supercedingVkVersion, '-promotions'), file=file)
|
|
else: # ext.supercedingExtension
|
|
write(' ** Which in turn was _promoted_ to extension\n' + ext.conditionalLinkExt(ext.supercedingExtension), file=file)
|
|
ext.resolveDeprecationChain(extensionsList, ext.supercedingExtension, file)
|
|
elif ext.deprecationType == 'deprecation':
|
|
if ext.supercedingVkVersion:
|
|
write(' ** Which in turn was _deprecated_ by\n' + ext.conditionalLinkCoreVk(ext.supercedingVkVersion, '-new-feature'), file=file)
|
|
elif ext.supercedingExtension:
|
|
write(' ** Which in turn was _deprecated_ by\n' + ext.conditionalLinkExt(ext.supercedingExtension) + ' extension', file=file)
|
|
ext.resolveDeprecationChain(extensionsList, ext.supercedingExtension, file)
|
|
else:
|
|
write(' ** Which in turn was _deprecated_ without replacement', file=file)
|
|
elif ext.deprecationType == 'obsoletion':
|
|
if ext.supercedingVkVersion:
|
|
write(' ** Which in turn was _obsoleted_ by\n' + ext.conditionalLinkCoreVk(ext.supercedingVkVersion, '-new-feature'), file=file)
|
|
elif ext.supercedingExtension:
|
|
write(' ** Which in turn was _obsoleted_ by\n' + ext.conditionalLinkExt(ext.supercedingExtension) + ' extension', file=file)
|
|
ext.resolveDeprecationChain(extensionsList, ext.supercedingExtension, file)
|
|
else:
|
|
write(' ** Which in turn was _obsoleted_ without replacement', file=file)
|
|
else: # should be unreachable
|
|
self.generator.logMsg('error', 'Logic error in resolveDeprecationChain(): deprecationType is neither \'promotion\', \'deprecation\' nor \'obsoletion\'!')
|
|
|
|
|
|
def makeMetafile(self, extensionsList):
|
|
fp = self.generator.newFile(self.filename)
|
|
|
|
write('[[' + self.name + ']]', file=fp)
|
|
write('=== ' + self.name, file=fp)
|
|
write('', file=fp)
|
|
|
|
write('*Name String*::', file=fp)
|
|
write(' `' + self.name + '`', file=fp)
|
|
|
|
write('*Extension Type*::', file=fp)
|
|
write(' ' + self.typeToStr(), file=fp)
|
|
|
|
write('*Registered Extension Number*::', file=fp)
|
|
write(' ' + self.number, file=fp)
|
|
|
|
write('*Revision*::', file=fp)
|
|
write(' ' + self.revision, file=fp)
|
|
|
|
# Only Vulkan extension dependencies are coded in XML, others are explicit
|
|
write('*Extension and Version Dependencies*::', file=fp)
|
|
write(' * Requires Vulkan ' + self.requiresCore, file=fp)
|
|
if self.requires:
|
|
for dep in self.requires.split(','):
|
|
write(' * Requires `<<' + dep + '>>`', file=fp)
|
|
|
|
if self.deprecationType:
|
|
write('*Deprecation state*::', file=fp)
|
|
|
|
if self.deprecationType == 'promotion':
|
|
if self.supercedingVkVersion:
|
|
write(' * _Promoted_ to\n' + self.conditionalLinkCoreVk(self.supercedingVkVersion, '-promotions'), file=fp)
|
|
else: # ext.supercedingExtension
|
|
write(' * _Promoted_ to\n' + self.conditionalLinkExt(self.supercedingExtension) + ' extension', file=fp)
|
|
self.resolveDeprecationChain(extensionsList, self.supercedingExtension, fp)
|
|
elif self.deprecationType == 'deprecation':
|
|
if self.supercedingVkVersion:
|
|
write(' * _Deprecated_ by\n' + self.conditionalLinkCoreVk(self.supercedingVkVersion, '-new-features'), file=fp)
|
|
elif self.supercedingExtension:
|
|
write(' * _Deprecated_ by\n' + self.conditionalLinkExt(self.supercedingExtension) + ' extension' , file=fp)
|
|
self.resolveDeprecationChain(extensionsList, self.supercedingExtension, fp)
|
|
else:
|
|
write(' * _Deprecated_ without replacement' , file=fp)
|
|
elif self.deprecationType == 'obsoletion':
|
|
if self.supercedingVkVersion:
|
|
write(' * _Obsoleted_ by\n' + self.conditionalLinkCoreVk(self.supercedingVkVersion, '-new-features'), file=fp)
|
|
elif self.supercedingExtension:
|
|
write(' * _Obsoleted_ by\n' + self.conditionalLinkExt(self.supercedingExtension) + ' extension' , file=fp)
|
|
self.resolveDeprecationChain(extensionsList, self.supercedingExtension, fp)
|
|
else:
|
|
# TODO: Does not make sense to retroactively ban use of extensions from 1.0.
|
|
# Needs some tweaks to the semantics and this message, when such extension(s) occur.
|
|
write(' * _Obsoleted_ without replacement' , file=fp)
|
|
else: # should be unreachable
|
|
self.generator.logMsg('error', 'Logic error in makeMetafile(): deprecationType is neither \'promotion\', \'deprecation\' nor \'obsoletion\'!')
|
|
|
|
write('*Contact*::', file=fp)
|
|
contacts = self.contact.split(',')
|
|
for c in contacts:
|
|
write(' * ' + c, file=fp)
|
|
|
|
fp.close()
|
|
|
|
|
|
# ExtensionMetaDocOutputGenerator - subclass of OutputGenerator.
|
|
# Generates AsciiDoc includes with metainformation for the Vulkan extension
|
|
# appendices. The fields used from <extension> tags in vk.xml are:
|
|
#
|
|
# name extension name string
|
|
# number extension number (optional)
|
|
# contact name and github login or email address (optional)
|
|
# type 'instance' | 'device' (optional)
|
|
# requires list of comma-separate requires Vulkan extensions (optional)
|
|
# requiresCore required core version of Vulkan (optional)
|
|
# promotedTo extension or Vulkan version it was promoted to
|
|
# deprecatedBy extension or Vulkan version which deprecated this extension,
|
|
# or empty string if deprecated without replacement
|
|
# obsoletedBy extension or Vulkan version which obsoleted this extension,
|
|
# or empty string if obsoleted without replacement
|
|
#
|
|
# ---- methods ----
|
|
# ExtensionMetaDocOutputGenerator(errFile, warnFile, diagFile) - args as for
|
|
# OutputGenerator. Defines additional internal state.
|
|
# ---- methods overriding base class ----
|
|
# beginFile(genOpts)
|
|
# endFile()
|
|
# beginFeature(interface, emit)
|
|
# endFeature()
|
|
class ExtensionMetaDocOutputGenerator(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)
|
|
self.extensions = []
|
|
|
|
def newFile(self, filename):
|
|
self.logMsg('diag', '# Generating include file:', filename)
|
|
fp = open(filename, 'w', encoding='utf-8')
|
|
write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
|
|
return fp
|
|
|
|
def beginFile(self, genOpts):
|
|
OutputGenerator.beginFile(self, genOpts)
|
|
|
|
self.directory = self.genOpts.directory
|
|
|
|
# Create subdirectory, if needed
|
|
self.makeDir(self.directory)
|
|
|
|
def conditionalExt(self, extName, content, ifdef = None, condition = None):
|
|
doc = ''
|
|
|
|
innerdoc = 'ifdef::' + extName + '[]\n'
|
|
innerdoc += content + '\n'
|
|
innerdoc += 'endif::' + extName + '[]\n'
|
|
|
|
if ifdef:
|
|
if ifdef == 'ifndef':
|
|
if condition:
|
|
doc += 'ifndef::' + condition + '[]\n'
|
|
doc += innerdoc
|
|
doc += 'endif::' + condition + '[]\n'
|
|
else: # no condition is as if condition is defined; "nothing" is always defined :p
|
|
pass # so no output
|
|
elif ifdef == 'ifdef':
|
|
if condition:
|
|
doc += 'ifdef::' + condition + '+' + extName + '[]\n'
|
|
doc += content + '\n' # does not include innerdoc; the ifdef was merged with the one above
|
|
doc += 'endif::' + condition + '+' + extName + '[]\n'
|
|
else: # no condition is as if condition is defined; "nothing" is always defined :p
|
|
doc += innerdoc
|
|
else: # should be unreachable
|
|
self.generator.logMsg('error', 'Logic error in conditionalExt(): ifdef is neither \'ifdef \' nor \'ifndef\'!')
|
|
else:
|
|
doc += innerdoc
|
|
|
|
return doc
|
|
|
|
def endFile(self):
|
|
self.extensions.sort()
|
|
|
|
for ext in self.extensions:
|
|
ext.makeMetafile(self.extensions)
|
|
|
|
promotedExtensions = {}
|
|
for ext in self.extensions:
|
|
if ext.deprecationType == 'promotion' and ext.supercedingVkVersion:
|
|
promotedExtensions.setdefault(ext.supercedingVkVersion, []).append(ext)
|
|
|
|
for coreVersion, extensions in promotedExtensions.items():
|
|
promoted_extensions_fp = self.newFile(self.directory + '/promoted_extensions_' + coreVersion + '.txt')
|
|
|
|
for ext in extensions:
|
|
indent = ''
|
|
write(' * {blank}\n+\n' + ext.conditionalLinkExt(ext.name, indent), file=promoted_extensions_fp)
|
|
|
|
promoted_extensions_fp.close()
|
|
|
|
current_extensions_appendix_fp = self.newFile(self.directory + '/current_extensions_appendix.txt')
|
|
deprecated_extensions_appendix_fp = self.newFile(self.directory + '/deprecated_extensions_appendix.txt')
|
|
current_extension_appendices_fp = self.newFile(self.directory + '/current_extension_appendices.txt')
|
|
current_extension_appendices_toc_fp = self.newFile(self.directory + '/current_extension_appendices_toc.txt')
|
|
deprecated_extension_appendices_fp = self.newFile(self.directory + '/deprecated_extension_appendices.txt')
|
|
deprecated_extension_appendices_toc_fp = self.newFile(self.directory + '/deprecated_extension_appendices_toc.txt')
|
|
deprecated_extensions_guard_macro_fp = self.newFile(self.directory + '/deprecated_extensions_guard_macro.txt')
|
|
|
|
write('include::deprecated_extensions_guard_macro.txt[]', file=current_extensions_appendix_fp)
|
|
write('', file=current_extensions_appendix_fp)
|
|
write('ifndef::HAS_DEPRECATED_EXTENSIONS[]', file=current_extensions_appendix_fp)
|
|
write('[[extension-appendices-list]]', file=current_extensions_appendix_fp)
|
|
write('== List of Extensions', file=current_extensions_appendix_fp)
|
|
write('endif::HAS_DEPRECATED_EXTENSIONS[]', file=current_extensions_appendix_fp)
|
|
write('ifdef::HAS_DEPRECATED_EXTENSIONS[]', file=current_extensions_appendix_fp)
|
|
write('[[extension-appendices-list]]', file=current_extensions_appendix_fp)
|
|
write('== List of Current Extensions', file=current_extensions_appendix_fp)
|
|
write('endif::HAS_DEPRECATED_EXTENSIONS[]', file=current_extensions_appendix_fp)
|
|
write('', file=current_extensions_appendix_fp)
|
|
write('include::current_extension_appendices_toc.txt[]', file=current_extensions_appendix_fp)
|
|
write('<<<', file=current_extensions_appendix_fp)
|
|
write('include::current_extension_appendices.txt[]', file=current_extensions_appendix_fp)
|
|
|
|
write('include::deprecated_extensions_guard_macro.txt[]', file=deprecated_extensions_appendix_fp)
|
|
write('', file=deprecated_extensions_appendix_fp)
|
|
write('ifdef::HAS_DEPRECATED_EXTENSIONS[]', file=deprecated_extensions_appendix_fp)
|
|
write('[[deprecated-extension-appendices-list]]', file=deprecated_extensions_appendix_fp)
|
|
write('== List of Deprecated Extensions', file=deprecated_extensions_appendix_fp)
|
|
write('include::deprecated_extension_appendices_toc.txt[]', file=deprecated_extensions_appendix_fp)
|
|
write('<<<', file=deprecated_extensions_appendix_fp)
|
|
write('include::deprecated_extension_appendices.txt[]', file=deprecated_extensions_appendix_fp)
|
|
write('endif::HAS_DEPRECATED_EXTENSIONS[]', file=deprecated_extensions_appendix_fp)
|
|
|
|
# add include guard to allow multiple includes
|
|
write('ifndef::DEPRECATED_EXTENSIONS_GUARD_MACRO_INCLUDE_GUARD[]', file=deprecated_extensions_guard_macro_fp)
|
|
write(':DEPRECATED_EXTENSIONS_GUARD_MACRO_INCLUDE_GUARD:\n', file=deprecated_extensions_guard_macro_fp)
|
|
|
|
for ext in self.extensions:
|
|
include = 'include::../' + ext.name + '.txt[]'
|
|
link = ' * <<' + ext.name + '>>'
|
|
|
|
if ext.deprecationType is None:
|
|
write(self.conditionalExt(ext.name, include), file=current_extension_appendices_fp)
|
|
write(self.conditionalExt(ext.name, link), file=current_extension_appendices_toc_fp)
|
|
else:
|
|
condition = ext.supercedingVkVersion if ext.supercedingVkVersion else ext.supercedingExtension # potentially None too
|
|
|
|
write(self.conditionalExt(ext.name, include, 'ifndef', condition), file=current_extension_appendices_fp)
|
|
write(self.conditionalExt(ext.name, link, 'ifndef', condition), file=current_extension_appendices_toc_fp)
|
|
|
|
write(self.conditionalExt(ext.name, include, 'ifdef', condition), file=deprecated_extension_appendices_fp)
|
|
write(self.conditionalExt(ext.name, link, 'ifdef', condition), file=deprecated_extension_appendices_toc_fp)
|
|
|
|
write(self.conditionalExt(ext.name, ':HAS_DEPRECATED_EXTENSIONS:', 'ifdef', condition), file=deprecated_extensions_guard_macro_fp)
|
|
|
|
current_extensions_appendix_fp.close()
|
|
deprecated_extensions_appendix_fp.close()
|
|
current_extension_appendices_fp.close()
|
|
current_extension_appendices_toc_fp.close()
|
|
deprecated_extension_appendices_fp.close()
|
|
deprecated_extension_appendices_toc_fp.close()
|
|
|
|
write('endif::DEPRECATED_EXTENSIONS_GUARD_MACRO_INCLUDE_GUARD[]', file=deprecated_extensions_guard_macro_fp)
|
|
deprecated_extensions_guard_macro_fp.close()
|
|
|
|
OutputGenerator.endFile(self)
|
|
|
|
def beginFeature(self, interface, emit):
|
|
# Start processing in superclass
|
|
OutputGenerator.beginFeature(self, interface, emit)
|
|
|
|
if interface.tag != 'extension':
|
|
self.logMsg('diag', 'beginFeature: ignoring non-extension feature', self.featureName)
|
|
return
|
|
|
|
# These attributes must exist
|
|
name = self.featureName
|
|
number = self.getAttrib(interface, 'number')
|
|
type = self.getAttrib(interface, 'type')
|
|
revision = self.getSpecVersion(interface, name)
|
|
|
|
# These attributes are optional
|
|
OPTIONAL = False
|
|
requires = self.getAttrib(interface, 'requires', OPTIONAL)
|
|
requiresCore = self.getAttrib(interface, 'requiresCore', OPTIONAL, '1.0')
|
|
contact = self.getAttrib(interface, 'contact', OPTIONAL)
|
|
promotedTo = self.getAttrib(interface, 'promotedto', OPTIONAL)
|
|
deprecatedBy = self.getAttrib(interface, 'deprecatedby', OPTIONAL)
|
|
obsoletedBy = self.getAttrib(interface, 'obsoletedby', OPTIONAL)
|
|
|
|
filename = self.directory + '/' + name + '.txt'
|
|
|
|
self.extensions.append( Extension(self, filename, name, number, type, requires, requiresCore, contact, promotedTo, deprecatedBy, obsoletedBy, revision) )
|
|
|
|
def endFeature(self):
|
|
# Finish processing in superclass
|
|
OutputGenerator.endFeature(self)
|
|
|
|
# Query an attribute from an element, or return a default value
|
|
# elem - element to query
|
|
# attribute - attribute name
|
|
# required - whether attribute must exist
|
|
# default - default value if attribute not present
|
|
def getAttrib(self, elem, attribute, required=True, default=None):
|
|
attrib = elem.get(attribute, default)
|
|
if required and (attrib is None):
|
|
name = elem.get('name', 'UNKNOWN')
|
|
self.logMsg('error', 'While processing \'' + self.featureName + ', <' + elem.tag + '> \'' + name + '\' does not contain required attribute \'' + attribute + '\'')
|
|
return attrib
|
|
|
|
def content(tag, ET):
|
|
return tag.text + ''.join(ET.tostring(e) for e in tag)
|
|
|
|
def numbersToWords(self, name):
|
|
whitelist = ['WIN32', 'INT16']
|
|
|
|
# temporarily replace whitelist items
|
|
for i, w in enumerate(whitelist):
|
|
name = re.sub(w, '{' + str(i) + '}', name)
|
|
|
|
name = re.sub(r'(?<=[A-Z])(\d+)(?![A-Z])', '_\g<1>', name)
|
|
|
|
# undo whitelist substitution
|
|
for i, w in enumerate(whitelist):
|
|
name = re.sub('\\{' + str(i) + '}', w, name)
|
|
|
|
return name
|
|
|
|
#
|
|
# Determine the extension revision from the EXTENSION_NAME_SPEC_VERSION
|
|
# enumerant.
|
|
#
|
|
# elem - <extension> element to query
|
|
# extname - extension name from the <extension> 'name' attribute
|
|
# default - default value if SPEC_VERSION token not present
|
|
def getSpecVersion(self, elem, extname, default=None):
|
|
# The literal enumerant name to match
|
|
versioningEnumName = self.numbersToWords(extname.upper()) + '_SPEC_VERSION'
|
|
|
|
for enum in elem.findall('./require/enum'):
|
|
enumName = self.getAttrib(enum, 'name')
|
|
if enumName == versioningEnumName:
|
|
return self.getAttrib(enum, 'value')
|
|
|
|
#if not found:
|
|
for enum in elem.findall('./require/enum'):
|
|
enumName = self.getAttrib(enum, 'name')
|
|
if enumName.find('SPEC_VERSION') != -1:
|
|
self.logMsg('warn', 'Missing ' + versioningEnumName + '! Potential misnamed candidate ' + enumName + '.')
|
|
return self.getAttrib(enum, 'value')
|
|
|
|
self.logMsg('error', 'Missing ' + versioningEnumName + '!')
|