Home | History | Annotate | Download | only in src
      1 #!/usr/bin/python3 -i
      2 #
      3 # Copyright (c) 2013-2016 The Khronos Group Inc.
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #     http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 import os,re,sys
     18 from collections import namedtuple
     19 import xml.etree.ElementTree as etree
     20 
     21 def write( *args, **kwargs ):
     22     file = kwargs.pop('file',sys.stdout)
     23     end = kwargs.pop( 'end','\n')
     24     file.write( ' '.join([str(arg) for arg in args]) )
     25     file.write( end )
     26 
     27 # noneStr - returns string argument, or "" if argument is None.
     28 # Used in converting etree Elements into text.
     29 #   str - string to convert
     30 def noneStr(str):
     31     if (str):
     32         return str
     33     else:
     34         return ""
     35 
     36 # enquote - returns string argument with surrounding quotes,
     37 #   for serialization into Python code.
     38 def enquote(str):
     39     if (str):
     40         return "'" + str + "'"
     41     else:
     42         return None
     43 
     44 # Primary sort key for regSortFeatures.
     45 # Sorts by category of the feature name string:
     46 #   Core API features (those defined with a <feature> tag)
     47 #   ARB/KHR/OES (Khronos extensions)
     48 #   other       (EXT/vendor extensions)
     49 # This will need changing for Vulkan!
     50 def regSortCategoryKey(feature):
     51     if (feature.elem.tag == 'feature'):
     52         return 0
     53     elif (feature.category == 'ARB' or
     54           feature.category == 'KHR' or
     55           feature.category == 'OES'):
     56         return 1
     57     else:
     58         return 2
     59 
     60 # Secondary sort key for regSortFeatures.
     61 # Sorts by extension name.
     62 def regSortNameKey(feature):
     63     return feature.name
     64 
     65 # Second sort key for regSortFeatures.
     66 # Sorts by feature version. <extension> elements all have version number "0"
     67 def regSortFeatureVersionKey(feature):
     68     return float(feature.version)
     69 
     70 # Tertiary sort key for regSortFeatures.
     71 # Sorts by extension number. <feature> elements all have extension number 0.
     72 def regSortExtensionNumberKey(feature):
     73     return int(feature.number)
     74 
     75 # regSortFeatures - default sort procedure for features.
     76 # Sorts by primary key of feature category ('feature' or 'extension')
     77 #   then by version number (for features)
     78 #   then by extension number (for extensions)
     79 def regSortFeatures(featureList):
     80     featureList.sort(key = regSortExtensionNumberKey)
     81     featureList.sort(key = regSortFeatureVersionKey)
     82     featureList.sort(key = regSortCategoryKey)
     83 
     84 # GeneratorOptions - base class for options used during header production
     85 # These options are target language independent, and used by
     86 # Registry.apiGen() and by base OutputGenerator objects.
     87 #
     88 # Members
     89 #   filename - name of file to generate, or None to write to stdout.
     90 #   apiname - string matching <api> 'apiname' attribute, e.g. 'gl'.
     91 #   profile - string specifying API profile , e.g. 'core', or None.
     92 #   versions - regex matching API versions to process interfaces for.
     93 #     Normally '.*' or '[0-9]\.[0-9]' to match all defined versions.
     94 #   emitversions - regex matching API versions to actually emit
     95 #    interfaces for (though all requested versions are considered
     96 #    when deciding which interfaces to generate). For GL 4.3 glext.h,
     97 #     this might be '1\.[2-5]|[2-4]\.[0-9]'.
     98 #   defaultExtensions - If not None, a string which must in its
     99 #     entirety match the pattern in the "supported" attribute of
    100 #     the <extension>. Defaults to None. Usually the same as apiname.
    101 #   addExtensions - regex matching names of additional extensions
    102 #     to include. Defaults to None.
    103 #   removeExtensions - regex matching names of extensions to
    104 #     remove (after defaultExtensions and addExtensions). Defaults
    105 #     to None.
    106 #   sortProcedure - takes a list of FeatureInfo objects and sorts
    107 #     them in place to a preferred order in the generated output.
    108 #     Default is core API versions, ARB/KHR/OES extensions, all
    109 #     other extensions, alphabetically within each group.
    110 # The regex patterns can be None or empty, in which case they match
    111 #   nothing.
    112 class GeneratorOptions:
    113     """Represents options during header production from an API registry"""
    114     def __init__(self,
    115                  filename = None,
    116                  apiname = None,
    117                  profile = None,
    118                  versions = '.*',
    119                  emitversions = '.*',
    120                  defaultExtensions = None,
    121                  addExtensions = None,
    122                  removeExtensions = None,
    123                  sortProcedure = regSortFeatures):
    124         self.filename          = filename
    125         self.apiname           = apiname
    126         self.profile           = profile
    127         self.versions          = self.emptyRegex(versions)
    128         self.emitversions      = self.emptyRegex(emitversions)
    129         self.defaultExtensions = defaultExtensions
    130         self.addExtensions     = self.emptyRegex(addExtensions)
    131         self.removeExtensions  = self.emptyRegex(removeExtensions)
    132         self.sortProcedure     = sortProcedure
    133     #
    134     # Substitute a regular expression which matches no version
    135     # or extension names for None or the empty string.
    136     def emptyRegex(self,pat):
    137         if (pat == None or pat == ''):
    138             return '_nomatch_^'
    139         else:
    140             return pat
    141 
    142 # CGeneratorOptions - subclass of GeneratorOptions.
    143 #
    144 # Adds options used by COutputGenerator objects during C language header
    145 # generation.
    146 #
    147 # Additional members
    148 #   prefixText - list of strings to prefix generated header with
    149 #     (usually a copyright statement + calling convention macros).
    150 #   protectFile - True if multiple inclusion protection should be
    151 #     generated (based on the filename) around the entire header.
    152 #   protectFeature - True if #ifndef..#endif protection should be
    153 #     generated around a feature interface in the header file.
    154 #   genFuncPointers - True if function pointer typedefs should be
    155 #     generated
    156 #   protectProto - If conditional protection should be generated
    157 #     around prototype declarations, set to either '#ifdef'
    158 #     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
    159 #     to require opt-out (#ifndef protectProtoStr). Otherwise
    160 #     set to None.
    161 #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
    162 #     declarations, if protectProto is set
    163 #   apicall - string to use for the function declaration prefix,
    164 #     such as APICALL on Windows.
    165 #   apientry - string to use for the calling convention macro,
    166 #     in typedefs, such as APIENTRY.
    167 #   apientryp - string to use for the calling convention macro
    168 #     in function pointer typedefs, such as APIENTRYP.
    169 #   indentFuncProto - True if prototype declarations should put each
    170 #     parameter on a separate line
    171 #   indentFuncPointer - True if typedefed function pointers should put each
    172 #     parameter on a separate line
    173 #   alignFuncParam - if nonzero and parameters are being put on a
    174 #     separate line, align parameter names at the specified column
    175 class CGeneratorOptions(GeneratorOptions):
    176     """Represents options during C interface generation for headers"""
    177     def __init__(self,
    178                  filename = None,
    179                  apiname = None,
    180                  profile = None,
    181                  versions = '.*',
    182                  emitversions = '.*',
    183                  defaultExtensions = None,
    184                  addExtensions = None,
    185                  removeExtensions = None,
    186                  sortProcedure = regSortFeatures,
    187                  prefixText = "",
    188                  genFuncPointers = True,
    189                  protectFile = True,
    190                  protectFeature = True,
    191                  protectProto = None,
    192                  protectProtoStr = None,
    193                  apicall = '',
    194                  apientry = '',
    195                  apientryp = '',
    196                  indentFuncProto = True,
    197                  indentFuncPointer = False,
    198                  alignFuncParam = 0):
    199         GeneratorOptions.__init__(self, filename, apiname, profile,
    200                                   versions, emitversions, defaultExtensions,
    201                                   addExtensions, removeExtensions, sortProcedure)
    202         self.prefixText      = prefixText
    203         self.genFuncPointers = genFuncPointers
    204         self.protectFile     = protectFile
    205         self.protectFeature  = protectFeature
    206         self.protectProto    = protectProto
    207         self.protectProtoStr = protectProtoStr
    208         self.apicall         = apicall
    209         self.apientry        = apientry
    210         self.apientryp       = apientryp
    211         self.indentFuncProto = indentFuncProto
    212         self.indentFuncPointer = indentFuncPointer
    213         self.alignFuncParam  = alignFuncParam
    214 
    215 # DocGeneratorOptions - subclass of GeneratorOptions.
    216 #
    217 # Shares many members with CGeneratorOptions, since
    218 # both are writing C-style declarations:
    219 #
    220 #   prefixText - list of strings to prefix generated header with
    221 #     (usually a copyright statement + calling convention macros).
    222 #   apicall - string to use for the function declaration prefix,
    223 #     such as APICALL on Windows.
    224 #   apientry - string to use for the calling convention macro,
    225 #     in typedefs, such as APIENTRY.
    226 #   apientryp - string to use for the calling convention macro
    227 #     in function pointer typedefs, such as APIENTRYP.
    228 #   genDirectory - directory into which to generate include files
    229 #   indentFuncProto - True if prototype declarations should put each
    230 #     parameter on a separate line
    231 #   indentFuncPointer - True if typedefed function pointers should put each
    232 #     parameter on a separate line
    233 #   alignFuncParam - if nonzero and parameters are being put on a
    234 #     separate line, align parameter names at the specified column
    235 #
    236 # Additional members:
    237 #
    238 class DocGeneratorOptions(GeneratorOptions):
    239     """Represents options during C interface generation for Asciidoc"""
    240     def __init__(self,
    241                  filename = None,
    242                  apiname = None,
    243                  profile = None,
    244                  versions = '.*',
    245                  emitversions = '.*',
    246                  defaultExtensions = None,
    247                  addExtensions = None,
    248                  removeExtensions = None,
    249                  sortProcedure = regSortFeatures,
    250                  prefixText = "",
    251                  apicall = '',
    252                  apientry = '',
    253                  apientryp = '',
    254                  genDirectory = 'gen',
    255                  indentFuncProto = True,
    256                  indentFuncPointer = False,
    257                  alignFuncParam = 0,
    258                  expandEnumerants = True):
    259         GeneratorOptions.__init__(self, filename, apiname, profile,
    260                                   versions, emitversions, defaultExtensions,
    261                                   addExtensions, removeExtensions, sortProcedure)
    262         self.prefixText      = prefixText
    263         self.apicall         = apicall
    264         self.apientry        = apientry
    265         self.apientryp       = apientryp
    266         self.genDirectory    = genDirectory
    267         self.indentFuncProto = indentFuncProto
    268         self.indentFuncPointer = indentFuncPointer
    269         self.alignFuncParam  = alignFuncParam
    270         self.expandEnumerants = expandEnumerants
    271 
    272 # ThreadGeneratorOptions - subclass of GeneratorOptions.
    273 #
    274 # Adds options used by COutputGenerator objects during C language header
    275 # generation.
    276 #
    277 # Additional members
    278 #   prefixText - list of strings to prefix generated header with
    279 #     (usually a copyright statement + calling convention macros).
    280 #   protectFile - True if multiple inclusion protection should be
    281 #     generated (based on the filename) around the entire header.
    282 #   protectFeature - True if #ifndef..#endif protection should be
    283 #     generated around a feature interface in the header file.
    284 #   genFuncPointers - True if function pointer typedefs should be
    285 #     generated
    286 #   protectProto - True if #ifdef..#endif protection should be
    287 #     generated around prototype declarations
    288 #   protectProtoStr - #ifdef symbol to use around prototype
    289 #     declarations, if protected
    290 #   apicall - string to use for the function declaration prefix,
    291 #     such as APICALL on Windows.
    292 #   apientry - string to use for the calling convention macro,
    293 #     in typedefs, such as APIENTRY.
    294 #   apientryp - string to use for the calling convention macro
    295 #     in function pointer typedefs, such as APIENTRYP.
    296 #   indentFuncProto - True if prototype declarations should put each
    297 #     parameter on a separate line
    298 #   indentFuncPointer - True if typedefed function pointers should put each
    299 #     parameter on a separate line
    300 #   alignFuncParam - if nonzero and parameters are being put on a
    301 #     separate line, align parameter names at the specified column
    302 class ThreadGeneratorOptions(GeneratorOptions):
    303     """Represents options during C interface generation for headers"""
    304     def __init__(self,
    305                  filename = None,
    306                  apiname = None,
    307                  profile = None,
    308                  versions = '.*',
    309                  emitversions = '.*',
    310                  defaultExtensions = None,
    311                  addExtensions = None,
    312                  removeExtensions = None,
    313                  sortProcedure = regSortFeatures,
    314                  prefixText = "",
    315                  genFuncPointers = True,
    316                  protectFile = True,
    317                  protectFeature = True,
    318                  protectProto = True,
    319                  protectProtoStr = True,
    320                  apicall = '',
    321                  apientry = '',
    322                  apientryp = '',
    323                  indentFuncProto = True,
    324                  indentFuncPointer = False,
    325                  alignFuncParam = 0,
    326                  genDirectory = None):
    327         GeneratorOptions.__init__(self, filename, apiname, profile,
    328                                   versions, emitversions, defaultExtensions,
    329                                   addExtensions, removeExtensions, sortProcedure)
    330         self.prefixText      = prefixText
    331         self.genFuncPointers = genFuncPointers
    332         self.protectFile     = protectFile
    333         self.protectFeature  = protectFeature
    334         self.protectProto    = protectProto
    335         self.protectProtoStr = protectProtoStr
    336         self.apicall         = apicall
    337         self.apientry        = apientry
    338         self.apientryp       = apientryp
    339         self.indentFuncProto = indentFuncProto
    340         self.indentFuncPointer = indentFuncPointer
    341         self.alignFuncParam  = alignFuncParam
    342         self.genDirectory    = genDirectory
    343 
    344 
    345 # ParamCheckerGeneratorOptions - subclass of GeneratorOptions.
    346 #
    347 # Adds options used by ParamCheckerOutputGenerator objects during parameter validation
    348 # generation.
    349 #
    350 # Additional members
    351 #   prefixText - list of strings to prefix generated header with
    352 #     (usually a copyright statement + calling convention macros).
    353 #   protectFile - True if multiple inclusion protection should be
    354 #     generated (based on the filename) around the entire header.
    355 #   protectFeature - True if #ifndef..#endif protection should be
    356 #     generated around a feature interface in the header file.
    357 #   genFuncPointers - True if function pointer typedefs should be
    358 #     generated
    359 #   protectProto - If conditional protection should be generated
    360 #     around prototype declarations, set to either '#ifdef'
    361 #     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
    362 #     to require opt-out (#ifndef protectProtoStr). Otherwise
    363 #     set to None.
    364 #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
    365 #     declarations, if protectProto is set
    366 #   apicall - string to use for the function declaration prefix,
    367 #     such as APICALL on Windows.
    368 #   apientry - string to use for the calling convention macro,
    369 #     in typedefs, such as APIENTRY.
    370 #   apientryp - string to use for the calling convention macro
    371 #     in function pointer typedefs, such as APIENTRYP.
    372 #   indentFuncProto - True if prototype declarations should put each
    373 #     parameter on a separate line
    374 #   indentFuncPointer - True if typedefed function pointers should put each
    375 #     parameter on a separate line
    376 #   alignFuncParam - if nonzero and parameters are being put on a
    377 #     separate line, align parameter names at the specified column
    378 class ParamCheckerGeneratorOptions(GeneratorOptions):
    379     """Represents options during C interface generation for headers"""
    380     def __init__(self,
    381                  filename = None,
    382                  apiname = None,
    383                  profile = None,
    384                  versions = '.*',
    385                  emitversions = '.*',
    386                  defaultExtensions = None,
    387                  addExtensions = None,
    388                  removeExtensions = None,
    389                  sortProcedure = regSortFeatures,
    390                  prefixText = "",
    391                  genFuncPointers = True,
    392                  protectFile = True,
    393                  protectFeature = True,
    394                  protectProto = None,
    395                  protectProtoStr = None,
    396                  apicall = '',
    397                  apientry = '',
    398                  apientryp = '',
    399                  indentFuncProto = True,
    400                  indentFuncPointer = False,
    401                  alignFuncParam = 0,
    402                  genDirectory = None):
    403         GeneratorOptions.__init__(self, filename, apiname, profile,
    404                                   versions, emitversions, defaultExtensions,
    405                                   addExtensions, removeExtensions, sortProcedure)
    406         self.prefixText      = prefixText
    407         self.genFuncPointers = genFuncPointers
    408         self.protectFile     = protectFile
    409         self.protectFeature  = protectFeature
    410         self.protectProto    = protectProto
    411         self.protectProtoStr = protectProtoStr
    412         self.apicall         = apicall
    413         self.apientry        = apientry
    414         self.apientryp       = apientryp
    415         self.indentFuncProto = indentFuncProto
    416         self.indentFuncPointer = indentFuncPointer
    417         self.alignFuncParam  = alignFuncParam
    418         self.genDirectory    = genDirectory
    419 
    420 
    421 # OutputGenerator - base class for generating API interfaces.
    422 # Manages basic logic, logging, and output file control
    423 # Derived classes actually generate formatted output.
    424 #
    425 # ---- methods ----
    426 # OutputGenerator(errFile, warnFile, diagFile)
    427 #   errFile, warnFile, diagFile - file handles to write errors,
    428 #     warnings, diagnostics to. May be None to not write.
    429 # logMsg(level, *args) - log messages of different categories
    430 #   level - 'error', 'warn', or 'diag'. 'error' will also
    431 #     raise a UserWarning exception
    432 #   *args - print()-style arguments
    433 # setExtMap(map) - specify a dictionary map from extension names to
    434 #   numbers, used in creating values for extension enumerants.
    435 # beginFile(genOpts) - start a new interface file
    436 #   genOpts - GeneratorOptions controlling what's generated and how
    437 # endFile() - finish an interface file, closing it when done
    438 # beginFeature(interface, emit) - write interface for a feature
    439 # and tag generated features as having been done.
    440 #   interface - element for the <version> / <extension> to generate
    441 #   emit - actually write to the header only when True
    442 # endFeature() - finish an interface.
    443 # genType(typeinfo,name) - generate interface for a type
    444 #   typeinfo - TypeInfo for a type
    445 # genStruct(typeinfo,name) - generate interface for a C "struct" type.
    446 #   typeinfo - TypeInfo for a type interpreted as a struct
    447 # genGroup(groupinfo,name) - generate interface for a group of enums (C "enum")
    448 #   groupinfo - GroupInfo for a group
    449 # genEnum(enuminfo, name) - generate interface for an enum (constant)
    450 #   enuminfo - EnumInfo for an enum
    451 #   name - enum name
    452 # genCmd(cmdinfo) - generate interface for a command
    453 #   cmdinfo - CmdInfo for a command
    454 # makeCDecls(cmd) - return C prototype and function pointer typedef for a
    455 #     <command> Element, as a list of two strings
    456 #   cmd - Element for the <command>
    457 # newline() - print a newline to the output file (utility function)
    458 #
    459 class OutputGenerator:
    460     """Generate specified API interfaces in a specific style, such as a C header"""
    461     def __init__(self,
    462                  errFile = sys.stderr,
    463                  warnFile = sys.stderr,
    464                  diagFile = sys.stdout):
    465         self.outFile = None
    466         self.errFile = errFile
    467         self.warnFile = warnFile
    468         self.diagFile = diagFile
    469         # Internal state
    470         self.featureName = None
    471         self.genOpts = None
    472         self.registry = None
    473         # Used for extension enum value generation
    474         self.extBase      = 1000000000
    475         self.extBlockSize = 1000
    476     #
    477     # logMsg - write a message of different categories to different
    478     #   destinations.
    479     # level -
    480     #   'diag' (diagnostic, voluminous)
    481     #   'warn' (warning)
    482     #   'error' (fatal error - raises exception after logging)
    483     # *args - print()-style arguments to direct to corresponding log
    484     def logMsg(self, level, *args):
    485         """Log a message at the given level. Can be ignored or log to a file"""
    486         if (level == 'error'):
    487             strfile = io.StringIO()
    488             write('ERROR:', *args, file=strfile)
    489             if (self.errFile != None):
    490                 write(strfile.getvalue(), file=self.errFile)
    491             raise UserWarning(strfile.getvalue())
    492         elif (level == 'warn'):
    493             if (self.warnFile != None):
    494                 write('WARNING:', *args, file=self.warnFile)
    495         elif (level == 'diag'):
    496             if (self.diagFile != None):
    497                 write('DIAG:', *args, file=self.diagFile)
    498         else:
    499             raise UserWarning(
    500                 '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
    501     #
    502     # enumToValue - parses and converts an <enum> tag into a value.
    503     # Returns a list
    504     #   first element - integer representation of the value, or None
    505     #       if needsNum is False. The value must be a legal number
    506     #       if needsNum is True.
    507     #   second element - string representation of the value
    508     # There are several possible representations of values.
    509     #   A 'value' attribute simply contains the value.
    510     #   A 'bitpos' attribute defines a value by specifying the bit
    511     #       position which is set in that value.
    512     #   A 'offset','extbase','extends' triplet specifies a value
    513     #       as an offset to a base value defined by the specified
    514     #       'extbase' extension name, which is then cast to the
    515     #       typename specified by 'extends'. This requires probing
    516     #       the registry database, and imbeds knowledge of the
    517     #       Vulkan extension enum scheme in this function.
    518     def enumToValue(self, elem, needsNum):
    519         name = elem.get('name')
    520         numVal = None
    521         if ('value' in elem.keys()):
    522             value = elem.get('value')
    523             # print('About to translate value =', value, 'type =', type(value))
    524             if (needsNum):
    525                 numVal = int(value, 0)
    526             # If there's a non-integer, numeric 'type' attribute (e.g. 'u' or
    527             # 'ull'), append it to the string value.
    528             # t = enuminfo.elem.get('type')
    529             # if (t != None and t != '' and t != 'i' and t != 's'):
    530             #     value += enuminfo.type
    531             self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
    532             return [numVal, value]
    533         if ('bitpos' in elem.keys()):
    534             value = elem.get('bitpos')
    535             numVal = int(value, 0)
    536             numVal = 1 << numVal
    537             value = '0x%08x' % numVal
    538             self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
    539             return [numVal, value]
    540         if ('offset' in elem.keys()):
    541             # Obtain values in the mapping from the attributes
    542             enumNegative = False
    543             offset = int(elem.get('offset'),0)
    544             extnumber = int(elem.get('extnumber'),0)
    545             extends = elem.get('extends')
    546             if ('dir' in elem.keys()):
    547                 enumNegative = True
    548             self.logMsg('diag', 'Enum', name, 'offset =', offset,
    549                 'extnumber =', extnumber, 'extends =', extends,
    550                 'enumNegative =', enumNegative)
    551             # Now determine the actual enumerant value, as defined
    552             # in the "Layers and Extensions" appendix of the spec.
    553             numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
    554             if (enumNegative):
    555                 numVal = -numVal
    556             value = '%d' % numVal
    557             # More logic needed!
    558             self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
    559             return [numVal, value]
    560         return [None, None]
    561     #
    562     def beginFile(self, genOpts):
    563         self.genOpts = genOpts
    564         #
    565         # Open specified output file. Not done in constructor since a
    566         # Generator can be used without writing to a file.
    567         if (self.genOpts.filename != None):
    568             if (self.genOpts.genDirectory != None):
    569                 self.outFile = open(os.path.join(self.genOpts.genDirectory, self.genOpts.filename), 'w')
    570             else:
    571                 self.outFile = open(self.genOpts.filename, 'w')
    572         else:
    573             self.outFile = sys.stdout
    574     def endFile(self):
    575         self.errFile and self.errFile.flush()
    576         self.warnFile and self.warnFile.flush()
    577         self.diagFile and self.diagFile.flush()
    578         self.outFile.flush()
    579         if (self.outFile != sys.stdout and self.outFile != sys.stderr):
    580             self.outFile.close()
    581         self.genOpts = None
    582     #
    583     def beginFeature(self, interface, emit):
    584         self.emit = emit
    585         self.featureName = interface.get('name')
    586         # If there's an additional 'protect' attribute in the feature, save it
    587         self.featureExtraProtect = interface.get('protect')
    588     def endFeature(self):
    589         # Derived classes responsible for emitting feature
    590         self.featureName = None
    591         self.featureExtraProtect = None
    592     # Utility method to validate we're generating something only inside a
    593     # <feature> tag
    594     def validateFeature(self, featureType, featureName):
    595         if (self.featureName == None):
    596             raise UserWarning('Attempt to generate', featureType, name,
    597                     'when not in feature')
    598     #
    599     # Type generation
    600     def genType(self, typeinfo, name):
    601         self.validateFeature('type', name)
    602     #
    603     # Struct (e.g. C "struct" type) generation
    604     def genStruct(self, typeinfo, name):
    605         self.validateFeature('struct', name)
    606     #
    607     # Group (e.g. C "enum" type) generation
    608     def genGroup(self, groupinfo, name):
    609         self.validateFeature('group', name)
    610     #
    611     # Enumerant (really, constant) generation
    612     def genEnum(self, enuminfo, name):
    613         self.validateFeature('enum', name)
    614     #
    615     # Command generation
    616     def genCmd(self, cmd, name):
    617         self.validateFeature('command', name)
    618     #
    619     # Utility functions - turn a <proto> <name> into C-language prototype
    620     # and typedef declarations for that name.
    621     # name - contents of <name> tag
    622     # tail - whatever text follows that tag in the Element
    623     def makeProtoName(self, name, tail):
    624         return self.genOpts.apientry + name + tail
    625     def makeTypedefName(self, name, tail):
    626        return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')'
    627     #
    628     # makeCParamDecl - return a string which is an indented, formatted
    629     # declaration for a <param> or <member> block (e.g. function parameter
    630     # or structure/union member).
    631     # param - Element (<param> or <member>) to format
    632     # aligncol - if non-zero, attempt to align the nested <name> element
    633     #   at this column
    634     def makeCParamDecl(self, param, aligncol):
    635         paramdecl = '    ' + noneStr(param.text)
    636         for elem in param:
    637             text = noneStr(elem.text)
    638             tail = noneStr(elem.tail)
    639             if (elem.tag == 'name' and aligncol > 0):
    640                 self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam)
    641                 # Align at specified column, if possible
    642                 paramdecl = paramdecl.rstrip()
    643                 oldLen = len(paramdecl)
    644                 paramdecl = paramdecl.ljust(aligncol)
    645                 newLen = len(paramdecl)
    646                 self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl)
    647             paramdecl += text + tail
    648         return paramdecl
    649     #
    650     # getCParamTypeLength - return the length of the type field is an indented, formatted
    651     # declaration for a <param> or <member> block (e.g. function parameter
    652     # or structure/union member).
    653     # param - Element (<param> or <member>) to identify
    654     def getCParamTypeLength(self, param):
    655         paramdecl = '    ' + noneStr(param.text)
    656         for elem in param:
    657             text = noneStr(elem.text)
    658             tail = noneStr(elem.tail)
    659             if (elem.tag == 'name'):
    660                 # Align at specified column, if possible
    661                 newLen = len(paramdecl.rstrip())
    662                 self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen)
    663             paramdecl += text + tail
    664         return newLen
    665     #
    666     # makeCDecls - return C prototype and function pointer typedef for a
    667     #   command, as a two-element list of strings.
    668     # cmd - Element containing a <command> tag
    669     def makeCDecls(self, cmd):
    670         """Generate C function pointer typedef for <command> Element"""
    671         proto = cmd.find('proto')
    672         params = cmd.findall('param')
    673         # Begin accumulating prototype and typedef strings
    674         pdecl = self.genOpts.apicall
    675         tdecl = 'typedef '
    676         #
    677         # Insert the function return type/name.
    678         # For prototypes, add APIENTRY macro before the name
    679         # For typedefs, add (APIENTRY *<name>) around the name and
    680         #   use the PFN_cmdnameproc naming convention.
    681         # Done by walking the tree for <proto> element by element.
    682         # etree has elem.text followed by (elem[i], elem[i].tail)
    683         #   for each child element and any following text
    684         # Leading text
    685         pdecl += noneStr(proto.text)
    686         tdecl += noneStr(proto.text)
    687         # For each child element, if it's a <name> wrap in appropriate
    688         # declaration. Otherwise append its contents and tail contents.
    689         for elem in proto:
    690             text = noneStr(elem.text)
    691             tail = noneStr(elem.tail)
    692             if (elem.tag == 'name'):
    693                 pdecl += self.makeProtoName(text, tail)
    694                 tdecl += self.makeTypedefName(text, tail)
    695             else:
    696                 pdecl += text + tail
    697                 tdecl += text + tail
    698         # Now add the parameter declaration list, which is identical
    699         # for prototypes and typedefs. Concatenate all the text from
    700         # a <param> node without the tags. No tree walking required
    701         # since all tags are ignored.
    702         # Uses: self.indentFuncProto
    703         # self.indentFuncPointer
    704         # self.alignFuncParam
    705         # Might be able to doubly-nest the joins, e.g.
    706         #   ','.join(('_'.join([l[i] for i in range(0,len(l))])
    707         n = len(params)
    708         # Indented parameters
    709         if n > 0:
    710             indentdecl = '(\n'
    711             for i in range(0,n):
    712                 paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam)
    713                 if (i < n - 1):
    714                     paramdecl += ',\n'
    715                 else:
    716                     paramdecl += ');'
    717                 indentdecl += paramdecl
    718         else:
    719             indentdecl = '(void);'
    720         # Non-indented parameters
    721         paramdecl = '('
    722         if n > 0:
    723             for i in range(0,n):
    724                 paramdecl += ''.join([t for t in params[i].itertext()])
    725                 if (i < n - 1):
    726                     paramdecl += ', '
    727         else:
    728             paramdecl += 'void'
    729         paramdecl += ");";
    730         return [ pdecl + indentdecl, tdecl + paramdecl ]
    731     #
    732     def newline(self):
    733         write('', file=self.outFile)
    734 
    735     def setRegistry(self, registry):
    736         self.registry = registry
    737         #
    738 
    739 # COutputGenerator - subclass of OutputGenerator.
    740 # Generates C-language API interfaces.
    741 #
    742 # ---- methods ----
    743 # COutputGenerator(errFile, warnFile, diagFile) - args as for
    744 #   OutputGenerator. Defines additional internal state.
    745 # ---- methods overriding base class ----
    746 # beginFile(genOpts)
    747 # endFile()
    748 # beginFeature(interface, emit)
    749 # endFeature()
    750 # genType(typeinfo,name)
    751 # genStruct(typeinfo,name)
    752 # genGroup(groupinfo,name)
    753 # genEnum(enuminfo, name)
    754 # genCmd(cmdinfo)
    755 class COutputGenerator(OutputGenerator):
    756     """Generate specified API interfaces in a specific style, such as a C header"""
    757     # This is an ordered list of sections in the header file.
    758     TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
    759                      'group', 'bitmask', 'funcpointer', 'struct']
    760     ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
    761     def __init__(self,
    762                  errFile = sys.stderr,
    763                  warnFile = sys.stderr,
    764                  diagFile = sys.stdout):
    765         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
    766         # Internal state - accumulators for different inner block text
    767         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
    768     #
    769     def beginFile(self, genOpts):
    770         OutputGenerator.beginFile(self, genOpts)
    771         # C-specific
    772         #
    773         # Multiple inclusion protection & C++ wrappers.
    774         if (genOpts.protectFile and self.genOpts.filename):
    775             headerSym = re.sub('\.h', '_h_',
    776                                os.path.basename(self.genOpts.filename)).upper()
    777             write('#ifndef', headerSym, file=self.outFile)
    778             write('#define', headerSym, '1', file=self.outFile)
    779             self.newline()
    780         write('#ifdef __cplusplus', file=self.outFile)
    781         write('extern "C" {', file=self.outFile)
    782         write('#endif', file=self.outFile)
    783         self.newline()
    784         #
    785         # User-supplied prefix text, if any (list of strings)
    786         if (genOpts.prefixText):
    787             for s in genOpts.prefixText:
    788                 write(s, file=self.outFile)
    789         #
    790         # Some boilerplate describing what was generated - this
    791         # will probably be removed later since the extensions
    792         # pattern may be very long.
    793         # write('/* Generated C header for:', file=self.outFile)
    794         # write(' * API:', genOpts.apiname, file=self.outFile)
    795         # if (genOpts.profile):
    796         #     write(' * Profile:', genOpts.profile, file=self.outFile)
    797         # write(' * Versions considered:', genOpts.versions, file=self.outFile)
    798         # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile)
    799         # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile)
    800         # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile)
    801         # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile)
    802         # write(' */', file=self.outFile)
    803     def endFile(self):
    804         # C-specific
    805         # Finish C++ wrapper and multiple inclusion protection
    806         self.newline()
    807         write('#ifdef __cplusplus', file=self.outFile)
    808         write('}', file=self.outFile)
    809         write('#endif', file=self.outFile)
    810         if (self.genOpts.protectFile and self.genOpts.filename):
    811             self.newline()
    812             write('#endif', file=self.outFile)
    813         # Finish processing in superclass
    814         OutputGenerator.endFile(self)
    815     def beginFeature(self, interface, emit):
    816         # Start processing in superclass
    817         OutputGenerator.beginFeature(self, interface, emit)
    818         # C-specific
    819         # Accumulate includes, defines, types, enums, function pointer typedefs,
    820         # end function prototypes separately for this feature. They're only
    821         # printed in endFeature().
    822         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
    823     def endFeature(self):
    824         # C-specific
    825         # Actually write the interface to the output file.
    826         if (self.emit):
    827             self.newline()
    828             if (self.genOpts.protectFeature):
    829                 write('#ifndef', self.featureName, file=self.outFile)
    830             # If type declarations are needed by other features based on
    831             # this one, it may be necessary to suppress the ExtraProtect,
    832             # or move it below the 'for section...' loop.
    833             if (self.featureExtraProtect != None):
    834                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
    835             write('#define', self.featureName, '1', file=self.outFile)
    836             for section in self.TYPE_SECTIONS:
    837                 contents = self.sections[section]
    838                 if contents:
    839                     write('\n'.join(contents), file=self.outFile)
    840                     self.newline()
    841             if (self.genOpts.genFuncPointers and self.sections['commandPointer']):
    842                 write('\n'.join(self.sections['commandPointer']), file=self.outFile)
    843                 self.newline()
    844             if (self.sections['command']):
    845                 if (self.genOpts.protectProto):
    846                     write(self.genOpts.protectProto,
    847                           self.genOpts.protectProtoStr, file=self.outFile)
    848                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
    849                 if (self.genOpts.protectProto):
    850                     write('#endif', file=self.outFile)
    851                 else:
    852                     self.newline()
    853             if (self.featureExtraProtect != None):
    854                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
    855             if (self.genOpts.protectFeature):
    856                 write('#endif /*', self.featureName, '*/', file=self.outFile)
    857         # Finish processing in superclass
    858         OutputGenerator.endFeature(self)
    859     #
    860     # Append a definition to the specified section
    861     def appendSection(self, section, text):
    862         # self.sections[section].append('SECTION: ' + section + '\n')
    863         self.sections[section].append(text)
    864     #
    865     # Type generation
    866     def genType(self, typeinfo, name):
    867         OutputGenerator.genType(self, typeinfo, name)
    868         typeElem = typeinfo.elem
    869         # If the type is a struct type, traverse the imbedded <member> tags
    870         # generating a structure. Otherwise, emit the tag text.
    871         category = typeElem.get('category')
    872         if (category == 'struct' or category == 'union'):
    873             self.genStruct(typeinfo, name)
    874         else:
    875             # Replace <apientry /> tags with an APIENTRY-style string
    876             # (from self.genOpts). Copy other text through unchanged.
    877             # If the resulting text is an empty string, don't emit it.
    878             s = noneStr(typeElem.text)
    879             for elem in typeElem:
    880                 if (elem.tag == 'apientry'):
    881                     s += self.genOpts.apientry + noneStr(elem.tail)
    882                 else:
    883                     s += noneStr(elem.text) + noneStr(elem.tail)
    884             if s:
    885                 # Add extra newline after multi-line entries.
    886                 if '\n' in s:
    887                     s += '\n'
    888                 self.appendSection(category, s)
    889     #
    890     # Struct (e.g. C "struct" type) generation.
    891     # This is a special case of the <type> tag where the contents are
    892     # interpreted as a set of <member> tags instead of freeform C
    893     # C type declarations. The <member> tags are just like <param>
    894     # tags - they are a declaration of a struct or union member.
    895     # Only simple member declarations are supported (no nested
    896     # structs etc.)
    897     def genStruct(self, typeinfo, typeName):
    898         OutputGenerator.genStruct(self, typeinfo, typeName)
    899         body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
    900         # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
    901         targetLen = 0;
    902         for member in typeinfo.elem.findall('.//member'):
    903             targetLen = max(targetLen, self.getCParamTypeLength(member))
    904         for member in typeinfo.elem.findall('.//member'):
    905             body += self.makeCParamDecl(member, targetLen + 4)
    906             body += ';\n'
    907         body += '} ' + typeName + ';\n'
    908         self.appendSection('struct', body)
    909     #
    910     # Group (e.g. C "enum" type) generation.
    911     # These are concatenated together with other types.
    912     def genGroup(self, groupinfo, groupName):
    913         OutputGenerator.genGroup(self, groupinfo, groupName)
    914         groupElem = groupinfo.elem
    915 
    916         expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
    917 
    918         expandPrefix = expandName
    919         expandSuffix = ''
    920         expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
    921         if expandSuffixMatch:
    922             expandSuffix = '_' + expandSuffixMatch.group()
    923             # Strip off the suffix from the prefix
    924             expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
    925 
    926         # Prefix
    927         body = "\ntypedef enum " + groupName + " {\n"
    928 
    929         isEnum = ('FLAG_BITS' not in expandPrefix)
    930 
    931         # Loop over the nested 'enum' tags. Keep track of the minimum and
    932         # maximum numeric values, if they can be determined; but only for
    933         # core API enumerants, not extension enumerants. This is inferred
    934         # by looking for 'extends' attributes.
    935         minName = None
    936         for elem in groupElem.findall('enum'):
    937             # Convert the value to an integer and use that to track min/max.
    938             # Values of form -(number) are accepted but nothing more complex.
    939             # Should catch exceptions here for more complex constructs. Not yet.
    940             (numVal,strVal) = self.enumToValue(elem, True)
    941             name = elem.get('name')
    942 
    943             # Extension enumerants are only included if they are requested
    944             # in addExtensions or match defaultExtensions.
    945             if (elem.get('extname') is None or
    946               re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
    947               self.genOpts.defaultExtensions == elem.get('supported')):
    948                 body += "    " + name + " = " + strVal + ",\n"
    949 
    950             if (isEnum  and elem.get('extends') is None):
    951                 if (minName == None):
    952                     minName = maxName = name
    953                     minValue = maxValue = numVal
    954                 elif (numVal < minValue):
    955                     minName = name
    956                     minValue = numVal
    957                 elif (numVal > maxValue):
    958                     maxName = name
    959                     maxValue = numVal
    960         # Generate min/max value tokens and a range-padding enum. Need some
    961         # additional padding to generate correct names...
    962         if isEnum:
    963             body += "    " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
    964             body += "    " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
    965             body += "    " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
    966 
    967         body += "    " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
    968 
    969         # Postfix
    970         body += "} " + groupName + ";"
    971         if groupElem.get('type') == 'bitmask':
    972             section = 'bitmask'
    973         else:
    974             section = 'group'
    975         self.appendSection(section, body)
    976     # Enumerant generation
    977     # <enum> tags may specify their values in several ways, but are usually
    978     # just integers.
    979     def genEnum(self, enuminfo, name):
    980         OutputGenerator.genEnum(self, enuminfo, name)
    981         (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
    982         body = '#define ' + name.ljust(33) + ' ' + strVal
    983         self.appendSection('enum', body)
    984     #
    985     # Command generation
    986     def genCmd(self, cmdinfo, name):
    987         OutputGenerator.genCmd(self, cmdinfo, name)
    988         #
    989         decls = self.makeCDecls(cmdinfo.elem)
    990         self.appendSection('command', decls[0] + '\n')
    991         if (self.genOpts.genFuncPointers):
    992             self.appendSection('commandPointer', decls[1])
    993 
    994 # DocOutputGenerator - subclass of OutputGenerator.
    995 # Generates AsciiDoc includes with C-language API interfaces, for reference
    996 # pages and the Vulkan specification. Similar to COutputGenerator, but
    997 # each interface is written into a different file as determined by the
    998 # options, only actual C types are emitted, and none of the boilerplate
    999 # preprocessor code is emitted.
   1000 #
   1001 # ---- methods ----
   1002 # DocOutputGenerator(errFile, warnFile, diagFile) - args as for
   1003 #   OutputGenerator. Defines additional internal state.
   1004 # ---- methods overriding base class ----
   1005 # beginFile(genOpts)
   1006 # endFile()
   1007 # beginFeature(interface, emit)
   1008 # endFeature()
   1009 # genType(typeinfo,name)
   1010 # genStruct(typeinfo,name)
   1011 # genGroup(groupinfo,name)
   1012 # genEnum(enuminfo, name)
   1013 # genCmd(cmdinfo)
   1014 class DocOutputGenerator(OutputGenerator):
   1015     """Generate specified API interfaces in a specific style, such as a C header"""
   1016     def __init__(self,
   1017                  errFile = sys.stderr,
   1018                  warnFile = sys.stderr,
   1019                  diagFile = sys.stdout):
   1020         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
   1021     #
   1022     def beginFile(self, genOpts):
   1023         OutputGenerator.beginFile(self, genOpts)
   1024     def endFile(self):
   1025         OutputGenerator.endFile(self)
   1026     def beginFeature(self, interface, emit):
   1027         # Start processing in superclass
   1028         OutputGenerator.beginFeature(self, interface, emit)
   1029     def endFeature(self):
   1030         # Finish processing in superclass
   1031         OutputGenerator.endFeature(self)
   1032     #
   1033     # Generate an include file
   1034     #
   1035     # directory - subdirectory to put file in
   1036     # basename - base name of the file
   1037     # contents - contents of the file (Asciidoc boilerplate aside)
   1038     def writeInclude(self, directory, basename, contents):
   1039         # Create file
   1040         filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
   1041         self.logMsg('diag', '# Generating include file:', filename)
   1042         fp = open(filename, 'w')
   1043         # Asciidoc anchor
   1044         write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
   1045         write('ifndef::doctype-manpage[]', file=fp)
   1046         write('[[{0},{0}]]'.format(basename), file=fp)
   1047         write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp)
   1048         write('endif::doctype-manpage[]', file=fp)
   1049         write('ifdef::doctype-manpage[]', file=fp)
   1050         write('["source","{basebackend@docbook:c++:cpp}"]', file=fp)
   1051         write('endif::doctype-manpage[]', file=fp)
   1052         write('------------------------------------------------------------------------------', file=fp)
   1053         write(contents, file=fp)
   1054         write('------------------------------------------------------------------------------', file=fp)
   1055         fp.close()
   1056     #
   1057     # Type generation
   1058     def genType(self, typeinfo, name):
   1059         OutputGenerator.genType(self, typeinfo, name)
   1060         typeElem = typeinfo.elem
   1061         # If the type is a struct type, traverse the imbedded <member> tags
   1062         # generating a structure. Otherwise, emit the tag text.
   1063         category = typeElem.get('category')
   1064         if (category == 'struct' or category == 'union'):
   1065             self.genStruct(typeinfo, name)
   1066         else:
   1067             # Replace <apientry /> tags with an APIENTRY-style string
   1068             # (from self.genOpts). Copy other text through unchanged.
   1069             # If the resulting text is an empty string, don't emit it.
   1070             s = noneStr(typeElem.text)
   1071             for elem in typeElem:
   1072                 if (elem.tag == 'apientry'):
   1073                     s += self.genOpts.apientry + noneStr(elem.tail)
   1074                 else:
   1075                     s += noneStr(elem.text) + noneStr(elem.tail)
   1076             if (len(s) > 0):
   1077                 if (category == 'bitmask'):
   1078                     self.writeInclude('flags', name, s + '\n')
   1079                 elif (category == 'enum'):
   1080                     self.writeInclude('enums', name, s + '\n')
   1081                 elif (category == 'funcpointer'):
   1082                     self.writeInclude('funcpointers', name, s+ '\n')
   1083                 else:
   1084                     self.logMsg('diag', '# NOT writing include file for type:',
   1085                         name, 'category: ', category)
   1086             else:
   1087                 self.logMsg('diag', '# NOT writing empty include file for type', name)
   1088     #
   1089     # Struct (e.g. C "struct" type) generation.
   1090     # This is a special case of the <type> tag where the contents are
   1091     # interpreted as a set of <member> tags instead of freeform C
   1092     # C type declarations. The <member> tags are just like <param>
   1093     # tags - they are a declaration of a struct or union member.
   1094     # Only simple member declarations are supported (no nested
   1095     # structs etc.)
   1096     def genStruct(self, typeinfo, typeName):
   1097         OutputGenerator.genStruct(self, typeinfo, typeName)
   1098         s = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
   1099         # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
   1100         targetLen = 0;
   1101         for member in typeinfo.elem.findall('.//member'):
   1102             targetLen = max(targetLen, self.getCParamTypeLength(member))
   1103         for member in typeinfo.elem.findall('.//member'):
   1104             s += self.makeCParamDecl(member, targetLen + 4)
   1105             s += ';\n'
   1106         s += '} ' + typeName + ';'
   1107         self.writeInclude('structs', typeName, s)
   1108     #
   1109     # Group (e.g. C "enum" type) generation.
   1110     # These are concatenated together with other types.
   1111     def genGroup(self, groupinfo, groupName):
   1112         OutputGenerator.genGroup(self, groupinfo, groupName)
   1113         groupElem = groupinfo.elem
   1114 
   1115         # See if we need min/max/num/padding at end
   1116         expand = self.genOpts.expandEnumerants
   1117 
   1118         if expand:
   1119             expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
   1120             isEnum = ('FLAG_BITS' not in expandName)
   1121 
   1122             expandPrefix = expandName
   1123             expandSuffix = ''
   1124 
   1125             # Look for a suffix
   1126             expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
   1127             if expandSuffixMatch:
   1128                 expandSuffix = '_' + expandSuffixMatch.group()
   1129                 # Strip off the suffix from the prefix
   1130                 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
   1131 
   1132         # Prefix
   1133         s = "typedef enum " + groupName + " {\n"
   1134 
   1135         # Loop over the nested 'enum' tags. Keep track of the minimum and
   1136         # maximum numeric values, if they can be determined.
   1137         minName = None
   1138         for elem in groupElem.findall('enum'):
   1139             # Convert the value to an integer and use that to track min/max.
   1140             # Values of form -(number) are accepted but nothing more complex.
   1141             # Should catch exceptions here for more complex constructs. Not yet.
   1142             (numVal,strVal) = self.enumToValue(elem, True)
   1143             name = elem.get('name')
   1144 
   1145             # Extension enumerants are only included if they are requested
   1146             # in addExtensions or match defaultExtensions.
   1147             if (elem.get('extname') is None or
   1148               re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
   1149               self.genOpts.defaultExtensions == elem.get('supported')):
   1150                 s += "    " + name + " = " + strVal + ",\n"
   1151 
   1152             if (expand and isEnum and elem.get('extends') is None):
   1153                 if (minName == None):
   1154                     minName = maxName = name
   1155                     minValue = maxValue = numVal
   1156                 elif (numVal < minValue):
   1157                     minName = name
   1158                     minValue = numVal
   1159                 elif (numVal > maxValue):
   1160                     maxName = name
   1161                     maxValue = numVal
   1162         # Generate min/max value tokens and a range-padding enum. Need some
   1163         # additional padding to generate correct names...
   1164         if (expand):
   1165             s += "\n"
   1166             if isEnum:
   1167                 s += "    " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
   1168                 s += "    " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
   1169                 s += "    " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
   1170 
   1171             s += "    " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
   1172         # Postfix
   1173         s += "} " + groupName + ";"
   1174         self.writeInclude('enums', groupName, s)
   1175     # Enumerant generation
   1176     # <enum> tags may specify their values in several ways, but are usually
   1177     # just integers.
   1178     def genEnum(self, enuminfo, name):
   1179         OutputGenerator.genEnum(self, enuminfo, name)
   1180         (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
   1181         s = '#define ' + name.ljust(33) + ' ' + strVal
   1182         self.logMsg('diag', '# NOT writing compile-time constant', name)
   1183         # self.writeInclude('consts', name, s)
   1184     #
   1185     # Command generation
   1186     def genCmd(self, cmdinfo, name):
   1187         OutputGenerator.genCmd(self, cmdinfo, name)
   1188         #
   1189         decls = self.makeCDecls(cmdinfo.elem)
   1190         self.writeInclude('protos', name, decls[0])
   1191 
   1192 # PyOutputGenerator - subclass of OutputGenerator.
   1193 # Generates Python data structures describing API names.
   1194 # Similar to DocOutputGenerator, but writes a single
   1195 # file.
   1196 #
   1197 # ---- methods ----
   1198 # PyOutputGenerator(errFile, warnFile, diagFile) - args as for
   1199 #   OutputGenerator. Defines additional internal state.
   1200 # ---- methods overriding base class ----
   1201 # beginFile(genOpts)
   1202 # endFile()
   1203 # genType(typeinfo,name)
   1204 # genStruct(typeinfo,name)
   1205 # genGroup(groupinfo,name)
   1206 # genEnum(enuminfo, name)
   1207 # genCmd(cmdinfo)
   1208 class PyOutputGenerator(OutputGenerator):
   1209     """Generate specified API interfaces in a specific style, such as a C header"""
   1210     def __init__(self,
   1211                  errFile = sys.stderr,
   1212                  warnFile = sys.stderr,
   1213                  diagFile = sys.stdout):
   1214         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
   1215     #
   1216     def beginFile(self, genOpts):
   1217         OutputGenerator.beginFile(self, genOpts)
   1218         for dict in [ 'flags', 'enums', 'structs', 'consts', 'enums',
   1219           'consts', 'protos', 'funcpointers' ]:
   1220             write(dict, '= {}', file=self.outFile)
   1221     def endFile(self):
   1222         OutputGenerator.endFile(self)
   1223     #
   1224     # Add a name from the interface
   1225     #
   1226     # dict - type of name (see beginFile above)
   1227     # name - name to add
   1228     # value - A serializable Python value for the name
   1229     def addName(self, dict, name, value=None):
   1230         write(dict + "['" + name + "'] = ", value, file=self.outFile)
   1231     #
   1232     # Type generation
   1233     # For 'struct' or 'union' types, defer to genStruct() to
   1234     #   add to the dictionary.
   1235     # For 'bitmask' types, add the type name to the 'flags' dictionary,
   1236     #   with the value being the corresponding 'enums' name defining
   1237     #   the acceptable flag bits.
   1238     # For 'enum' types, add the type name to the 'enums' dictionary,
   1239     #   with the value being '@STOPHERE@' (because this case seems
   1240     #   never to happen).
   1241     # For 'funcpointer' types, add the type name to the 'funcpointers'
   1242     #   dictionary.
   1243     # For 'handle' and 'define' types, add the handle or #define name
   1244     #   to the 'struct' dictionary, because that's how the spec sources
   1245     #   tag these types even though they aren't structs.
   1246     def genType(self, typeinfo, name):
   1247         OutputGenerator.genType(self, typeinfo, name)
   1248         typeElem = typeinfo.elem
   1249         # If the type is a struct type, traverse the imbedded <member> tags
   1250         # generating a structure. Otherwise, emit the tag text.
   1251         category = typeElem.get('category')
   1252         if (category == 'struct' or category == 'union'):
   1253             self.genStruct(typeinfo, name)
   1254         else:
   1255             # Extract the type name
   1256             # (from self.genOpts). Copy other text through unchanged.
   1257             # If the resulting text is an empty string, don't emit it.
   1258             count = len(noneStr(typeElem.text))
   1259             for elem in typeElem:
   1260                 count += len(noneStr(elem.text)) + len(noneStr(elem.tail))
   1261             if (count > 0):
   1262                 if (category == 'bitmask'):
   1263                     requiredEnum = typeElem.get('requires')
   1264                     self.addName('flags', name, enquote(requiredEnum))
   1265                 elif (category == 'enum'):
   1266                     # This case never seems to come up!
   1267                     # @enums   C 'enum' name           Dictionary of enumerant names
   1268                     self.addName('enums', name, enquote('@STOPHERE@'))
   1269                 elif (category == 'funcpointer'):
   1270                     self.addName('funcpointers', name, None)
   1271                 elif (category == 'handle' or category == 'define'):
   1272                     self.addName('structs', name, None)
   1273                 else:
   1274                     write('# Unprocessed type:', name, 'category:', category, file=self.outFile)
   1275             else:
   1276                 write('# Unprocessed type:', name, file=self.outFile)
   1277     #
   1278     # Struct (e.g. C "struct" type) generation.
   1279     #
   1280     # Add the struct name to the 'structs' dictionary, with the
   1281     # value being an ordered list of the struct member names.
   1282     def genStruct(self, typeinfo, typeName):
   1283         OutputGenerator.genStruct(self, typeinfo, typeName)
   1284 
   1285         members = [member.text for member in typeinfo.elem.findall('.//member/name')]
   1286         self.addName('structs', typeName, members)
   1287     #
   1288     # Group (e.g. C "enum" type) generation.
   1289     # These are concatenated together with other types.
   1290     #
   1291     # Add the enum type name to the 'enums' dictionary, with
   1292     #   the value being an ordered list of the enumerant names.
   1293     # Add each enumerant name to the 'consts' dictionary, with
   1294     #   the value being the enum type the enumerant is part of.
   1295     def genGroup(self, groupinfo, groupName):
   1296         OutputGenerator.genGroup(self, groupinfo, groupName)
   1297         groupElem = groupinfo.elem
   1298 
   1299         # @enums   C 'enum' name           Dictionary of enumerant names
   1300         # @consts  C enumerant/const name  Name of corresponding 'enums' key
   1301 
   1302         # Loop over the nested 'enum' tags. Keep track of the minimum and
   1303         # maximum numeric values, if they can be determined.
   1304         enumerants = [elem.get('name') for elem in groupElem.findall('enum')]
   1305         for name in enumerants:
   1306             self.addName('consts', name, enquote(groupName))
   1307         self.addName('enums', groupName, enumerants)
   1308     # Enumerant generation (compile-time constants)
   1309     #
   1310     # Add the constant name to the 'consts' dictionary, with the
   1311     #   value being None to indicate that the constant isn't
   1312     #   an enumeration value.
   1313     def genEnum(self, enuminfo, name):
   1314         OutputGenerator.genEnum(self, enuminfo, name)
   1315 
   1316         # @consts  C enumerant/const name  Name of corresponding 'enums' key
   1317 
   1318         self.addName('consts', name, None)
   1319     #
   1320     # Command generation
   1321     #
   1322     # Add the command name to the 'protos' dictionary, with the
   1323     #   value being an ordered list of the parameter names.
   1324     def genCmd(self, cmdinfo, name):
   1325         OutputGenerator.genCmd(self, cmdinfo, name)
   1326 
   1327         params = [param.text for param in cmdinfo.elem.findall('param/name')]
   1328         self.addName('protos', name, params)
   1329 
   1330 # ValidityOutputGenerator - subclass of OutputGenerator.
   1331 # Generates AsciiDoc includes of valid usage information, for reference
   1332 # pages and the Vulkan specification. Similar to DocOutputGenerator.
   1333 #
   1334 # ---- methods ----
   1335 # ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
   1336 #   OutputGenerator. Defines additional internal state.
   1337 # ---- methods overriding base class ----
   1338 # beginFile(genOpts)
   1339 # endFile()
   1340 # beginFeature(interface, emit)
   1341 # endFeature()
   1342 # genCmd(cmdinfo)
   1343 class ValidityOutputGenerator(OutputGenerator):
   1344     """Generate specified API interfaces in a specific style, such as a C header"""
   1345     def __init__(self,
   1346                  errFile = sys.stderr,
   1347                  warnFile = sys.stderr,
   1348                  diagFile = sys.stdout):
   1349         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
   1350 
   1351     def beginFile(self, genOpts):
   1352         OutputGenerator.beginFile(self, genOpts)
   1353     def endFile(self):
   1354         OutputGenerator.endFile(self)
   1355     def beginFeature(self, interface, emit):
   1356         # Start processing in superclass
   1357         OutputGenerator.beginFeature(self, interface, emit)
   1358     def endFeature(self):
   1359         # Finish processing in superclass
   1360         OutputGenerator.endFeature(self)
   1361 
   1362     def makeParameterName(self, name):
   1363         return 'pname:' + name
   1364 
   1365     def makeStructName(self, name):
   1366         return 'sname:' + name
   1367 
   1368     def makeBaseTypeName(self, name):
   1369         return 'basetype:' + name
   1370 
   1371     def makeEnumerationName(self, name):
   1372         return 'elink:' + name
   1373 
   1374     def makeEnumerantName(self, name):
   1375         return 'ename:' + name
   1376 
   1377     def makeFLink(self, name):
   1378         return 'flink:' + name
   1379 
   1380     #
   1381     # Generate an include file
   1382     #
   1383     # directory - subdirectory to put file in
   1384     # basename - base name of the file
   1385     # contents - contents of the file (Asciidoc boilerplate aside)
   1386     def writeInclude(self, directory, basename, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes):
   1387         # Create file
   1388         filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
   1389         self.logMsg('diag', '# Generating include file:', filename)
   1390         fp = open(filename, 'w')
   1391         # Asciidoc anchor
   1392         write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
   1393 
   1394         # Valid Usage
   1395         if validity is not None:
   1396             write('ifndef::doctype-manpage[]', file=fp)
   1397             write('.Valid Usage', file=fp)
   1398             write('*' * 80, file=fp)
   1399             write('endif::doctype-manpage[]', file=fp)
   1400             write('ifdef::doctype-manpage[]', file=fp)
   1401             write('Valid Usage', file=fp)
   1402             write('-----------', file=fp)
   1403             write('endif::doctype-manpage[]', file=fp)
   1404             write(validity, file=fp, end='')
   1405             write('ifndef::doctype-manpage[]', file=fp)
   1406             write('*' * 80, file=fp)
   1407             write('endif::doctype-manpage[]', file=fp)
   1408             write('', file=fp)
   1409 
   1410         # Host Synchronization
   1411         if threadsafety is not None:
   1412             write('ifndef::doctype-manpage[]', file=fp)
   1413             write('.Host Synchronization', file=fp)
   1414             write('*' * 80, file=fp)
   1415             write('endif::doctype-manpage[]', file=fp)
   1416             write('ifdef::doctype-manpage[]', file=fp)
   1417             write('Host Synchronization', file=fp)
   1418             write('--------------------', file=fp)
   1419             write('endif::doctype-manpage[]', file=fp)
   1420             write(threadsafety, file=fp, end='')
   1421             write('ifndef::doctype-manpage[]', file=fp)
   1422             write('*' * 80, file=fp)
   1423             write('endif::doctype-manpage[]', file=fp)
   1424             write('', file=fp)
   1425 
   1426         # Command Properties - contained within a block, to avoid table numbering
   1427         if commandpropertiesentry is not None:
   1428             write('ifndef::doctype-manpage[]', file=fp)
   1429             write('.Command Properties', file=fp)
   1430             write('*' * 80, file=fp)
   1431             write('endif::doctype-manpage[]', file=fp)
   1432             write('ifdef::doctype-manpage[]', file=fp)
   1433             write('Command Properties', file=fp)
   1434             write('------------------', file=fp)
   1435             write('endif::doctype-manpage[]', file=fp)
   1436             write('[options="header", width="100%"]', file=fp)
   1437             write('|=====================', file=fp)
   1438             write('|Command Buffer Levels|Render Pass Scope|Supported Queue Types', file=fp)
   1439             write(commandpropertiesentry, file=fp)
   1440             write('|=====================', file=fp)
   1441             write('ifndef::doctype-manpage[]', file=fp)
   1442             write('*' * 80, file=fp)
   1443             write('endif::doctype-manpage[]', file=fp)
   1444             write('', file=fp)
   1445 
   1446         # Success Codes - contained within a block, to avoid table numbering
   1447         if successcodes is not None or errorcodes is not None:
   1448             write('ifndef::doctype-manpage[]', file=fp)
   1449             write('.Return Codes', file=fp)
   1450             write('*' * 80, file=fp)
   1451             write('endif::doctype-manpage[]', file=fp)
   1452             write('ifdef::doctype-manpage[]', file=fp)
   1453             write('Return Codes', file=fp)
   1454             write('------------', file=fp)
   1455             write('endif::doctype-manpage[]', file=fp)
   1456             if successcodes is not None:
   1457                 write('ifndef::doctype-manpage[]', file=fp)
   1458                 write('<<fundamentals-successcodes,Success>>::', file=fp)
   1459                 write('endif::doctype-manpage[]', file=fp)
   1460                 write('ifdef::doctype-manpage[]', file=fp)
   1461                 write('On success, this command returns::', file=fp)
   1462                 write('endif::doctype-manpage[]', file=fp)
   1463                 write(successcodes, file=fp)
   1464             if errorcodes is not None:
   1465                 write('ifndef::doctype-manpage[]', file=fp)
   1466                 write('<<fundamentals-errorcodes,Failure>>::', file=fp)
   1467                 write('endif::doctype-manpage[]', file=fp)
   1468                 write('ifdef::doctype-manpage[]', file=fp)
   1469                 write('On failure, this command returns::', file=fp)
   1470                 write('endif::doctype-manpage[]', file=fp)
   1471                 write(errorcodes, file=fp)
   1472             write('ifndef::doctype-manpage[]', file=fp)
   1473             write('*' * 80, file=fp)
   1474             write('endif::doctype-manpage[]', file=fp)
   1475             write('', file=fp)
   1476 
   1477         fp.close()
   1478 
   1479     #
   1480     # Check if the parameter passed in is a pointer
   1481     def paramIsPointer(self, param):
   1482         ispointer = False
   1483         paramtype = param.find('type')
   1484         if paramtype.tail is not None and '*' in paramtype.tail:
   1485             ispointer = True
   1486 
   1487         return ispointer
   1488 
   1489     #
   1490     # Check if the parameter passed in is a static array
   1491     def paramIsStaticArray(self, param):
   1492         if param.find('name').tail is not None:
   1493             if param.find('name').tail[0] == '[':
   1494                 return True
   1495 
   1496     #
   1497     # Get the length of a parameter that's been identified as a static array
   1498     def staticArrayLength(self, param):
   1499         paramname = param.find('name')
   1500         paramenumsize = param.find('enum')
   1501 
   1502         if paramenumsize is not None:
   1503             return paramenumsize.text
   1504         else:
   1505             return paramname.tail[1:-1]
   1506 
   1507     #
   1508     # Check if the parameter passed in is a pointer to an array
   1509     def paramIsArray(self, param):
   1510         return param.attrib.get('len') is not None
   1511 
   1512     #
   1513     # Get the parent of a handle object
   1514     def getHandleParent(self, typename):
   1515         types = self.registry.findall("types/type")
   1516         for elem in types:
   1517             if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
   1518                 return elem.attrib.get('parent')
   1519 
   1520     #
   1521     # Check if a parent object is dispatchable or not
   1522     def isHandleTypeDispatchable(self, handlename):
   1523         handle = self.registry.find("types/type/[name='" + handlename + "'][@category='handle']")
   1524         if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
   1525             return True
   1526         else:
   1527             return False
   1528 
   1529     def isHandleOptional(self, param, params):
   1530 
   1531         # See if the handle is optional
   1532         isOptional = False
   1533 
   1534         # Simple, if it's optional, return true
   1535         if param.attrib.get('optional') is not None:
   1536             return True
   1537 
   1538         # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
   1539         if param.attrib.get('noautovalidity') is not None:
   1540             return True
   1541 
   1542         # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
   1543         if self.paramIsArray(param):
   1544             lengths = param.attrib.get('len').split(',')
   1545             for length in lengths:
   1546                 if (length) != 'null-terminated' and (length) != '1':
   1547                     for otherparam in params:
   1548                         if otherparam.find('name').text == length:
   1549                             if otherparam.attrib.get('optional') is not None:
   1550                                 return True
   1551 
   1552         return False
   1553     #
   1554     # Get the category of a type
   1555     def getTypeCategory(self, typename):
   1556         types = self.registry.findall("types/type")
   1557         for elem in types:
   1558             if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
   1559                 return elem.attrib.get('category')
   1560 
   1561     #
   1562     # Make a chunk of text for the end of a parameter if it is an array
   1563     def makeAsciiDocPreChunk(self, param, params):
   1564         paramname = param.find('name')
   1565         paramtype = param.find('type')
   1566 
   1567         # General pre-amble. Check optionality and add stuff.
   1568         asciidoc = '* '
   1569 
   1570         if self.paramIsStaticArray(param):
   1571             asciidoc += 'Any given element of '
   1572 
   1573         elif self.paramIsArray(param):
   1574             lengths = param.attrib.get('len').split(',')
   1575 
   1576             # 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
   1577             optionallengths = []
   1578             for length in lengths:
   1579                 if (length) != 'null-terminated' and (length) != '1':
   1580                     for otherparam in params:
   1581                         if otherparam.find('name').text == length:
   1582                             if otherparam.attrib.get('optional') is not None:
   1583                                 if self.paramIsPointer(otherparam):
   1584                                     optionallengths.append('the value referenced by ' + self.makeParameterName(length))
   1585                                 else:
   1586                                     optionallengths.append(self.makeParameterName(length))
   1587 
   1588             # Document that these arrays may be ignored if any of the length values are 0
   1589             if len(optionallengths) != 0 or param.attrib.get('optional') is not None:
   1590                 asciidoc += 'If '
   1591 
   1592 
   1593                 if len(optionallengths) != 0:
   1594                     if len(optionallengths) == 1:
   1595 
   1596                         asciidoc += optionallengths[0]
   1597                         asciidoc += ' is '
   1598 
   1599                     else:
   1600                         asciidoc += ' or '.join(optionallengths)
   1601                         asciidoc += ' are '
   1602 
   1603                     asciidoc += 'not `0`, '
   1604 
   1605                 if len(optionallengths) != 0 and param.attrib.get('optional') is not None:
   1606                     asciidoc += 'and '
   1607 
   1608                 if param.attrib.get('optional') is not None:
   1609                     asciidoc += self.makeParameterName(paramname.text)
   1610                     asciidoc += ' is not `NULL`, '
   1611 
   1612         elif param.attrib.get('optional') is not None:
   1613             # Don't generate this stub for bitflags
   1614             if self.getTypeCategory(paramtype.text) != 'bitmask':
   1615                 if param.attrib.get('optional').split(',')[0] == 'true':
   1616                     asciidoc += 'If '
   1617                     asciidoc += self.makeParameterName(paramname.text)
   1618                     asciidoc += ' is not '
   1619                     if self.paramIsArray(param) or self.paramIsPointer(param) or self.isHandleTypeDispatchable(paramtype.text):
   1620                         asciidoc += '`NULL`'
   1621                     elif self.getTypeCategory(paramtype.text) == 'handle':
   1622                         asciidoc += 'sname:VK_NULL_HANDLE'
   1623                     else:
   1624                         asciidoc += '`0`'
   1625 
   1626                     asciidoc += ', '
   1627 
   1628         return asciidoc
   1629 
   1630     #
   1631     # Make the generic asciidoc line chunk portion used for all parameters.
   1632     # May return an empty string if nothing to validate.
   1633     def createValidationLineForParameterIntroChunk(self, param, params, typetext):
   1634         asciidoc = ''
   1635         paramname = param.find('name')
   1636         paramtype = param.find('type')
   1637 
   1638         asciidoc += self.makeAsciiDocPreChunk(param, params)
   1639 
   1640         asciidoc += self.makeParameterName(paramname.text)
   1641         asciidoc += ' must: be '
   1642 
   1643         if self.paramIsArray(param):
   1644             # Arrays. These are hard to get right, apparently
   1645 
   1646             lengths = param.attrib.get('len').split(',')
   1647 
   1648             if (lengths[0]) == 'null-terminated':
   1649                 asciidoc += 'a null-terminated '
   1650             elif (lengths[0]) == '1':
   1651                 asciidoc += 'a pointer to '
   1652             else:
   1653                 asciidoc += 'a pointer to an array of '
   1654 
   1655                 # Handle equations, which are currently denoted with latex
   1656                 if 'latexmath:' in lengths[0]:
   1657                     asciidoc += lengths[0]
   1658                 else:
   1659                     asciidoc += self.makeParameterName(lengths[0])
   1660                 asciidoc += ' '
   1661 
   1662             for length in lengths[1:]:
   1663                 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.
   1664                     asciidoc += 'null-terminated '
   1665                 elif (length) == '1':
   1666                     asciidoc += 'pointers to '
   1667                 else:
   1668                     asciidoc += 'pointers to arrays of '
   1669                     # Handle equations, which are currently denoted with latex
   1670                     if 'latex:' in length:
   1671                         asciidoc += length
   1672                     else:
   1673                         asciidoc += self.makeParameterName(length)
   1674                     asciidoc += ' '
   1675 
   1676             # Void pointers don't actually point at anything - remove the word "to"
   1677             if paramtype.text == 'void':
   1678                 if lengths[-1] == '1':
   1679                     if len(lengths) > 1:
   1680                         asciidoc = asciidoc[:-5]    # Take care of the extra s added by the post array chunk function. #HACK#
   1681                     else:
   1682                         asciidoc = asciidoc[:-4]
   1683                 else:
   1684                     # An array of void values is a byte array.
   1685                     asciidoc += 'byte'
   1686 
   1687             elif paramtype.text == 'char':
   1688                 # A null terminated array of chars is a string
   1689                 if lengths[-1] == 'null-terminated':
   1690                     asciidoc += 'string'
   1691                 else:
   1692                     # Else it's just a bunch of chars
   1693                     asciidoc += 'char value'
   1694             elif param.text is not None:
   1695                 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
   1696                 if 'const' in param.text:
   1697                     typecategory = self.getTypeCategory(paramtype.text)
   1698                     if (typecategory != 'struct' and typecategory != 'union' and typecategory != 'basetype' and typecategory is not None) or not self.isStructAlwaysValid(paramtype.text):
   1699                         asciidoc += 'valid '
   1700 
   1701             asciidoc += typetext
   1702 
   1703             # pluralize
   1704             if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'):
   1705                 asciidoc += 's'
   1706 
   1707         elif self.paramIsPointer(param):
   1708             # Handle pointers - which are really special case arrays (i.e. they don't have a length)
   1709             pointercount = paramtype.tail.count('*')
   1710 
   1711             # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that.
   1712             for i in range(0, pointercount):
   1713                 asciidoc += 'a pointer to '
   1714 
   1715             if paramtype.text == 'void':
   1716                 # If there's only one pointer, it's optional, and it doesn't point at anything in particular - we don't need any language.
   1717                 if pointercount == 1 and param.attrib.get('optional') is not None:
   1718                     return '' # early return
   1719                 else:
   1720                     # Pointer to nothing in particular - delete the " to " portion
   1721                     asciidoc = asciidoc[:-4]
   1722             else:
   1723                 # Add an article for English semantic win
   1724                 asciidoc += 'a '
   1725 
   1726             # If a value is "const" that means it won't get modified, so it must be valid going into the function.
   1727             if param.text is not None and paramtype.text != 'void':
   1728                 if 'const' in param.text:
   1729                     asciidoc += 'valid '
   1730 
   1731             asciidoc += typetext
   1732 
   1733         else:
   1734             # Non-pointer, non-optional things must be valid
   1735             asciidoc += 'a valid '
   1736             asciidoc += typetext
   1737 
   1738         if asciidoc != '':
   1739             asciidoc += '\n'
   1740 
   1741             # Add additional line for non-optional bitmasks
   1742             if self.getTypeCategory(paramtype.text) == 'bitmask':
   1743                 if param.attrib.get('optional') is None:
   1744                     asciidoc += '* '
   1745                     if self.paramIsArray(param):
   1746                         asciidoc += 'Each element of '
   1747                     asciidoc += 'pname:'
   1748                     asciidoc += paramname.text
   1749                     asciidoc += ' mustnot: be `0`'
   1750                     asciidoc += '\n'
   1751 
   1752         return asciidoc
   1753 
   1754     def makeAsciiDocLineForParameter(self, param, params, typetext):
   1755         if param.attrib.get('noautovalidity') is not None:
   1756             return ''
   1757         asciidoc  = self.createValidationLineForParameterIntroChunk(param, params, typetext)
   1758 
   1759         return asciidoc
   1760 
   1761     # Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance)
   1762     def isStructAlwaysValid(self, structname):
   1763 
   1764         struct = self.registry.find("types/type[@name='" + structname + "']")
   1765 
   1766         params = struct.findall('member')
   1767         validity = struct.find('validity')
   1768 
   1769         if validity is not None:
   1770             return False
   1771 
   1772         for param in params:
   1773             paramname = param.find('name')
   1774             paramtype = param.find('type')
   1775             typecategory = self.getTypeCategory(paramtype.text)
   1776 
   1777             if paramname.text == 'pNext':
   1778                 return False
   1779 
   1780             if paramname.text == 'sType':
   1781                 return False
   1782 
   1783             if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param):
   1784                 if self.makeAsciiDocLineForParameter(param, params, '') != '':
   1785                     return False
   1786             elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask' or param.attrib.get('returnedonly') == 'true':
   1787                 return False
   1788             elif typecategory == 'struct' or typecategory == 'union':
   1789                 if self.isStructAlwaysValid(paramtype.text) is False:
   1790                     return False
   1791 
   1792         return True
   1793 
   1794     #
   1795     # Make an entire asciidoc line for a given parameter
   1796     def createValidationLineForParameter(self, param, params, typecategory):
   1797         asciidoc = ''
   1798         paramname = param.find('name')
   1799         paramtype = param.find('type')
   1800 
   1801         if paramtype.text == 'void' or paramtype.text == 'char':
   1802             # Chars and void are special cases - needs care inside the generator functions
   1803             # A null-terminated char array is a string, else it's chars.
   1804             # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular
   1805             asciidoc += self.makeAsciiDocLineForParameter(param, params, '')
   1806         elif typecategory == 'bitmask':
   1807             bitsname = paramtype.text.replace('Flags', 'FlagBits')
   1808             if self.registry.find("enums[@name='" + bitsname + "']") is None:
   1809                 asciidoc += '* '
   1810                 asciidoc += self.makeParameterName(paramname.text)
   1811                 asciidoc += ' must: be `0`'
   1812                 asciidoc += '\n'
   1813             else:
   1814                 if self.paramIsArray(param):
   1815                     asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value')
   1816                 else:
   1817                     asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values')
   1818         elif typecategory == 'handle':
   1819             asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' handle')
   1820         elif typecategory == 'enum':
   1821             asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeEnumerationName(paramtype.text) + ' value')
   1822         elif typecategory == 'struct':
   1823             if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
   1824                 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' structure')
   1825         elif typecategory == 'union':
   1826             if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
   1827                 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' union')
   1828         elif self.paramIsArray(param) or self.paramIsPointer(param):
   1829             asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeBaseTypeName(paramtype.text) + ' value')
   1830 
   1831         return asciidoc
   1832 
   1833     #
   1834     # Make an asciidoc validity entry for a handle's parent object
   1835     def makeAsciiDocHandleParent(self, param, params):
   1836         asciidoc = ''
   1837         paramname = param.find('name')
   1838         paramtype = param.find('type')
   1839 
   1840         # Deal with handle parents
   1841         handleparent = self.getHandleParent(paramtype.text)
   1842         if handleparent is not None:
   1843             parentreference = None
   1844             for otherparam in params:
   1845                 if otherparam.find('type').text == handleparent:
   1846                     parentreference = otherparam.find('name').text
   1847             if parentreference is not None:
   1848                 asciidoc += '* '
   1849 
   1850                 if self.isHandleOptional(param, params):
   1851                     if self.paramIsArray(param):
   1852                         asciidoc += 'Each element of '
   1853                         asciidoc += self.makeParameterName(paramname.text)
   1854                         asciidoc += ' that is a valid handle'
   1855                     else:
   1856                         asciidoc += 'If '
   1857                         asciidoc += self.makeParameterName(paramname.text)
   1858                         asciidoc += ' is a valid handle, it'
   1859                 else:
   1860                     if self.paramIsArray(param):
   1861                         asciidoc += 'Each element of '
   1862                     asciidoc += self.makeParameterName(paramname.text)
   1863                 asciidoc += ' must: have been created, allocated or retrieved from '
   1864                 asciidoc += self.makeParameterName(parentreference)
   1865 
   1866                 asciidoc += '\n'
   1867         return asciidoc
   1868 
   1869     #
   1870     # Generate an asciidoc validity line for the sType value of a struct
   1871     def makeStructureType(self, blockname, param):
   1872         asciidoc = '* '
   1873         paramname = param.find('name')
   1874         paramtype = param.find('type')
   1875 
   1876         asciidoc += self.makeParameterName(paramname.text)
   1877         asciidoc += ' must: be '
   1878 
   1879         structuretype = ''
   1880         for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname):
   1881             if elem[0] == 'Vk':
   1882                 structuretype += 'VK_STRUCTURE_TYPE_'
   1883             else:
   1884                 structuretype += elem[0].upper()
   1885                 structuretype += '_'
   1886 
   1887         asciidoc += self.makeEnumerantName(structuretype[:-1])
   1888         asciidoc += '\n'
   1889 
   1890         return asciidoc
   1891 
   1892     #
   1893     # Generate an asciidoc validity line for the pNext value of a struct
   1894     def makeStructureExtensionPointer(self, param):
   1895         asciidoc = '* '
   1896         paramname = param.find('name')
   1897         paramtype = param.find('type')
   1898 
   1899         asciidoc += self.makeParameterName(paramname.text)
   1900 
   1901         validextensionstructs = param.attrib.get('validextensionstructs')
   1902         asciidoc += ' must: be `NULL`'
   1903         if validextensionstructs is not None:
   1904             extensionstructs = ['slink:' + x for x in validextensionstructs.split(',')]
   1905             asciidoc += ', or a pointer to a valid instance of '
   1906             if len(extensionstructs) == 1:
   1907                 asciidoc += validextensionstructs
   1908             else:
   1909                 asciidoc += (', ').join(extensionstructs[:-1]) + ' or ' + extensionstructs[-1]
   1910 
   1911         asciidoc += '\n'
   1912 
   1913         return asciidoc
   1914 
   1915     #
   1916     # Generate all the valid usage information for a given struct or command
   1917     def makeValidUsageStatements(self, cmd, blockname, params, usages):
   1918         # Start the asciidoc block for this
   1919         asciidoc = ''
   1920 
   1921         handles = []
   1922         anyparentedhandlesoptional = False
   1923         parentdictionary = {}
   1924         arraylengths = set()
   1925         for param in params:
   1926             paramname = param.find('name')
   1927             paramtype = param.find('type')
   1928 
   1929             # Get the type's category
   1930             typecategory = self.getTypeCategory(paramtype.text)
   1931 
   1932             # Generate language to independently validate a parameter
   1933             if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
   1934                 asciidoc += self.makeStructureType(blockname, param)
   1935             elif paramtype.text == 'void' and paramname.text == 'pNext':
   1936                 asciidoc += self.makeStructureExtensionPointer(param)
   1937             else:
   1938                 asciidoc += self.createValidationLineForParameter(param, params, typecategory)
   1939 
   1940             # Ensure that any parenting is properly validated, and list that a handle was found
   1941             if typecategory == 'handle':
   1942                 # Don't detect a parent for return values!
   1943                 if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
   1944                     parent = self.getHandleParent(paramtype.text)
   1945                     if parent is not None:
   1946                         handles.append(param)
   1947 
   1948                         # If any param is optional, it affects the output
   1949                         if self.isHandleOptional(param, params):
   1950                             anyparentedhandlesoptional = True
   1951 
   1952                         # Find the first dispatchable parent
   1953                         ancestor = parent
   1954                         while ancestor is not None and not self.isHandleTypeDispatchable(ancestor):
   1955                             ancestor = self.getHandleParent(ancestor)
   1956 
   1957                         # If one was found, add this parameter to the parent dictionary
   1958                         if ancestor is not None:
   1959                             if ancestor not in parentdictionary:
   1960                                 parentdictionary[ancestor] = []
   1961 
   1962                             if self.paramIsArray(param):
   1963                                 parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text))
   1964                             else:
   1965                                 parentdictionary[ancestor].append(self.makeParameterName(paramname.text))
   1966 
   1967             # Get the array length for this parameter
   1968             arraylength = param.attrib.get('len')
   1969             if arraylength is not None:
   1970                 for onelength in arraylength.split(','):
   1971                     arraylengths.add(onelength)
   1972 
   1973         # For any vkQueue* functions, there might be queue type data
   1974         if 'vkQueue' in blockname:
   1975             # The queue type must be valid
   1976             queuetypes = cmd.attrib.get('queues')
   1977             if queuetypes is not None:
   1978                 queuebits = []
   1979                 for queuetype in re.findall(r'([^,]+)', queuetypes):
   1980                     queuebits.append(queuetype.replace('_',' '))
   1981 
   1982                 asciidoc += '* '
   1983                 asciidoc += 'The pname:queue must: support '
   1984                 if len(queuebits) == 1:
   1985                     asciidoc += queuebits[0]
   1986                 else:
   1987                     asciidoc += (', ').join(queuebits[:-1])
   1988                     asciidoc += ' or '
   1989                     asciidoc += queuebits[-1]
   1990                 asciidoc += ' operations'
   1991                 asciidoc += '\n'
   1992 
   1993         if 'vkCmd' in blockname:
   1994             # The commandBuffer parameter must be being recorded
   1995             asciidoc += '* '
   1996             asciidoc += 'pname:commandBuffer must: be in the recording state'
   1997             asciidoc += '\n'
   1998 
   1999             # The queue type must be valid
   2000             queuetypes = cmd.attrib.get('queues')
   2001             queuebits = []
   2002             for queuetype in re.findall(r'([^,]+)', queuetypes):
   2003                 queuebits.append(queuetype.replace('_',' '))
   2004 
   2005             asciidoc += '* '
   2006             asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
   2007             if len(queuebits) == 1:
   2008                 asciidoc += queuebits[0]
   2009             else:
   2010                 asciidoc += (', ').join(queuebits[:-1])
   2011                 asciidoc += ' or '
   2012                 asciidoc += queuebits[-1]
   2013             asciidoc += ' operations'
   2014             asciidoc += '\n'
   2015 
   2016             # Must be called inside/outside a renderpass appropriately
   2017             renderpass = cmd.attrib.get('renderpass')
   2018 
   2019             if renderpass != 'both':
   2020                 asciidoc += '* This command must: only be called '
   2021                 asciidoc += renderpass
   2022                 asciidoc += ' of a render pass instance'
   2023                 asciidoc += '\n'
   2024 
   2025             # Must be in the right level command buffer
   2026             cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
   2027 
   2028             if cmdbufferlevel != 'primary,secondary':
   2029                 asciidoc += '* pname:commandBuffer must: be a '
   2030                 asciidoc += cmdbufferlevel
   2031                 asciidoc += ' sname:VkCommandBuffer'
   2032                 asciidoc += '\n'
   2033 
   2034         # Any non-optional arraylengths should specify they must be greater than 0
   2035         for param in params:
   2036             paramname = param.find('name')
   2037 
   2038             for arraylength in arraylengths:
   2039                 if paramname.text == arraylength and param.attrib.get('optional') is None:
   2040                     # Get all the array dependencies
   2041                     arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
   2042 
   2043                     # Get all the optional array dependencies, including those not generating validity for some reason
   2044                     optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
   2045                     optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']"))
   2046 
   2047                     asciidoc += '* '
   2048 
   2049                     # Allow lengths to be arbitrary if all their dependents are optional
   2050                     if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
   2051                         asciidoc += 'If '
   2052                         if len(optionalarrays) > 1:
   2053                             asciidoc += 'any of '
   2054 
   2055                         for array in optionalarrays[:-1]:
   2056                             asciidoc += self.makeParameterName(optionalarrays.find('name').text)
   2057                             asciidoc += ', '
   2058 
   2059                         if len(optionalarrays) > 1:
   2060                             asciidoc += 'and '
   2061                             asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
   2062                             asciidoc += ' are '
   2063                         else:
   2064                             asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
   2065                             asciidoc += ' is '
   2066 
   2067                         asciidoc += 'not `NULL`, '
   2068 
   2069                         if self.paramIsPointer(param):
   2070                             asciidoc += 'the value referenced by '
   2071 
   2072                     elif self.paramIsPointer(param):
   2073                         asciidoc += 'The value referenced by '
   2074 
   2075                     asciidoc += self.makeParameterName(arraylength)
   2076                     asciidoc += ' must: be greater than `0`'
   2077                     asciidoc += '\n'
   2078 
   2079         # Find the parents of all objects referenced in this command
   2080         for param in handles:
   2081             asciidoc += self.makeAsciiDocHandleParent(param, params)
   2082 
   2083         # Find the common ancestors of objects
   2084         noancestorscount = 0
   2085         while noancestorscount < len(parentdictionary):
   2086             noancestorscount = 0
   2087             oldparentdictionary = parentdictionary.copy()
   2088             for parent in oldparentdictionary.items():
   2089                 ancestor = self.getHandleParent(parent[0])
   2090 
   2091                 while ancestor is not None and ancestor not in parentdictionary:
   2092                     ancestor = self.getHandleParent(ancestor)
   2093 
   2094                 if ancestor is not None:
   2095                     parentdictionary[ancestor] += parentdictionary.pop(parent[0])
   2096                 else:
   2097                     # No ancestors possible - so count it up
   2098                     noancestorscount += 1
   2099 
   2100         # Add validation language about common ancestors
   2101         for parent in parentdictionary.items():
   2102             if len(parent[1]) > 1:
   2103                 parentlanguage = '* '
   2104 
   2105                 parentlanguage += 'Each of '
   2106                 parentlanguage += ", ".join(parent[1][:-1])
   2107                 parentlanguage += ' and '
   2108                 parentlanguage += parent[1][-1]
   2109                 if anyparentedhandlesoptional is True:
   2110                     parentlanguage += ' that are valid handles'
   2111                 parentlanguage += ' must: have been created, allocated or retrieved from the same '
   2112                 parentlanguage += self.makeStructName(parent[0])
   2113                 parentlanguage += '\n'
   2114 
   2115                 # Capitalize and add to the main language
   2116                 asciidoc += parentlanguage
   2117 
   2118         # Add in any plain-text validation language that should be added
   2119         for usage in usages:
   2120             asciidoc += '* '
   2121             asciidoc += usage
   2122             asciidoc += '\n'
   2123 
   2124         # In case there's nothing to report, return None
   2125         if asciidoc == '':
   2126             return None
   2127         # Delimit the asciidoc block
   2128         return asciidoc
   2129 
   2130     def makeThreadSafetyBlock(self, cmd, paramtext):
   2131         """Generate C function pointer typedef for <command> Element"""
   2132         paramdecl = ''
   2133 
   2134         # For any vkCmd* functions, the commandBuffer parameter must be being recorded
   2135         if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name'):
   2136             paramdecl += '* '
   2137             paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from'
   2138             paramdecl += '\n'
   2139 
   2140         # Find and add any parameters that are thread unsafe
   2141         explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
   2142         if (explicitexternsyncparams is not None):
   2143             for param in explicitexternsyncparams:
   2144                 externsyncattribs = param.attrib.get('externsync')
   2145                 paramname = param.find('name')
   2146                 for externsyncattrib in externsyncattribs.split(','):
   2147                     paramdecl += '* '
   2148                     paramdecl += 'Host access to '
   2149                     if externsyncattrib == 'true':
   2150                         if self.paramIsArray(param):
   2151                             paramdecl += 'each member of ' + self.makeParameterName(paramname.text)
   2152                         elif self.paramIsPointer(param):
   2153                             paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text)
   2154                         else:
   2155                             paramdecl += self.makeParameterName(paramname.text)
   2156                     else:
   2157                         paramdecl += 'pname:'
   2158                         paramdecl += externsyncattrib
   2159                     paramdecl += ' must: be externally synchronized\n'
   2160 
   2161         # Find and add any "implicit" parameters that are thread unsafe
   2162         implicitexternsyncparams = cmd.find('implicitexternsyncparams')
   2163         if (implicitexternsyncparams is not None):
   2164             for elem in implicitexternsyncparams:
   2165                 paramdecl += '* '
   2166                 paramdecl += 'Host access to '
   2167                 paramdecl += elem.text
   2168                 paramdecl += ' must: be externally synchronized\n'
   2169 
   2170         if (paramdecl == ''):
   2171             return None
   2172         else:
   2173             return paramdecl
   2174 
   2175     def makeCommandPropertiesTableEntry(self, cmd, name):
   2176 
   2177         if 'vkCmd' in name:
   2178             # Must be called inside/outside a renderpass appropriately
   2179             cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
   2180             cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
   2181 
   2182             renderpass = cmd.attrib.get('renderpass')
   2183             renderpass = renderpass.capitalize()
   2184 
   2185             queues = cmd.attrib.get('queues')
   2186             queues = (' + \n').join(queues.upper().split(','))
   2187 
   2188             return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues
   2189         elif 'vkQueue' in name:
   2190             # Must be called inside/outside a renderpass appropriately
   2191 
   2192             queues = cmd.attrib.get('queues')
   2193             if queues is None:
   2194                 queues = 'Any'
   2195             else:
   2196                 queues = (' + \n').join(queues.upper().split(','))
   2197 
   2198             return '|-|-|' + queues
   2199 
   2200         return None
   2201 
   2202     def makeSuccessCodes(self, cmd, name):
   2203 
   2204         successcodes = cmd.attrib.get('successcodes')
   2205         if successcodes is not None:
   2206 
   2207             successcodeentry = ''
   2208             successcodes = successcodes.split(',')
   2209             return '* ename:' + '\n* ename:'.join(successcodes)
   2210 
   2211         return None
   2212 
   2213     def makeErrorCodes(self, cmd, name):
   2214 
   2215         errorcodes = cmd.attrib.get('errorcodes')
   2216         if errorcodes is not None:
   2217 
   2218             errorcodeentry = ''
   2219             errorcodes = errorcodes.split(',')
   2220             return '* ename:' + '\n* ename:'.join(errorcodes)
   2221 
   2222         return None
   2223 
   2224     #
   2225     # Command generation
   2226     def genCmd(self, cmdinfo, name):
   2227         OutputGenerator.genCmd(self, cmdinfo, name)
   2228         #
   2229         # Get all the parameters
   2230         params = cmdinfo.elem.findall('param')
   2231         usageelements = cmdinfo.elem.findall('validity/usage')
   2232         usages = []
   2233 
   2234         for usage in usageelements:
   2235             usages.append(usage.text)
   2236         for usage in cmdinfo.additionalValidity:
   2237             usages.append(usage.text)
   2238         for usage in cmdinfo.removedValidity:
   2239             usages.remove(usage.text)
   2240 
   2241         validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages)
   2242         threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
   2243         commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name)
   2244         successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
   2245         errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
   2246 
   2247         self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
   2248 
   2249     #
   2250     # Struct Generation
   2251     def genStruct(self, typeinfo, typename):
   2252         OutputGenerator.genStruct(self, typeinfo, typename)
   2253 
   2254         # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
   2255         if typeinfo.elem.attrib.get('returnedonly') is None:
   2256             params = typeinfo.elem.findall('member')
   2257 
   2258             usageelements = typeinfo.elem.findall('validity/usage')
   2259             usages = []
   2260 
   2261             for usage in usageelements:
   2262                 usages.append(usage.text)
   2263             for usage in typeinfo.additionalValidity:
   2264                 usages.append(usage.text)
   2265             for usage in typeinfo.removedValidity:
   2266                 usages.remove(usage.text)
   2267 
   2268             validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
   2269             threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
   2270 
   2271             self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None)
   2272         else:
   2273             # Still generate files for return only structs, in case this state changes later
   2274             self.writeInclude('validity/structs', typename, None, None, None, None, None)
   2275 
   2276     #
   2277     # Type Generation
   2278     def genType(self, typeinfo, typename):
   2279         OutputGenerator.genType(self, typeinfo, typename)
   2280 
   2281         category = typeinfo.elem.get('category')
   2282         if (category == 'struct' or category == 'union'):
   2283             self.genStruct(typeinfo, typename)
   2284 
   2285 # HostSynchronizationOutputGenerator - subclass of OutputGenerator.
   2286 # Generates AsciiDoc includes of the externsync parameter table for the
   2287 # fundamentals chapter of the Vulkan specification. Similar to
   2288 # DocOutputGenerator.
   2289 #
   2290 # ---- methods ----
   2291 # HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for
   2292 #   OutputGenerator. Defines additional internal state.
   2293 # ---- methods overriding base class ----
   2294 # genCmd(cmdinfo)
   2295 class HostSynchronizationOutputGenerator(OutputGenerator):
   2296     # Generate Host Synchronized Parameters in a table at the top of the spec
   2297     def __init__(self,
   2298                  errFile = sys.stderr,
   2299                  warnFile = sys.stderr,
   2300                  diagFile = sys.stdout):
   2301         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
   2302 
   2303     threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''}
   2304 
   2305     def makeParameterName(self, name):
   2306         return 'pname:' + name
   2307 
   2308     def makeFLink(self, name):
   2309         return 'flink:' + name
   2310 
   2311     #
   2312     # Generate an include file
   2313     #
   2314     # directory - subdirectory to put file in
   2315     # basename - base name of the file
   2316     # contents - contents of the file (Asciidoc boilerplate aside)
   2317     def writeInclude(self):
   2318 
   2319         if self.threadsafety['parameters'] is not None:
   2320             # Create file
   2321             filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt'
   2322             self.logMsg('diag', '# Generating include file:', filename)
   2323             fp = open(filename, 'w')
   2324 
   2325             # Host Synchronization
   2326             write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
   2327             write('.Externally Synchronized Parameters', file=fp)
   2328             write('*' * 80, file=fp)
   2329             write(self.threadsafety['parameters'], file=fp, end='')
   2330             write('*' * 80, file=fp)
   2331             write('', file=fp)
   2332 
   2333         if self.threadsafety['parameterlists'] is not None:
   2334             # Create file
   2335             filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt'
   2336             self.logMsg('diag', '# Generating include file:', filename)
   2337             fp = open(filename, 'w')
   2338 
   2339             # Host Synchronization
   2340             write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
   2341             write('.Externally Synchronized Parameter Lists', file=fp)
   2342             write('*' * 80, file=fp)
   2343             write(self.threadsafety['parameterlists'], file=fp, end='')
   2344             write('*' * 80, file=fp)
   2345             write('', file=fp)
   2346 
   2347         if self.threadsafety['implicit'] is not None:
   2348             # Create file
   2349             filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt'
   2350             self.logMsg('diag', '# Generating include file:', filename)
   2351             fp = open(filename, 'w')
   2352 
   2353             # Host Synchronization
   2354             write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
   2355             write('.Implicit Externally Synchronized Parameters', file=fp)
   2356             write('*' * 80, file=fp)
   2357             write(self.threadsafety['implicit'], file=fp, end='')
   2358             write('*' * 80, file=fp)
   2359             write('', file=fp)
   2360 
   2361         fp.close()
   2362 
   2363     #
   2364     # Check if the parameter passed in is a pointer to an array
   2365     def paramIsArray(self, param):
   2366         return param.attrib.get('len') is not None
   2367 
   2368     # Check if the parameter passed in is a pointer
   2369     def paramIsPointer(self, param):
   2370         ispointer = False
   2371         paramtype = param.find('type')
   2372         if paramtype.tail is not None and '*' in paramtype.tail:
   2373             ispointer = True
   2374 
   2375         return ispointer
   2376 
   2377     # Turn the "name[].member[]" notation into plain English.
   2378     def makeThreadDereferenceHumanReadable(self, dereference):
   2379         matches = re.findall(r"[\w]+[^\w]*",dereference)
   2380         stringval = ''
   2381         for match in reversed(matches):
   2382             if '->' in match or '.' in match:
   2383                 stringval += 'member of '
   2384             if '[]' in match:
   2385                 stringval += 'each element of '
   2386 
   2387             stringval += 'the '
   2388             stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0])
   2389             stringval += ' '
   2390 
   2391         stringval += 'parameter'
   2392 
   2393         return stringval[0].upper() + stringval[1:]
   2394 
   2395     def makeThreadSafetyBlocks(self, cmd, paramtext):
   2396         protoname = cmd.find('proto/name').text
   2397 
   2398         # Find and add any parameters that are thread unsafe
   2399         explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
   2400         if (explicitexternsyncparams is not None):
   2401             for param in explicitexternsyncparams:
   2402                 externsyncattribs = param.attrib.get('externsync')
   2403                 paramname = param.find('name')
   2404                 for externsyncattrib in externsyncattribs.split(','):
   2405 
   2406                     tempstring = '* '
   2407                     if externsyncattrib == 'true':
   2408                         if self.paramIsArray(param):
   2409                             tempstring += 'Each element of the '
   2410                         elif self.paramIsPointer(param):
   2411                             tempstring += 'The object referenced by the '
   2412                         else:
   2413                             tempstring += 'The '
   2414 
   2415                         tempstring += self.makeParameterName(paramname.text)
   2416                         tempstring += ' parameter'
   2417 
   2418                     else:
   2419                         tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib)
   2420 
   2421                     tempstring += ' in '
   2422                     tempstring += self.makeFLink(protoname)
   2423                     tempstring += '\n'
   2424 
   2425 
   2426                     if ' element of ' in tempstring:
   2427                         self.threadsafety['parameterlists'] += tempstring
   2428                     else:
   2429                         self.threadsafety['parameters'] += tempstring
   2430 
   2431 
   2432         # Find and add any "implicit" parameters that are thread unsafe
   2433         implicitexternsyncparams = cmd.find('implicitexternsyncparams')
   2434         if (implicitexternsyncparams is not None):
   2435             for elem in implicitexternsyncparams:
   2436                 self.threadsafety['implicit'] += '* '
   2437                 self.threadsafety['implicit'] += elem.text[0].upper()
   2438                 self.threadsafety['implicit'] += elem.text[1:]
   2439                 self.threadsafety['implicit'] += ' in '
   2440                 self.threadsafety['implicit'] += self.makeFLink(protoname)
   2441                 self.threadsafety['implicit'] += '\n'
   2442 
   2443 
   2444         # For any vkCmd* functions, the commandBuffer parameter must be being recorded
   2445         if protoname is not None and 'vkCmd' in protoname:
   2446             self.threadsafety['implicit'] += '* '
   2447             self.threadsafety['implicit'] += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in '
   2448             self.threadsafety['implicit'] += self.makeFLink(protoname)
   2449 
   2450             self.threadsafety['implicit'] += '\n'
   2451 
   2452     #
   2453     # Command generation
   2454     def genCmd(self, cmdinfo, name):
   2455         OutputGenerator.genCmd(self, cmdinfo, name)
   2456         #
   2457         # Get all thh parameters
   2458         params = cmdinfo.elem.findall('param')
   2459         usages = cmdinfo.elem.findall('validity/usage')
   2460 
   2461         self.makeThreadSafetyBlocks(cmdinfo.elem, 'param')
   2462 
   2463         self.writeInclude()
   2464 
   2465 # ThreadOutputGenerator - subclass of OutputGenerator.
   2466 # Generates Thread checking framework
   2467 #
   2468 # ---- methods ----
   2469 # ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
   2470 #   OutputGenerator. Defines additional internal state.
   2471 # ---- methods overriding base class ----
   2472 # beginFile(genOpts)
   2473 # endFile()
   2474 # beginFeature(interface, emit)
   2475 # endFeature()
   2476 # genType(typeinfo,name)
   2477 # genStruct(typeinfo,name)
   2478 # genGroup(groupinfo,name)
   2479 # genEnum(enuminfo, name)
   2480 # genCmd(cmdinfo)
   2481 class ThreadOutputGenerator(OutputGenerator):
   2482     """Generate specified API interfaces in a specific style, such as a C header"""
   2483     # This is an ordered list of sections in the header file.
   2484     TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
   2485                      'group', 'bitmask', 'funcpointer', 'struct']
   2486     ALL_SECTIONS = TYPE_SECTIONS + ['command']
   2487     def __init__(self,
   2488                  errFile = sys.stderr,
   2489                  warnFile = sys.stderr,
   2490                  diagFile = sys.stdout):
   2491         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
   2492         # Internal state - accumulators for different inner block text
   2493         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
   2494         self.intercepts = []
   2495 
   2496     # Check if the parameter passed in is a pointer to an array
   2497     def paramIsArray(self, param):
   2498         return param.attrib.get('len') is not None
   2499 
   2500     # Check if the parameter passed in is a pointer
   2501     def paramIsPointer(self, param):
   2502         ispointer = False
   2503         for elem in param:
   2504             #write('paramIsPointer '+elem.text, file=sys.stderr)
   2505             #write('elem.tag '+elem.tag, file=sys.stderr)
   2506             #if (elem.tail is None):
   2507             #    write('elem.tail is None', file=sys.stderr)
   2508             #else:
   2509             #    write('elem.tail '+elem.tail, file=sys.stderr)
   2510             if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
   2511                 ispointer = True
   2512             #    write('is pointer', file=sys.stderr)
   2513         return ispointer
   2514     def makeThreadUseBlock(self, cmd, functionprefix):
   2515         """Generate C function pointer typedef for <command> Element"""
   2516         paramdecl = ''
   2517         thread_check_dispatchable_objects = [
   2518             "VkCommandBuffer",
   2519             "VkDevice",
   2520             "VkInstance",
   2521             "VkQueue",
   2522         ]
   2523         thread_check_nondispatchable_objects = [
   2524             "VkBuffer",
   2525             "VkBufferView",
   2526             "VkCommandPool",
   2527             "VkDescriptorPool",
   2528             "VkDescriptorSetLayout",
   2529             "VkDeviceMemory",
   2530             "VkEvent",
   2531             "VkFence",
   2532             "VkFramebuffer",
   2533             "VkImage",
   2534             "VkImageView",
   2535             "VkPipeline",
   2536             "VkPipelineCache",
   2537             "VkPipelineLayout",
   2538             "VkQueryPool",
   2539             "VkRenderPass",
   2540             "VkSampler",
   2541             "VkSemaphore",
   2542             "VkShaderModule",
   2543         ]
   2544 
   2545         # Find and add any parameters that are thread unsafe
   2546         params = cmd.findall('param')
   2547         for param in params:
   2548             paramname = param.find('name')
   2549             if False: # self.paramIsPointer(param):
   2550                 paramdecl += '    // not watching use of pointer ' + paramname.text + '\n'
   2551             else:
   2552                 externsync = param.attrib.get('externsync')
   2553                 if externsync == 'true':
   2554                     if self.paramIsArray(param):
   2555                         paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
   2556                         paramdecl += '        ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + '[index]);\n'
   2557                         paramdecl += '    }\n'
   2558                     else:
   2559                         paramdecl += '    ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + ');\n'
   2560                 elif (param.attrib.get('externsync')):
   2561                     if self.paramIsArray(param):
   2562                         # Externsync can list pointers to arrays of members to synchronize
   2563                         paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
   2564                         for member in externsync.split(","):
   2565                             # Replace first empty [] in member name with index
   2566                             element = member.replace('[]','[index]',1)
   2567                             if '[]' in element:
   2568                                 # Replace any second empty [] in element name with
   2569                                 # inner array index based on mapping array names like
   2570                                 # "pSomeThings[]" to "someThingCount" array size.
   2571                                 # This could be more robust by mapping a param member
   2572                                 # name to a struct type and "len" attribute.
   2573                                 limit = element[0:element.find('s[]')] + 'Count'
   2574                                 dotp = limit.rfind('.p')
   2575                                 limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:]
   2576                                 paramdecl += '        for(uint32_t index2=0;index2<'+limit+';index2++)'
   2577                                 element = element.replace('[]','[index2]')
   2578                             paramdecl += '        ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n'
   2579                         paramdecl += '    }\n'
   2580                     else:
   2581                         # externsync can list members to synchronize
   2582                         for member in externsync.split(","):
   2583                             paramdecl += '    ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
   2584                 else:
   2585                     paramtype = param.find('type')
   2586                     if paramtype is not None:
   2587                         paramtype = paramtype.text
   2588                     else:
   2589                         paramtype = 'None'
   2590                     if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
   2591                         if self.paramIsArray(param) and ('pPipelines' != paramname.text):
   2592                             paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
   2593                             paramdecl += '        ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
   2594                             paramdecl += '    }\n'
   2595                         elif not self.paramIsPointer(param):
   2596                             # Pointer params are often being created.
   2597                             # They are not being read from.
   2598                             paramdecl += '    ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
   2599         explicitexternsyncparams = cmd.findall("param[@externsync]")
   2600         if (explicitexternsyncparams is not None):
   2601             for param in explicitexternsyncparams:
   2602                 externsyncattrib = param.attrib.get('externsync')
   2603                 paramname = param.find('name')
   2604                 paramdecl += '// Host access to '
   2605                 if externsyncattrib == 'true':
   2606                     if self.paramIsArray(param):
   2607                         paramdecl += 'each member of ' + paramname.text
   2608                     elif self.paramIsPointer(param):
   2609                         paramdecl += 'the object referenced by ' + paramname.text
   2610                     else:
   2611                         paramdecl += paramname.text
   2612                 else:
   2613                     paramdecl += externsyncattrib
   2614                 paramdecl += ' must be externally synchronized\n'
   2615 
   2616         # Find and add any "implicit" parameters that are thread unsafe
   2617         implicitexternsyncparams = cmd.find('implicitexternsyncparams')
   2618         if (implicitexternsyncparams is not None):
   2619             for elem in implicitexternsyncparams:
   2620                 paramdecl += '    // '
   2621                 paramdecl += elem.text
   2622                 paramdecl += ' must be externally synchronized between host accesses\n'
   2623 
   2624         if (paramdecl == ''):
   2625             return None
   2626         else:
   2627             return paramdecl
   2628     def beginFile(self, genOpts):
   2629         OutputGenerator.beginFile(self, genOpts)
   2630         # C-specific
   2631         #
   2632         # Multiple inclusion protection & C++ namespace.
   2633         if (genOpts.protectFile and self.genOpts.filename):
   2634             headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
   2635             write('#ifndef', headerSym, file=self.outFile)
   2636             write('#define', headerSym, '1', file=self.outFile)
   2637             self.newline()
   2638         write('namespace threading {', file=self.outFile)
   2639         self.newline()
   2640         #
   2641         # User-supplied prefix text, if any (list of strings)
   2642         if (genOpts.prefixText):
   2643             for s in genOpts.prefixText:
   2644                 write(s, file=self.outFile)
   2645     def endFile(self):
   2646         # C-specific
   2647         # Finish C++ namespace and multiple inclusion protection
   2648         self.newline()
   2649         # record intercepted procedures
   2650         write('// intercepts', file=self.outFile)
   2651         write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
   2652         write('\n'.join(self.intercepts), file=self.outFile)
   2653         write('};\n', file=self.outFile)
   2654         self.newline()
   2655         write('} // namespace threading', file=self.outFile)
   2656         if (self.genOpts.protectFile and self.genOpts.filename):
   2657             self.newline()
   2658             write('#endif', file=self.outFile)
   2659         # Finish processing in superclass
   2660         OutputGenerator.endFile(self)
   2661     def beginFeature(self, interface, emit):
   2662         #write('// starting beginFeature', file=self.outFile)
   2663         # Start processing in superclass
   2664         OutputGenerator.beginFeature(self, interface, emit)
   2665         # C-specific
   2666         # Accumulate includes, defines, types, enums, function pointer typedefs,
   2667         # end function prototypes separately for this feature. They're only
   2668         # printed in endFeature().
   2669         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
   2670         #write('// ending beginFeature', file=self.outFile)
   2671     def endFeature(self):
   2672         # C-specific
   2673         # Actually write the interface to the output file.
   2674         #write('// starting endFeature', file=self.outFile)
   2675         if (self.emit):
   2676             self.newline()
   2677             if (self.genOpts.protectFeature):
   2678                 write('#ifndef', self.featureName, file=self.outFile)
   2679             # If type declarations are needed by other features based on
   2680             # this one, it may be necessary to suppress the ExtraProtect,
   2681             # or move it below the 'for section...' loop.
   2682             #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
   2683             if (self.featureExtraProtect != None):
   2684                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
   2685             #write('#define', self.featureName, '1', file=self.outFile)
   2686             for section in self.TYPE_SECTIONS:
   2687                 #write('// endFeature writing section'+section, file=self.outFile)
   2688                 contents = self.sections[section]
   2689                 if contents:
   2690                     write('\n'.join(contents), file=self.outFile)
   2691                     self.newline()
   2692             #write('// endFeature looking at self.sections[command]', file=self.outFile)
   2693             if (self.sections['command']):
   2694                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
   2695                 self.newline()
   2696             if (self.featureExtraProtect != None):
   2697                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
   2698             if (self.genOpts.protectFeature):
   2699                 write('#endif /*', self.featureName, '*/', file=self.outFile)
   2700         # Finish processing in superclass
   2701         OutputGenerator.endFeature(self)
   2702         #write('// ending endFeature', file=self.outFile)
   2703     #
   2704     # Append a definition to the specified section
   2705     def appendSection(self, section, text):
   2706         # self.sections[section].append('SECTION: ' + section + '\n')
   2707         self.sections[section].append(text)
   2708     #
   2709     # Type generation
   2710     def genType(self, typeinfo, name):
   2711         pass
   2712     #
   2713     # Struct (e.g. C "struct" type) generation.
   2714     # This is a special case of the <type> tag where the contents are
   2715     # interpreted as a set of <member> tags instead of freeform C
   2716     # C type declarations. The <member> tags are just like <param>
   2717     # tags - they are a declaration of a struct or union member.
   2718     # Only simple member declarations are supported (no nested
   2719     # structs etc.)
   2720     def genStruct(self, typeinfo, typeName):
   2721         OutputGenerator.genStruct(self, typeinfo, typeName)
   2722         body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
   2723         # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
   2724         for member in typeinfo.elem.findall('.//member'):
   2725             body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
   2726             body += ';\n'
   2727         body += '} ' + typeName + ';\n'
   2728         self.appendSection('struct', body)
   2729     #
   2730     # Group (e.g. C "enum" type) generation.
   2731     # These are concatenated together with other types.
   2732     def genGroup(self, groupinfo, groupName):
   2733         pass
   2734     # Enumerant generation
   2735     # <enum> tags may specify their values in several ways, but are usually
   2736     # just integers.
   2737     def genEnum(self, enuminfo, name):
   2738         pass
   2739     #
   2740     # Command generation
   2741     def genCmd(self, cmdinfo, name):
   2742         # Commands shadowed by interface functions and are not implemented
   2743         interface_functions = [
   2744             'vkEnumerateInstanceLayerProperties',
   2745             'vkEnumerateInstanceExtensionProperties',
   2746             'vkEnumerateDeviceLayerProperties',
   2747         ]
   2748         if name in interface_functions:
   2749             return
   2750         special_functions = [
   2751             'vkGetDeviceProcAddr',
   2752             'vkGetInstanceProcAddr',
   2753             'vkCreateDevice',
   2754             'vkDestroyDevice',
   2755             'vkCreateInstance',
   2756             'vkDestroyInstance',
   2757             'vkAllocateCommandBuffers',
   2758             'vkFreeCommandBuffers',
   2759             'vkCreateDebugReportCallbackEXT',
   2760             'vkDestroyDebugReportCallbackEXT',
   2761         ]
   2762         if name in special_functions:
   2763             decls = self.makeCDecls(cmdinfo.elem)
   2764             self.appendSection('command', '')
   2765             self.appendSection('command', '// declare only')
   2766             self.appendSection('command', decls[0])
   2767             self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
   2768             return
   2769         if "KHR" in name:
   2770             self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
   2771             return
   2772         if ("DebugMarker" in name) and ("EXT" in name):
   2773             self.appendSection('command', '// TODO - not wrapping EXT function ' + name)
   2774             return
   2775         # Determine first if this function needs to be intercepted
   2776         startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
   2777         if startthreadsafety is None:
   2778             return
   2779         finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
   2780         # record that the function will be intercepted
   2781         if (self.featureExtraProtect != None):
   2782             self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
   2783         self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
   2784         if (self.featureExtraProtect != None):
   2785             self.intercepts += [ '#endif' ]
   2786 
   2787         OutputGenerator.genCmd(self, cmdinfo, name)
   2788         #
   2789         decls = self.makeCDecls(cmdinfo.elem)
   2790         self.appendSection('command', '')
   2791         self.appendSection('command', decls[0][:-1])
   2792         self.appendSection('command', '{')
   2793         # setup common to call wrappers
   2794         # first parameter is always dispatchable
   2795         dispatchable_type = cmdinfo.elem.find('param/type').text
   2796         dispatchable_name = cmdinfo.elem.find('param/name').text
   2797         self.appendSection('command', '    dispatch_key key = get_dispatch_key('+dispatchable_name+');')
   2798         self.appendSection('command', '    layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
   2799         if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
   2800             self.appendSection('command', '    VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
   2801         else:
   2802             self.appendSection('command', '    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
   2803         # Declare result variable, if any.
   2804         resulttype = cmdinfo.elem.find('proto/type')
   2805         if (resulttype != None and resulttype.text == 'void'):
   2806           resulttype = None
   2807         if (resulttype != None):
   2808             self.appendSection('command', '    ' + resulttype.text + ' result;')
   2809             assignresult = 'result = '
   2810         else:
   2811             assignresult = ''
   2812 
   2813         self.appendSection('command', str(startthreadsafety))
   2814         params = cmdinfo.elem.findall('param/name')
   2815         paramstext = ','.join([str(param.text) for param in params])
   2816         API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
   2817         self.appendSection('command', '    ' + assignresult + API + '(' + paramstext + ');')
   2818         self.appendSection('command', str(finishthreadsafety))
   2819         # Return result variable, if any.
   2820         if (resulttype != None):
   2821             self.appendSection('command', '    return result;')
   2822         self.appendSection('command', '}')
   2823     #
   2824     # override makeProtoName to drop the "vk" prefix
   2825     def makeProtoName(self, name, tail):
   2826         return self.genOpts.apientry + name[2:] + tail
   2827 
   2828 # ParamCheckerOutputGenerator - subclass of OutputGenerator.
   2829 # Generates param checker layer code.
   2830 #
   2831 # ---- methods ----
   2832 # ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
   2833 #   OutputGenerator. Defines additional internal state.
   2834 # ---- methods overriding base class ----
   2835 # beginFile(genOpts)
   2836 # endFile()
   2837 # beginFeature(interface, emit)
   2838 # endFeature()
   2839 # genType(typeinfo,name)
   2840 # genStruct(typeinfo,name)
   2841 # genGroup(groupinfo,name)
   2842 # genEnum(enuminfo, name)
   2843 # genCmd(cmdinfo)
   2844 class ParamCheckerOutputGenerator(OutputGenerator):
   2845     """Generate ParamChecker code based on XML element attributes"""
   2846     # This is an ordered list of sections in the header file.
   2847     ALL_SECTIONS = ['command']
   2848     def __init__(self,
   2849                  errFile = sys.stderr,
   2850                  warnFile = sys.stderr,
   2851                  diagFile = sys.stdout):
   2852         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
   2853         self.INDENT_SPACES = 4
   2854         # Commands to ignore
   2855         self.blacklist = [
   2856             'vkGetInstanceProcAddr',
   2857             'vkGetDeviceProcAddr',
   2858             'vkEnumerateInstanceLayerProperties',
   2859             'vkEnumerateInstanceExtensionsProperties',
   2860             'vkEnumerateDeviceLayerProperties',
   2861             'vkEnumerateDeviceExtensionsProperties',
   2862             'vkCreateDebugReportCallbackEXT',
   2863             'vkDebugReportMessageEXT']
   2864         # Validation conditions for some special case struct members that are conditionally validated
   2865         self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
   2866         # Internal state - accumulators for different inner block text
   2867         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
   2868         self.structNames = []                             # List of Vulkan struct typenames
   2869         self.stypes = []                                  # Values from the VkStructureType enumeration
   2870         self.structTypes = dict()                         # Map of Vulkan struct typename to required VkStructureType
   2871         self.handleTypes = set()                          # Set of handle type names
   2872         self.commands = []                                # List of CommandData records for all Vulkan commands
   2873         self.structMembers = []                           # List of StructMemberData records for all Vulkan structs
   2874         self.validatedStructs = dict()                    # Map of structs type names to generated validation code for that struct type
   2875         self.enumRanges = dict()                          # Map of enum name to BEGIN/END range values
   2876         self.flags = set()                                # Map of flags typenames
   2877         self.flagBits = dict()                            # Map of flag bits typename to list of values
   2878         # Named tuples to store struct and command data
   2879         self.StructType = namedtuple('StructType', ['name', 'value'])
   2880         self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
   2881                                                         'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
   2882                                                         'condition', 'cdecl'])
   2883         self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
   2884         self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
   2885     #
   2886     def incIndent(self, indent):
   2887         inc = ' ' * self.INDENT_SPACES
   2888         if indent:
   2889             return indent + inc
   2890         return inc
   2891     #
   2892     def decIndent(self, indent):
   2893         if indent and (len(indent) > self.INDENT_SPACES):
   2894             return indent[:-self.INDENT_SPACES]
   2895         return ''
   2896     #
   2897     def beginFile(self, genOpts):
   2898         OutputGenerator.beginFile(self, genOpts)
   2899         # C-specific
   2900         #
   2901         # User-supplied prefix text, if any (list of strings)
   2902         if (genOpts.prefixText):
   2903             for s in genOpts.prefixText:
   2904                 write(s, file=self.outFile)
   2905         #
   2906         # Multiple inclusion protection & C++ wrappers.
   2907         if (genOpts.protectFile and self.genOpts.filename):
   2908             headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
   2909             write('#ifndef', headerSym, file=self.outFile)
   2910             write('#define', headerSym, '1', file=self.outFile)
   2911             self.newline()
   2912         #
   2913         # Headers
   2914         write('#include <string>', file=self.outFile)
   2915         self.newline()
   2916         write('#include "vulkan/vulkan.h"', file=self.outFile)
   2917         write('#include "vk_layer_extension_utils.h"', file=self.outFile)
   2918         write('#include "parameter_validation_utils.h"', file=self.outFile)
   2919         #
   2920         # Macros
   2921         self.newline()
   2922         write('#ifndef UNUSED_PARAMETER', file=self.outFile)
   2923         write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
   2924         write('#endif // UNUSED_PARAMETER', file=self.outFile)
   2925         #
   2926         # Namespace
   2927         self.newline()
   2928         write('namespace parameter_validation {', file = self.outFile)
   2929     def endFile(self):
   2930         # C-specific
   2931         self.newline()
   2932         # Namespace
   2933         write('} // namespace parameter_validation', file = self.outFile)
   2934         # Finish C++ wrapper and multiple inclusion protection
   2935         if (self.genOpts.protectFile and self.genOpts.filename):
   2936             self.newline()
   2937             write('#endif', file=self.outFile)
   2938         # Finish processing in superclass
   2939         OutputGenerator.endFile(self)
   2940     def beginFeature(self, interface, emit):
   2941         # Start processing in superclass
   2942         OutputGenerator.beginFeature(self, interface, emit)
   2943         # C-specific
   2944         # Accumulate includes, defines, types, enums, function pointer typedefs,
   2945         # end function prototypes separately for this feature. They're only
   2946         # printed in endFeature().
   2947         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
   2948         self.structNames = []
   2949         self.stypes = []
   2950         self.structTypes = dict()
   2951         self.handleTypes = set()
   2952         self.commands = []
   2953         self.structMembers = []
   2954         self.validatedStructs = dict()
   2955         self.enumRanges = dict()
   2956         self.flags = set()
   2957         self.flagBits = dict()
   2958     def endFeature(self):
   2959         # C-specific
   2960         # Actually write the interface to the output file.
   2961         if (self.emit):
   2962             self.newline()
   2963             # If type declarations are needed by other features based on
   2964             # this one, it may be necessary to suppress the ExtraProtect,
   2965             # or move it below the 'for section...' loop.
   2966             if (self.featureExtraProtect != None):
   2967                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
   2968             # Generate the struct member checking code from the captured data
   2969             self.processStructMemberData()
   2970             # Generate the command parameter checking code from the captured data
   2971             self.processCmdData()
   2972             # Write the declarations for the VkFlags values combining all flag bits
   2973             for flag in sorted(self.flags):
   2974                 flagBits = flag.replace('Flags', 'FlagBits')
   2975                 if flagBits in self.flagBits:
   2976                     bits = self.flagBits[flagBits]
   2977                     decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
   2978                     for bit in bits[1:]:
   2979                         decl += '|' + bit
   2980                     decl += ';'
   2981                     write(decl, file=self.outFile)
   2982             self.newline()
   2983             # Write the parameter validation code to the file
   2984             if (self.sections['command']):
   2985                 if (self.genOpts.protectProto):
   2986                     write(self.genOpts.protectProto,
   2987                           self.genOpts.protectProtoStr, file=self.outFile)
   2988                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
   2989             if (self.featureExtraProtect != None):
   2990                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
   2991             else:
   2992                 self.newline()
   2993         # Finish processing in superclass
   2994         OutputGenerator.endFeature(self)
   2995     #
   2996     # Append a definition to the specified section
   2997     def appendSection(self, section, text):
   2998         # self.sections[section].append('SECTION: ' + section + '\n')
   2999         self.sections[section].append(text)
   3000     #
   3001     # Type generation
   3002     def genType(self, typeinfo, name):
   3003         OutputGenerator.genType(self, typeinfo, name)
   3004         typeElem = typeinfo.elem
   3005         # If the type is a struct type, traverse the imbedded <member> tags
   3006         # generating a structure. Otherwise, emit the tag text.
   3007         category = typeElem.get('category')
   3008         if (category == 'struct' or category == 'union'):
   3009             self.structNames.append(name)
   3010             self.genStruct(typeinfo, name)
   3011         elif (category == 'handle'):
   3012             self.handleTypes.add(name)
   3013         elif (category == 'bitmask'):
   3014             self.flags.add(name)
   3015     #
   3016     # Struct parameter check generation.
   3017     # This is a special case of the <type> tag where the contents are
   3018     # interpreted as a set of <member> tags instead of freeform C
   3019     # C type declarations. The <member> tags are just like <param>
   3020     # tags - they are a declaration of a struct or union member.
   3021     # Only simple member declarations are supported (no nested
   3022     # structs etc.)
   3023     def genStruct(self, typeinfo, typeName):
   3024         OutputGenerator.genStruct(self, typeinfo, typeName)
   3025         conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
   3026         members = typeinfo.elem.findall('.//member')
   3027         #
   3028         # Iterate over members once to get length parameters for arrays
   3029         lens = set()
   3030         for member in members:
   3031             len = self.getLen(member)
   3032             if len:
   3033                 lens.add(len)
   3034         #
   3035         # Generate member info
   3036         membersInfo = []
   3037         for member in members:
   3038             # Get the member's type and name
   3039             info = self.getTypeNameTuple(member)
   3040             type = info[0]
   3041             name = info[1]
   3042             stypeValue = ''
   3043             cdecl = self.makeCParamDecl(member, 0)
   3044             # Process VkStructureType
   3045             if type == 'VkStructureType':
   3046                 # Extract the required struct type value from the comments
   3047                 # embedded in the original text defining the 'typeinfo' element
   3048                 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
   3049                 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
   3050                 if result:
   3051                     value = result.group(0)
   3052                 else:
   3053                     value = self.genVkStructureType(typeName)
   3054                 # Store the required type value
   3055                 self.structTypes[typeName] = self.StructType(name=name, value=value)
   3056             #
   3057             # Store pointer/array/string info
   3058             # Check for parameter name in lens set
   3059             iscount = False
   3060             if name in lens:
   3061                 iscount = True
   3062             # The pNext members are not tagged as optional, but are treated as
   3063             # optional for parameter NULL checks.  Static array members
   3064             # are also treated as optional to skip NULL pointer validation, as
   3065             # they won't be NULL.
   3066             isstaticarray = self.paramIsStaticArray(member)
   3067             isoptional = False
   3068             if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
   3069                 isoptional = True
   3070             membersInfo.append(self.CommandParam(type=type, name=name,
   3071                                                 ispointer=self.paramIsPointer(member),
   3072                                                 isstaticarray=isstaticarray,
   3073                                                 isbool=True if type == 'VkBool32' else False,
   3074                                                 israngedenum=True if type in self.enumRanges else False,
   3075                                                 isconst=True if 'const' in cdecl else False,
   3076                                                 isoptional=isoptional,
   3077                                                 iscount=iscount,
   3078                                                 noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
   3079                                                 len=self.getLen(member),
   3080                                                 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
   3081                                                 condition=conditions[name] if conditions and name in conditions else None,
   3082                                                 cdecl=cdecl))
   3083         self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
   3084     #
   3085     # Capture group (e.g. C "enum" type) info to be used for
   3086     # param check code generation.
   3087     # These are concatenated together with other types.
   3088     def genGroup(self, groupinfo, groupName):
   3089         OutputGenerator.genGroup(self, groupinfo, groupName)
   3090         groupElem = groupinfo.elem
   3091         #
   3092         # Store the sType values
   3093         if groupName == 'VkStructureType':
   3094             for elem in groupElem.findall('enum'):
   3095                 self.stypes.append(elem.get('name'))
   3096         elif 'FlagBits' in groupName:
   3097             bits = []
   3098             for elem in groupElem.findall('enum'):
   3099                 bits.append(elem.get('name'))
   3100             if bits:
   3101                 self.flagBits[groupName] = bits
   3102         else:
   3103             # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
   3104             expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
   3105             expandPrefix = expandName
   3106             expandSuffix = ''
   3107             expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
   3108             if expandSuffixMatch:
   3109                 expandSuffix = '_' + expandSuffixMatch.group()
   3110                 # Strip off the suffix from the prefix
   3111                 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
   3112             isEnum = ('FLAG_BITS' not in expandPrefix)
   3113             if isEnum:
   3114                 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
   3115     #
   3116     # Capture command parameter info to be used for param
   3117     # check code generation.
   3118     def genCmd(self, cmdinfo, name):
   3119         OutputGenerator.genCmd(self, cmdinfo, name)
   3120         if name not in self.blacklist:
   3121             params = cmdinfo.elem.findall('param')
   3122             # Get list of array lengths
   3123             lens = set()
   3124             for param in params:
   3125                 len = self.getLen(param)
   3126                 if len:
   3127                     lens.add(len)
   3128             # Get param info
   3129             paramsInfo = []
   3130             for param in params:
   3131                 paramInfo = self.getTypeNameTuple(param)
   3132                 cdecl = self.makeCParamDecl(param, 0)
   3133                 # Check for parameter name in lens set
   3134                 iscount = False
   3135                 if paramInfo[1] in lens:
   3136                     iscount = True
   3137                 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
   3138                                                     ispointer=self.paramIsPointer(param),
   3139                                                     isstaticarray=self.paramIsStaticArray(param),
   3140                                                     isbool=True if paramInfo[0] == 'VkBool32' else False,
   3141                                                     israngedenum=True if paramInfo[0] in self.enumRanges else False,
   3142                                                     isconst=True if 'const' in cdecl else False,
   3143                                                     isoptional=self.paramIsOptional(param),
   3144                                                     iscount=iscount,
   3145                                                     noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
   3146                                                     len=self.getLen(param),
   3147                                                     extstructs=None,
   3148                                                     condition=None,
   3149                                                     cdecl=cdecl))
   3150             self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
   3151     #
   3152     # Check if the parameter passed in is a pointer
   3153     def paramIsPointer(self, param):
   3154         ispointer = 0
   3155         paramtype = param.find('type')
   3156         if (paramtype.tail is not None) and ('*' in paramtype.tail):
   3157             ispointer = paramtype.tail.count('*')
   3158         elif paramtype.text[:4] == 'PFN_':
   3159             # Treat function pointer typedefs as a pointer to a single value
   3160             ispointer = 1
   3161         return ispointer
   3162     #
   3163     # Check if the parameter passed in is a static array
   3164     def paramIsStaticArray(self, param):
   3165         isstaticarray = 0
   3166         paramname = param.find('name')
   3167         if (paramname.tail is not None) and ('[' in paramname.tail):
   3168             isstaticarray = paramname.tail.count('[')
   3169         return isstaticarray
   3170     #
   3171     # Check if the parameter passed in is optional
   3172     # Returns a list of Boolean values for comma separated len attributes (len='false,true')
   3173     def paramIsOptional(self, param):
   3174         # See if the handle is optional
   3175         isoptional = False
   3176         # Simple, if it's optional, return true
   3177         optString = param.attrib.get('optional')
   3178         if optString:
   3179             if optString == 'true':
   3180                 isoptional = True
   3181             elif ',' in optString:
   3182                 opts = []
   3183                 for opt in optString.split(','):
   3184                     val = opt.strip()
   3185                     if val == 'true':
   3186                         opts.append(True)
   3187                     elif val == 'false':
   3188                         opts.append(False)
   3189                     else:
   3190                         print('Unrecognized len attribute value',val)
   3191                 isoptional = opts
   3192         return isoptional
   3193     #
   3194     # Check if the handle passed in is optional
   3195     # Uses the same logic as ValidityOutputGenerator.isHandleOptional
   3196     def isHandleOptional(self, param, lenParam):
   3197         # Simple, if it's optional, return true
   3198         if param.isoptional:
   3199             return True
   3200         # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
   3201         if param.noautovalidity:
   3202             return True
   3203         # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
   3204         if lenParam and lenParam.isoptional:
   3205             return True
   3206         return False
   3207     #
   3208     # Generate a VkStructureType based on a structure typename
   3209     def genVkStructureType(self, typename):
   3210         # Add underscore between lowercase then uppercase
   3211         value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
   3212         # Change to uppercase
   3213         value = value.upper()
   3214         # Add STRUCTURE_TYPE_
   3215         return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
   3216     #
   3217     # Get the cached VkStructureType value for the specified struct typename, or generate a VkStructureType
   3218     # value assuming the struct is defined by a different feature
   3219     def getStructType(self, typename):
   3220         value = None
   3221         if typename in self.structTypes:
   3222             value = self.structTypes[typename].value
   3223         else:
   3224             value = self.genVkStructureType(typename)
   3225             self.logMsg('diag', 'ParameterValidation: Generating {} for {} structure type that was not defined by the current feature'.format(value, typename))
   3226         return value
   3227     #
   3228     # Retrieve the value of the len tag
   3229     def getLen(self, param):
   3230         result = None
   3231         len = param.attrib.get('len')
   3232         if len and len != 'null-terminated':
   3233             # For string arrays, 'len' can look like 'count,null-terminated',
   3234             # indicating that we have a null terminated array of strings.  We
   3235             # strip the null-terminated from the 'len' field and only return
   3236             # the parameter specifying the string count
   3237             if 'null-terminated' in len:
   3238                 result = len.split(',')[0]
   3239             else:
   3240                 result = len
   3241         return result
   3242     #
   3243     # Retrieve the type and name for a parameter
   3244     def getTypeNameTuple(self, param):
   3245         type = ''
   3246         name = ''
   3247         for elem in param:
   3248             if elem.tag == 'type':
   3249                 type = noneStr(elem.text)
   3250             elif elem.tag == 'name':
   3251                 name = noneStr(elem.text)
   3252         return (type, name)
   3253     #
   3254     # Find a named parameter in a parameter list
   3255     def getParamByName(self, params, name):
   3256         for param in params:
   3257             if param.name == name:
   3258                 return param
   3259         return None
   3260     #
   3261     # Extract length values from latexmath.  Currently an inflexible solution that looks for specific
   3262     # patterns that are found in vk.xml.  Will need to be updated when new patterns are introduced.
   3263     def parseLateXMath(self, source):
   3264         name = 'ERROR'
   3265         decoratedName = 'ERROR'
   3266         if 'mathit' in source:
   3267             # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
   3268             match = re.match(r'latexmath\s*\:\s*\[\s*\$\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\$\s*\]', source)
   3269             if not match or match.group(1) != match.group(4):
   3270                 raise 'Unrecognized latexmath expression'
   3271             name = match.group(2)
   3272             decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
   3273         else:
   3274             # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
   3275             match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
   3276             name = match.group(1)
   3277             decoratedName = '{}/{}'.format(*match.group(1, 2))
   3278         return name, decoratedName
   3279     #
   3280     # Get the length paramater record for the specified parameter name
   3281     def getLenParam(self, params, name):
   3282         lenParam = None
   3283         if name:
   3284             if '->' in name:
   3285                 # The count is obtained by dereferencing a member of a struct parameter
   3286                 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
   3287                                              isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
   3288                                              condition=None, cdecl=None)
   3289             elif 'latexmath' in name:
   3290                 lenName, decoratedName = self.parseLateXMath(name)
   3291                 lenParam = self.getParamByName(params, lenName)
   3292                 # TODO: Zero-check the result produced by the equation?
   3293                 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
   3294                 #param = self.getParamByName(params, lenName)
   3295                 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
   3296                 #                             isoptional=param.isoptional, type=param.type, len=param.len,
   3297                 #                             isstaticarray=param.isstaticarray, extstructs=param.extstructs,
   3298                 #                             noautovalidity=True, condition=None, cdecl=param.cdecl)
   3299             else:
   3300                 lenParam = self.getParamByName(params, name)
   3301         return lenParam
   3302     #
   3303     # Convert a vulkan.h command declaration into a parameter_validation.h definition
   3304     def getCmdDef(self, cmd):
   3305         #
   3306         # Strip the trailing ';' and split into individual lines
   3307         lines = cmd.cdecl[:-1].split('\n')
   3308         # Replace Vulkan prototype
   3309         lines[0] = 'static bool parameter_validation_' + cmd.name + '('
   3310         # Replace the first argument with debug_report_data, when the first
   3311         # argument is a handle (not vkCreateInstance)
   3312         reportData = '    debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
   3313         if cmd.name != 'vkCreateInstance':
   3314             lines[1] = reportData
   3315         else:
   3316             lines.insert(1, reportData)
   3317         return '\n'.join(lines)
   3318     #
   3319     # Generate the code to check for a NULL dereference before calling the
   3320     # validation function
   3321     def genCheckedLengthCall(self, name, exprs):
   3322         count = name.count('->')
   3323         if count:
   3324             checkedExpr = []
   3325             localIndent = ''
   3326             elements = name.split('->')
   3327             # Open the if expression blocks
   3328             for i in range(0, count):
   3329                 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
   3330                 localIndent = self.incIndent(localIndent)
   3331             # Add the validation expression
   3332             for expr in exprs:
   3333                 checkedExpr.append(localIndent + expr)
   3334             # Close the if blocks
   3335             for i in range(0, count):
   3336                 localIndent = self.decIndent(localIndent)
   3337                 checkedExpr.append(localIndent + '}\n')
   3338             return [checkedExpr]
   3339         # No if statements were required
   3340         return exprs
   3341     #
   3342     # Generate code to check for a specific condition before executing validation code
   3343     def genConditionalCall(self, prefix, condition, exprs):
   3344         checkedExpr = []
   3345         localIndent = ''
   3346         formattedCondition = condition.format(prefix)
   3347         checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
   3348         checkedExpr.append(localIndent + '{\n')
   3349         localIndent = self.incIndent(localIndent)
   3350         for expr in exprs:
   3351             checkedExpr.append(localIndent + expr)
   3352         localIndent = self.decIndent(localIndent)
   3353         checkedExpr.append(localIndent + '}\n')
   3354         return [checkedExpr]
   3355     #
   3356     # Generate the sType check string
   3357     def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName):
   3358         checkExpr = []
   3359         stype = self.structTypes[value.type]
   3360         if lenValue:
   3361             # This is an array with a pointer to a count value
   3362             if lenValue.ispointer:
   3363                 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
   3364                 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", "{ldn}", "{dn}", "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
   3365                     funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix))
   3366             # This is an array with an integer count value
   3367             else:
   3368                 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", "{ldn}", "{dn}", "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
   3369                     funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix))
   3370         # This is an individual struct
   3371         else:
   3372             checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", "{}", "{sv}", {}{vn}, {sv}, {});\n'.format(
   3373                 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value))
   3374         return checkExpr
   3375     #
   3376     # Generate the handle check string
   3377     def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName):
   3378         checkExpr = []
   3379         if lenValue:
   3380             if lenValue.ispointer:
   3381                 # This is assumed to be an output array with a pointer to a count value
   3382                 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
   3383             else:
   3384                 # This is an array with an integer count value
   3385                 checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
   3386                     funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
   3387         else:
   3388             # This is assumed to be an output handle pointer
   3389             raise('Unsupported parameter validation case: Output handles are not NULL checked')
   3390         return checkExpr
   3391     #
   3392     # Generate check string for an array of VkFlags values
   3393     def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName):
   3394         checkExpr = []
   3395         flagBitsName = value.type.replace('Flags', 'FlagBits')
   3396         if not flagBitsName in self.flagBits:
   3397             raise('Unsupported parameter validation case: array of reserved VkFlags')
   3398         else:
   3399             allFlags = 'All' + flagBitsName
   3400             checkExpr.append('skipCall |= validate_flags_array(report_data, "{}", "{}", "{}", "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix))
   3401         return checkExpr
   3402     #
   3403     # Generate pNext check string
   3404     def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName):
   3405         checkExpr = []
   3406         # Generate an array of acceptable VkStructureType values for pNext
   3407         extStructCount = 0
   3408         extStructVar = 'NULL'
   3409         extStructNames = 'NULL'
   3410         if value.extstructs:
   3411             structs = value.extstructs.split(',')
   3412             checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.getStructType(s) for s in structs]) + '};\n')
   3413             extStructCount = 'ARRAY_SIZE(allowedStructs)'
   3414             extStructVar = 'allowedStructs'
   3415             extStructNames = '"' + ', '.join(structs) + '"'
   3416         checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", "{}", {}, {}{}, {}, {});\n'.format(
   3417             funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar))
   3418         return checkExpr
   3419     #
   3420     # Generate the pointer check string
   3421     def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName):
   3422         checkExpr = []
   3423         if lenValue:
   3424             # This is an array with a pointer to a count value
   3425             if lenValue.ispointer:
   3426                 # If count and array parameters are optional, there will be no validation
   3427                 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
   3428                     # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
   3429                     checkExpr.append('skipCall |= validate_array(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
   3430                         funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
   3431             # This is an array with an integer count value
   3432             else:
   3433                 # If count and array parameters are optional, there will be no validation
   3434                 if valueRequired == 'true' or lenValueRequired == 'true':
   3435                     # Arrays of strings receive special processing
   3436                     validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
   3437                     checkExpr.append('skipCall |= {}(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
   3438                         validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
   3439             if checkExpr:
   3440                 if lenValue and ('->' in lenValue.name):
   3441                     # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
   3442                     checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
   3443         # This is an individual struct that is not allowed to be NULL
   3444         elif not value.isoptional:
   3445             # Function pointers need a reinterpret_cast to void*
   3446             if value.type[:4] == 'PFN_':
   3447                 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", "{}", reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name))
   3448             else:
   3449                 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", "{}", {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name))
   3450         return checkExpr
   3451     #
   3452     # Process struct member validation code, performing name suibstitution if required
   3453     def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix):
   3454         if any(token in line for token in ['{funcName}', '{valuePrefix}', '{displayNamePrefix}']):
   3455             return line.format(funcName=funcName, valuePrefix=memberNamePrefix, displayNamePrefix=memberDisplayNamePrefix)
   3456         return line
   3457     #
   3458     # Process struct validation code for inclusion in function or parent struct validation code
   3459     def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output):
   3460         for line in lines:
   3461             if output:
   3462                 output[-1] += '\n'
   3463             if type(line) is list:
   3464                 for sub in line:
   3465                     output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix))
   3466             else:
   3467                 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix))
   3468         return output
   3469     #
   3470     # Process struct pointer/array validation code, perfoeming name substitution if required
   3471     def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName):
   3472         expr = []
   3473         expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
   3474         expr.append('{')
   3475         indent = self.incIndent(None)
   3476         if lenValue:
   3477             # Need to process all elements in the array
   3478             indexName = lenValue.name.replace('Count', 'Index')
   3479             expr[-1] += '\n'
   3480             expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
   3481             expr.append(indent + '{')
   3482             indent = self.incIndent(indent)
   3483             # Prefix for value name to display in error message
   3484             memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
   3485             memberDisplayNamePrefix = '{}[i].'.format(valueDisplayName)
   3486         else:
   3487             memberNamePrefix = '{}{}->'.format(prefix, value.name)
   3488             memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
   3489         #
   3490         # Expand the struct validation lines
   3491         expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr)
   3492         #
   3493         if lenValue:
   3494             # Close if and for scopes
   3495             indent = self.decIndent(indent)
   3496             expr.append(indent + '}\n')
   3497         expr.append('}\n')
   3498         return expr
   3499     #
   3500     # Generate the parameter checking code
   3501     def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
   3502         lines = []    # Generated lines of code
   3503         unused = []   # Unused variable names
   3504         for value in values:
   3505             usedLines = []
   3506             lenParam = None
   3507             #
   3508             # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
   3509             valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
   3510             #
   3511             # Check for NULL pointers, ignore the inout count parameters that
   3512             # will be validated with their associated array
   3513             if (value.ispointer or value.isstaticarray) and not value.iscount:
   3514                 #
   3515                 # Parameters for function argument generation
   3516                 req = 'true'    # Paramerter cannot be NULL
   3517                 cpReq = 'true'  # Count pointer cannot be NULL
   3518                 cvReq = 'true'  # Count value cannot be 0
   3519                 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
   3520                 #
   3521                 # Generate required/optional parameter strings for the pointer and count values
   3522                 if value.isoptional:
   3523                     req = 'false'
   3524                 if value.len:
   3525                     # The parameter is an array with an explicit count parameter
   3526                     lenParam = self.getLenParam(values, value.len)
   3527                     lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
   3528                     if lenParam.ispointer:
   3529                         # Count parameters that are pointers are inout
   3530                         if type(lenParam.isoptional) is list:
   3531                             if lenParam.isoptional[0]:
   3532                                 cpReq = 'false'
   3533                             if lenParam.isoptional[1]:
   3534                                 cvReq = 'false'
   3535                         else:
   3536                             if lenParam.isoptional:
   3537                                 cpReq = 'false'
   3538                     else:
   3539                         if lenParam.isoptional:
   3540                             cvReq = 'false'
   3541                 #
   3542                 # The parameter will not be processes when tagged as 'noautovalidity'
   3543                 # For the pointer to struct case, the struct pointer will not be validated, but any
   3544                 # members not tagged as 'noatuvalidity' will be validated
   3545                 if value.noautovalidity:
   3546                     # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
   3547                     self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
   3548                 else:
   3549                     #
   3550                     # If this is a pointer to a struct with an sType field, verify the type
   3551                     if value.type in self.structTypes:
   3552                         usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName)
   3553                     # If this is an input handle array that is not allowed to contain NULL handles, verify that none of the handles are VK_NULL_HANDLE
   3554                     elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
   3555                         usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName)
   3556                     elif value.type in self.flags and value.isconst:
   3557                         usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName)
   3558                     elif value.isbool and value.isconst:
   3559                         usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", "{}", "{}", {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix))
   3560                     elif value.israngedenum and value.isconst:
   3561                         enumRange = self.enumRanges[value.type]
   3562                         usedLines.append('skipCall |= validate_ranged_enum_array(report_data, "{}", "{}", "{}", "{}", {}, {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, cvReq, req, pf=valuePrefix))
   3563                     elif value.name == 'pNext':
   3564                         # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
   3565                         if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
   3566                             usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName)
   3567                     else:
   3568                         usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName)
   3569                 #
   3570                 # If this is a pointer to a struct (input), see if it contains members that need to be checked
   3571                 if value.type in self.validatedStructs and value.isconst:
   3572                     usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName))
   3573             # Non-pointer types
   3574             else:
   3575                 #
   3576                 # The parameter will not be processes when tagged as 'noautovalidity'
   3577                 # For the struct case, the struct type will not be validated, but any
   3578                 # members not tagged as 'noatuvalidity' will be validated
   3579                 if value.noautovalidity:
   3580                     # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
   3581                     self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
   3582                 else:
   3583                     if value.type in self.structTypes:
   3584                         stype = self.structTypes[value.type]
   3585                         usedLines.append('skipCall |= validate_struct_type(report_data, "{}", "{}", "{sv}", &({}{vn}), {sv}, false);\n'.format(
   3586                             funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value))
   3587                     elif value.type in self.handleTypes:
   3588                         if not self.isHandleOptional(value, None):
   3589                             usedLines.append('skipCall |= validate_required_handle(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name))
   3590                     elif value.type in self.flags:
   3591                         flagBitsName = value.type.replace('Flags', 'FlagBits')
   3592                         if not flagBitsName in self.flagBits:
   3593                             usedLines.append('skipCall |= validate_reserved_flags(report_data, "{}", "{}", {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix))
   3594                         else:
   3595                             flagsRequired = 'false' if value.isoptional else 'true'
   3596                             allFlagsName = 'All' + flagBitsName
   3597                             usedLines.append('skipCall |= validate_flags(report_data, "{}", "{}", "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix))
   3598                     elif value.isbool:
   3599                         usedLines.append('skipCall |= validate_bool32(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name))
   3600                     elif value.israngedenum:
   3601                         enumRange = self.enumRanges[value.type]
   3602                         usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", "{}", "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name))
   3603                 #
   3604                 # If this is a pointer to a struct (input), see if it contains members that need to be checked
   3605                 if value.type in self.validatedStructs:
   3606                     memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
   3607                     memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
   3608                     usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', []))
   3609             #
   3610             # Append the parameter check to the function body for the current command
   3611             if usedLines:
   3612                 # Apply special conditional checks
   3613                 if value.condition:
   3614                     usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
   3615                 lines += usedLines
   3616             elif not value.iscount:
   3617                 # If no expression was generated for this value, it is unreferenced by the validation function, unless
   3618                 # it is an array count, which is indirectly referenced for array valiadation.
   3619                 unused.append(value.name)
   3620         return lines, unused
   3621     #
   3622     # Generate the struct member check code from the captured data
   3623     def processStructMemberData(self):
   3624         indent = self.incIndent(None)
   3625         for struct in self.structMembers:
   3626             #
   3627             # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
   3628             lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
   3629             if lines:
   3630                 self.validatedStructs[struct.name] = lines
   3631     #
   3632     # Generate the command param check code from the captured data
   3633     def processCmdData(self):
   3634         indent = self.incIndent(None)
   3635         for command in self.commands:
   3636             # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
   3637             startIndex = 0 if command.name == 'vkCreateInstance' else 1
   3638             lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
   3639             if lines:
   3640                 cmdDef = self.getCmdDef(command) + '\n'
   3641                 cmdDef += '{\n'
   3642                 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
   3643                 # processed by parameter_validation (except for vkCreateInstance, which does not have a
   3644                 # handle as its first parameter)
   3645                 if unused:
   3646                     for name in unused:
   3647                         cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
   3648                     if len(unused) > 0:
   3649                         cmdDef += '\n'
   3650                 cmdDef += indent + 'bool skipCall = false;\n'
   3651                 for line in lines:
   3652                     cmdDef += '\n'
   3653                     if type(line) is list:
   3654                         for sub in line:
   3655                             cmdDef += indent + sub
   3656                     else:
   3657                         cmdDef += indent + line
   3658                 cmdDef += '\n'
   3659                 cmdDef += indent + 'return skipCall;\n'
   3660                 cmdDef += '}\n'
   3661                 self.appendSection('command', cmdDef)
   3662