3 # Copyright (c) 2013-2016 The Khronos Group Inc.
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
18 from collections import namedtuple
19 import xml.etree.ElementTree as etree
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]) )
27 # noneStr - returns string argument, or "" if argument is None.
28 # Used in converting etree Elements into text.
29 # str - string to convert
36 # enquote - returns string argument with surrounding quotes,
37 # for serialization into Python code.
40 return "'" + str + "'"
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'):
53 elif (feature.category == 'ARB' or
54 feature.category == 'KHR' or
55 feature.category == 'OES'):
60 # Secondary sort key for regSortFeatures.
61 # Sorts by extension name.
62 def regSortNameKey(feature):
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)
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)
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)
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.
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
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
112 class GeneratorOptions:
113 """Represents options during header production from an API registry"""
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
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 == ''):
142 # CGeneratorOptions - subclass of GeneratorOptions.
144 # Adds options used by COutputGenerator objects during C language header
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
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
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"""
183 defaultExtensions = None,
184 addExtensions = None,
185 removeExtensions = None,
186 sortProcedure = regSortFeatures,
188 genFuncPointers = True,
190 protectFeature = True,
192 protectProtoStr = None,
196 indentFuncProto = True,
197 indentFuncPointer = False,
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
215 # DocGeneratorOptions - subclass of GeneratorOptions.
217 # Shares many members with CGeneratorOptions, since
218 # both are writing C-style declarations:
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
236 # Additional members:
238 class DocGeneratorOptions(GeneratorOptions):
239 """Represents options during C interface generation for Asciidoc"""
246 defaultExtensions = None,
247 addExtensions = None,
248 removeExtensions = None,
249 sortProcedure = regSortFeatures,
254 genDirectory = 'gen',
255 indentFuncProto = True,
256 indentFuncPointer = False,
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
272 # ThreadGeneratorOptions - subclass of GeneratorOptions.
274 # Adds options used by COutputGenerator objects during C language header
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
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"""
310 defaultExtensions = None,
311 addExtensions = None,
312 removeExtensions = None,
313 sortProcedure = regSortFeatures,
315 genFuncPointers = True,
317 protectFeature = True,
319 protectProtoStr = True,
323 indentFuncProto = True,
324 indentFuncPointer = False,
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
345 # ParamCheckerGeneratorOptions - subclass of GeneratorOptions.
347 # Adds options used by ParamCheckerOutputGenerator objects during parameter validation
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
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
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"""
386 defaultExtensions = None,
387 addExtensions = None,
388 removeExtensions = None,
389 sortProcedure = regSortFeatures,
391 genFuncPointers = True,
393 protectFeature = True,
395 protectProtoStr = None,
399 indentFuncProto = True,
400 indentFuncPointer = False,
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
421 # OutputGenerator - base class for generating API interfaces.
422 # Manages basic logic, logging, and output file control
423 # Derived classes actually generate formatted output.
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
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)
459 class OutputGenerator:
460 """Generate specified API interfaces in a specific style, such as a C header"""
462 errFile = sys.stderr,
463 warnFile = sys.stderr,
464 diagFile = sys.stdout):
466 self.errFile = errFile
467 self.warnFile = warnFile
468 self.diagFile = diagFile
470 self.featureName = None
473 # Used for extension enum value generation
474 self.extBase = 1000000000
475 self.extBlockSize = 1000
477 # logMsg - write a message of different categories to different
480 # 'diag' (diagnostic, voluminous)
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)
500 '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
502 # enumToValue - parses and converts an <enum> tag into a value.
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')
521 if ('value' in elem.keys()):
522 value = elem.get('value')
523 # print('About to translate value =', value, 'type =', type(value))
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)
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
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()):
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
556 value = '%d' % numVal
558 self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
559 return [numVal, value]
562 def beginFile(self, genOpts):
563 self.genOpts = genOpts
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')
571 self.outFile = open(self.genOpts.filename, 'w')
573 self.outFile = sys.stdout
575 self.errFile and self.errFile.flush()
576 self.warnFile and self.warnFile.flush()
577 self.diagFile and self.diagFile.flush()
579 if (self.outFile != sys.stdout and self.outFile != sys.stderr):
583 def beginFeature(self, interface, 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
594 def validateFeature(self, featureType, featureName):
595 if (self.featureName == None):
596 raise UserWarning('Attempt to generate', featureType, name,
597 'when not in feature')
600 def genType(self, typeinfo, name):
601 self.validateFeature('type', name)
603 # Struct (e.g. C "struct" type) generation
604 def genStruct(self, typeinfo, name):
605 self.validateFeature('struct', name)
607 # Group (e.g. C "enum" type) generation
608 def genGroup(self, groupinfo, name):
609 self.validateFeature('group', name)
611 # Enumerant (really, constant) generation
612 def genEnum(self, enuminfo, name):
613 self.validateFeature('enum', name)
616 def genCmd(self, cmd, name):
617 self.validateFeature('command', name)
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 + ')'
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
634 def makeCParamDecl(self, param, aligncol):
635 paramdecl = ' ' + noneStr(param.text)
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
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)
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
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
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
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.
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)
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))])
708 # Indented parameters
712 paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam)
717 indentdecl += paramdecl
719 indentdecl = '(void);'
720 # Non-indented parameters
724 paramdecl += ''.join([t for t in params[i].itertext()])
730 return [ pdecl + indentdecl, tdecl + paramdecl ]
733 write('', file=self.outFile)
735 def setRegistry(self, registry):
736 self.registry = registry
739 # COutputGenerator - subclass of OutputGenerator.
740 # Generates C-language API interfaces.
743 # COutputGenerator(errFile, warnFile, diagFile) - args as for
744 # OutputGenerator. Defines additional internal state.
745 # ---- methods overriding base class ----
748 # beginFeature(interface, emit)
750 # genType(typeinfo,name)
751 # genStruct(typeinfo,name)
752 # genGroup(groupinfo,name)
753 # genEnum(enuminfo, name)
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']
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])
769 def beginFile(self, genOpts):
770 OutputGenerator.beginFile(self, genOpts)
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)
780 write('#ifdef __cplusplus', file=self.outFile)
781 write('extern "C" {', file=self.outFile)
782 write('#endif', file=self.outFile)
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)
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)
805 # Finish C++ wrapper and multiple inclusion protection
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):
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)
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):
825 # Actually write the interface to the output file.
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]
839 write('\n'.join(contents), file=self.outFile)
841 if (self.genOpts.genFuncPointers and self.sections['commandPointer']):
842 write('\n'.join(self.sections['commandPointer']), file=self.outFile)
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)
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)
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)
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)
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)
883 s += noneStr(elem.text) + noneStr(elem.tail)
885 # Add extra newline after multi-line entries.
888 self.appendSection(category, s)
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
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)
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)
907 body += '} ' + typeName + ';\n'
908 self.appendSection('struct', body)
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
916 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
918 expandPrefix = expandName
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]
927 body = "\ntypedef enum " + groupName + " {\n"
929 isEnum = ('FLAG_BITS' not in expandPrefix)
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.
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')
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"
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):
957 elif (numVal > maxValue):
960 # Generate min/max value tokens and a range-padding enum. Need some
961 # additional padding to generate correct names...
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"
967 body += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
970 body += "} " + groupName + ";"
971 if groupElem.get('type') == 'bitmask':
975 self.appendSection(section, body)
976 # Enumerant generation
977 # <enum> tags may specify their values in several ways, but are usually
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)
986 def genCmd(self, cmdinfo, name):
987 OutputGenerator.genCmd(self, cmdinfo, name)
989 decls = self.makeCDecls(cmdinfo.elem)
990 self.appendSection('command', decls[0] + '\n')
991 if (self.genOpts.genFuncPointers):
992 self.appendSection('commandPointer', decls[1])
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.
1002 # DocOutputGenerator(errFile, warnFile, diagFile) - args as for
1003 # OutputGenerator. Defines additional internal state.
1004 # ---- methods overriding base class ----
1005 # beginFile(genOpts)
1007 # beginFeature(interface, emit)
1009 # genType(typeinfo,name)
1010 # genStruct(typeinfo,name)
1011 # genGroup(groupinfo,name)
1012 # genEnum(enuminfo, name)
1014 class DocOutputGenerator(OutputGenerator):
1015 """Generate specified API interfaces in a specific style, such as a C header"""
1017 errFile = sys.stderr,
1018 warnFile = sys.stderr,
1019 diagFile = sys.stdout):
1020 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1022 def beginFile(self, genOpts):
1023 OutputGenerator.beginFile(self, genOpts)
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)
1033 # Generate an include file
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):
1040 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1041 self.logMsg('diag', '# Generating include file:', filename)
1042 fp = open(filename, 'w')
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)
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)
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)
1075 s += noneStr(elem.text) + noneStr(elem.tail)
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')
1084 self.logMsg('diag', '# NOT writing include file for type:',
1085 name, 'category: ', category)
1087 self.logMsg('diag', '# NOT writing empty include file for type', name)
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
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)
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)
1106 s += '} ' + typeName + ';'
1107 self.writeInclude('structs', typeName, s)
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
1115 # See if we need min/max/num/padding at end
1116 expand = self.genOpts.expandEnumerants
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)
1122 expandPrefix = expandName
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]
1133 s = "typedef enum " + groupName + " {\n"
1135 # Loop over the nested 'enum' tags. Keep track of the minimum and
1136 # maximum numeric values, if they can be determined.
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')
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"
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):
1159 elif (numVal > maxValue):
1162 # Generate min/max value tokens and a range-padding enum. Need some
1163 # additional padding to generate correct names...
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"
1171 s += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
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
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)
1185 # Command generation
1186 def genCmd(self, cmdinfo, name):
1187 OutputGenerator.genCmd(self, cmdinfo, name)
1189 decls = self.makeCDecls(cmdinfo.elem)
1190 self.writeInclude('protos', name, decls[0])
1192 # PyOutputGenerator - subclass of OutputGenerator.
1193 # Generates Python data structures describing API names.
1194 # Similar to DocOutputGenerator, but writes a single
1198 # PyOutputGenerator(errFile, warnFile, diagFile) - args as for
1199 # OutputGenerator. Defines additional internal state.
1200 # ---- methods overriding base class ----
1201 # beginFile(genOpts)
1203 # genType(typeinfo,name)
1204 # genStruct(typeinfo,name)
1205 # genGroup(groupinfo,name)
1206 # genEnum(enuminfo, name)
1208 class PyOutputGenerator(OutputGenerator):
1209 """Generate specified API interfaces in a specific style, such as a C header"""
1211 errFile = sys.stderr,
1212 warnFile = sys.stderr,
1213 diagFile = sys.stdout):
1214 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
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)
1222 OutputGenerator.endFile(self)
1224 # Add a name from the interface
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)
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
1241 # For 'funcpointer' types, add the type name to the 'funcpointers'
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)
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))
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)
1274 write('# Unprocessed type:', name, 'category:', category, file=self.outFile)
1276 write('# Unprocessed type:', name, file=self.outFile)
1278 # Struct (e.g. C "struct" type) generation.
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)
1285 members = [member.text for member in typeinfo.elem.findall('.//member/name')]
1286 self.addName('structs', typeName, members)
1288 # Group (e.g. C "enum" type) generation.
1289 # These are concatenated together with other types.
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
1299 # @enums C 'enum' name Dictionary of enumerant names
1300 # @consts C enumerant/const name Name of corresponding 'enums' key
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)
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)
1316 # @consts C enumerant/const name Name of corresponding 'enums' key
1318 self.addName('consts', name, None)
1320 # Command generation
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)
1327 params = [param.text for param in cmdinfo.elem.findall('param/name')]
1328 self.addName('protos', name, params)
1330 # ValidityOutputGenerator - subclass of OutputGenerator.
1331 # Generates AsciiDoc includes of valid usage information, for reference
1332 # pages and the Vulkan specification. Similar to DocOutputGenerator.
1335 # ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
1336 # OutputGenerator. Defines additional internal state.
1337 # ---- methods overriding base class ----
1338 # beginFile(genOpts)
1340 # beginFeature(interface, emit)
1343 class ValidityOutputGenerator(OutputGenerator):
1344 """Generate specified API interfaces in a specific style, such as a C header"""
1346 errFile = sys.stderr,
1347 warnFile = sys.stderr,
1348 diagFile = sys.stdout):
1349 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1351 def beginFile(self, genOpts):
1352 OutputGenerator.beginFile(self, genOpts)
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)
1362 def makeParameterName(self, name):
1363 return 'pname:' + name
1365 def makeStructName(self, name):
1366 return 'sname:' + name
1368 def makeBaseTypeName(self, name):
1369 return 'basetype:' + name
1371 def makeEnumerationName(self, name):
1372 return 'elink:' + name
1374 def makeEnumerantName(self, name):
1375 return 'ename:' + name
1377 def makeFLink(self, name):
1378 return 'flink:' + name
1381 # Generate an include file
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):
1388 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1389 self.logMsg('diag', '# Generating include file:', filename)
1390 fp = open(filename, 'w')
1392 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
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)
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)
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)
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)
1480 # Check if the parameter passed in is a pointer
1481 def paramIsPointer(self, param):
1483 paramtype = param.find('type')
1484 if paramtype.tail is not None and '*' in paramtype.tail:
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] == '[':
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')
1502 if paramenumsize is not None:
1503 return paramenumsize.text
1505 return paramname.tail[1:-1]
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
1513 # Get the parent of a handle object
1514 def getHandleParent(self, typename):
1515 types = self.registry.findall("types/type")
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')
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':
1529 def isHandleOptional(self, param, params):
1531 # See if the handle is optional
1534 # Simple, if it's optional, return true
1535 if param.attrib.get('optional') is not None:
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:
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:
1554 # Get the category of a type
1555 def getTypeCategory(self, typename):
1556 types = self.registry.findall("types/type")
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')
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')
1567 # General pre-amble. Check optionality and add stuff.
1570 if self.paramIsStaticArray(param):
1571 asciidoc += 'Any given element of '
1573 elif self.paramIsArray(param):
1574 lengths = param.attrib.get('len').split(',')
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))
1586 optionallengths.append(self.makeParameterName(length))
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:
1593 if len(optionallengths) != 0:
1594 if len(optionallengths) == 1:
1596 asciidoc += optionallengths[0]
1600 asciidoc += ' or '.join(optionallengths)
1603 asciidoc += 'not `0`, '
1605 if len(optionallengths) != 0 and param.attrib.get('optional') is not None:
1608 if param.attrib.get('optional') is not None:
1609 asciidoc += self.makeParameterName(paramname.text)
1610 asciidoc += ' is not `NULL`, '
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':
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'
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):
1635 paramname = param.find('name')
1636 paramtype = param.find('type')
1638 asciidoc += self.makeAsciiDocPreChunk(param, params)
1640 asciidoc += self.makeParameterName(paramname.text)
1641 asciidoc += ' must: be '
1643 if self.paramIsArray(param):
1644 # Arrays. These are hard to get right, apparently
1646 lengths = param.attrib.get('len').split(',')
1648 if (lengths[0]) == 'null-terminated':
1649 asciidoc += 'a null-terminated '
1650 elif (lengths[0]) == '1':
1651 asciidoc += 'a pointer to '
1653 asciidoc += 'a pointer to an array of '
1655 # Handle equations, which are currently denoted with latex
1656 if 'latexmath:' in lengths[0]:
1657 asciidoc += lengths[0]
1659 asciidoc += self.makeParameterName(lengths[0])
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 '
1668 asciidoc += 'pointers to arrays of '
1669 # Handle equations, which are currently denoted with latex
1670 if 'latex:' in length:
1673 asciidoc += self.makeParameterName(length)
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#
1682 asciidoc = asciidoc[:-4]
1684 # An array of void values is a byte array.
1687 elif paramtype.text == 'char':
1688 # A null terminated array of chars is a string
1689 if lengths[-1] == 'null-terminated':
1690 asciidoc += 'string'
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 '
1701 asciidoc += typetext
1704 if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'):
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('*')
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 '
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
1720 # Pointer to nothing in particular - delete the " to " portion
1721 asciidoc = asciidoc[:-4]
1723 # Add an article for English semantic win
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 '
1731 asciidoc += typetext
1734 # Non-pointer, non-optional things must be valid
1735 asciidoc += 'a valid '
1736 asciidoc += typetext
1741 # Add additional line for non-optional bitmasks
1742 if self.getTypeCategory(paramtype.text) == 'bitmask':
1743 if param.attrib.get('optional') is None:
1745 if self.paramIsArray(param):
1746 asciidoc += 'Each element of '
1747 asciidoc += 'pname:'
1748 asciidoc += paramname.text
1749 asciidoc += ' mustnot: be `0`'
1754 def makeAsciiDocLineForParameter(self, param, params, typetext):
1755 if param.attrib.get('noautovalidity') is not None:
1757 asciidoc = self.createValidationLineForParameterIntroChunk(param, params, typetext)
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):
1764 struct = self.registry.find("types/type[@name='" + structname + "']")
1766 params = struct.findall('member')
1767 validity = struct.find('validity')
1769 if validity is not None:
1772 for param in params:
1773 paramname = param.find('name')
1774 paramtype = param.find('type')
1775 typecategory = self.getTypeCategory(paramtype.text)
1777 if paramname.text == 'pNext':
1780 if paramname.text == 'sType':
1783 if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param):
1784 if self.makeAsciiDocLineForParameter(param, params, '') != '':
1786 elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask' or param.attrib.get('returnedonly') == 'true':
1788 elif typecategory == 'struct' or typecategory == 'union':
1789 if self.isStructAlwaysValid(paramtype.text) is False:
1795 # Make an entire asciidoc line for a given parameter
1796 def createValidationLineForParameter(self, param, params, typecategory):
1798 paramname = param.find('name')
1799 paramtype = param.find('type')
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:
1810 asciidoc += self.makeParameterName(paramname.text)
1811 asciidoc += ' must: be `0`'
1814 if self.paramIsArray(param):
1815 asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value')
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')
1834 # Make an asciidoc validity entry for a handle's parent object
1835 def makeAsciiDocHandleParent(self, param, params):
1837 paramname = param.find('name')
1838 paramtype = param.find('type')
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:
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'
1857 asciidoc += self.makeParameterName(paramname.text)
1858 asciidoc += ' is a valid handle, it'
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)
1870 # Generate an asciidoc validity line for the sType value of a struct
1871 def makeStructureType(self, blockname, param):
1873 paramname = param.find('name')
1874 paramtype = param.find('type')
1876 asciidoc += self.makeParameterName(paramname.text)
1877 asciidoc += ' must: be '
1880 for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname):
1882 structuretype += 'VK_STRUCTURE_TYPE_'
1884 structuretype += elem[0].upper()
1885 structuretype += '_'
1887 asciidoc += self.makeEnumerantName(structuretype[:-1])
1893 # Generate an asciidoc validity line for the pNext value of a struct
1894 def makeStructureExtensionPointer(self, param):
1896 paramname = param.find('name')
1897 paramtype = param.find('type')
1899 asciidoc += self.makeParameterName(paramname.text)
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
1909 asciidoc += (', ').join(extensionstructs[:-1]) + ' or ' + extensionstructs[-1]
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
1922 anyparentedhandlesoptional = False
1923 parentdictionary = {}
1924 arraylengths = set()
1925 for param in params:
1926 paramname = param.find('name')
1927 paramtype = param.find('type')
1929 # Get the type's category
1930 typecategory = self.getTypeCategory(paramtype.text)
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)
1938 asciidoc += self.createValidationLineForParameter(param, params, typecategory)
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)
1948 # If any param is optional, it affects the output
1949 if self.isHandleOptional(param, params):
1950 anyparentedhandlesoptional = True
1952 # Find the first dispatchable parent
1954 while ancestor is not None and not self.isHandleTypeDispatchable(ancestor):
1955 ancestor = self.getHandleParent(ancestor)
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] = []
1962 if self.paramIsArray(param):
1963 parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text))
1965 parentdictionary[ancestor].append(self.makeParameterName(paramname.text))
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)
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:
1979 for queuetype in re.findall(r'([^,]+)', queuetypes):
1980 queuebits.append(queuetype.replace('_',' '))
1983 asciidoc += 'The pname:queue must: support '
1984 if len(queuebits) == 1:
1985 asciidoc += queuebits[0]
1987 asciidoc += (', ').join(queuebits[:-1])
1989 asciidoc += queuebits[-1]
1990 asciidoc += ' operations'
1993 if 'vkCmd' in blockname:
1994 # The commandBuffer parameter must be being recorded
1996 asciidoc += 'pname:commandBuffer must: be in the recording state'
1999 # The queue type must be valid
2000 queuetypes = cmd.attrib.get('queues')
2002 for queuetype in re.findall(r'([^,]+)', queuetypes):
2003 queuebits.append(queuetype.replace('_',' '))
2006 asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
2007 if len(queuebits) == 1:
2008 asciidoc += queuebits[0]
2010 asciidoc += (', ').join(queuebits[:-1])
2012 asciidoc += queuebits[-1]
2013 asciidoc += ' operations'
2016 # Must be called inside/outside a renderpass appropriately
2017 renderpass = cmd.attrib.get('renderpass')
2019 if renderpass != 'both':
2020 asciidoc += '* This command must: only be called '
2021 asciidoc += renderpass
2022 asciidoc += ' of a render pass instance'
2025 # Must be in the right level command buffer
2026 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2028 if cmdbufferlevel != 'primary,secondary':
2029 asciidoc += '* pname:commandBuffer must: be a '
2030 asciidoc += cmdbufferlevel
2031 asciidoc += ' sname:VkCommandBuffer'
2034 # Any non-optional arraylengths should specify they must be greater than 0
2035 for param in params:
2036 paramname = param.find('name')
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']")
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']"))
2049 # Allow lengths to be arbitrary if all their dependents are optional
2050 if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
2052 if len(optionalarrays) > 1:
2053 asciidoc += 'any of '
2055 for array in optionalarrays[:-1]:
2056 asciidoc += self.makeParameterName(optionalarrays.find('name').text)
2059 if len(optionalarrays) > 1:
2061 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2064 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2067 asciidoc += 'not `NULL`, '
2069 if self.paramIsPointer(param):
2070 asciidoc += 'the value referenced by '
2072 elif self.paramIsPointer(param):
2073 asciidoc += 'The value referenced by '
2075 asciidoc += self.makeParameterName(arraylength)
2076 asciidoc += ' must: be greater than `0`'
2079 # Find the parents of all objects referenced in this command
2080 for param in handles:
2081 asciidoc += self.makeAsciiDocHandleParent(param, params)
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])
2091 while ancestor is not None and ancestor not in parentdictionary:
2092 ancestor = self.getHandleParent(ancestor)
2094 if ancestor is not None:
2095 parentdictionary[ancestor] += parentdictionary.pop(parent[0])
2097 # No ancestors possible - so count it up
2098 noancestorscount += 1
2100 # Add validation language about common ancestors
2101 for parent in parentdictionary.items():
2102 if len(parent[1]) > 1:
2103 parentlanguage = '* '
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'
2115 # Capitalize and add to the main language
2116 asciidoc += parentlanguage
2118 # Add in any plain-text validation language that should be added
2119 for usage in usages:
2124 # In case there's nothing to report, return None
2127 # Delimit the asciidoc block
2130 def makeThreadSafetyBlock(self, cmd, paramtext):
2131 """Generate C function pointer typedef for <command> Element"""
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'):
2137 paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from'
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(','):
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)
2155 paramdecl += self.makeParameterName(paramname.text)
2157 paramdecl += 'pname:'
2158 paramdecl += externsyncattrib
2159 paramdecl += ' must: be externally synchronized\n'
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:
2166 paramdecl += 'Host access to '
2167 paramdecl += elem.text
2168 paramdecl += ' must: be externally synchronized\n'
2170 if (paramdecl == ''):
2175 def makeCommandPropertiesTableEntry(self, cmd, name):
2178 # Must be called inside/outside a renderpass appropriately
2179 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2180 cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
2182 renderpass = cmd.attrib.get('renderpass')
2183 renderpass = renderpass.capitalize()
2185 queues = cmd.attrib.get('queues')
2186 queues = (' + \n').join(queues.upper().split(','))
2188 return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues
2189 elif 'vkQueue' in name:
2190 # Must be called inside/outside a renderpass appropriately
2192 queues = cmd.attrib.get('queues')
2196 queues = (' + \n').join(queues.upper().split(','))
2198 return '|-|-|' + queues
2202 def makeSuccessCodes(self, cmd, name):
2204 successcodes = cmd.attrib.get('successcodes')
2205 if successcodes is not None:
2207 successcodeentry = ''
2208 successcodes = successcodes.split(',')
2209 return '* ename:' + '\n* ename:'.join(successcodes)
2213 def makeErrorCodes(self, cmd, name):
2215 errorcodes = cmd.attrib.get('errorcodes')
2216 if errorcodes is not None:
2219 errorcodes = errorcodes.split(',')
2220 return '* ename:' + '\n* ename:'.join(errorcodes)
2225 # Command generation
2226 def genCmd(self, cmdinfo, name):
2227 OutputGenerator.genCmd(self, cmdinfo, name)
2229 # Get all the parameters
2230 params = cmdinfo.elem.findall('param')
2231 usageelements = cmdinfo.elem.findall('validity/usage')
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)
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)
2247 self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
2251 def genStruct(self, typeinfo, typename):
2252 OutputGenerator.genStruct(self, typeinfo, typename)
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')
2258 usageelements = typeinfo.elem.findall('validity/usage')
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)
2268 validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
2269 threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
2271 self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None)
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)
2278 def genType(self, typeinfo, typename):
2279 OutputGenerator.genType(self, typeinfo, typename)
2281 category = typeinfo.elem.get('category')
2282 if (category == 'struct' or category == 'union'):
2283 self.genStruct(typeinfo, typename)
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.
2291 # HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for
2292 # OutputGenerator. Defines additional internal state.
2293 # ---- methods overriding base class ----
2295 class HostSynchronizationOutputGenerator(OutputGenerator):
2296 # Generate Host Synchronized Parameters in a table at the top of the spec
2298 errFile = sys.stderr,
2299 warnFile = sys.stderr,
2300 diagFile = sys.stdout):
2301 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2303 threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''}
2305 def makeParameterName(self, name):
2306 return 'pname:' + name
2308 def makeFLink(self, name):
2309 return 'flink:' + name
2312 # Generate an include file
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):
2319 if self.threadsafety['parameters'] is not None:
2321 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt'
2322 self.logMsg('diag', '# Generating include file:', filename)
2323 fp = open(filename, 'w')
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)
2333 if self.threadsafety['parameterlists'] is not None:
2335 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt'
2336 self.logMsg('diag', '# Generating include file:', filename)
2337 fp = open(filename, 'w')
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)
2347 if self.threadsafety['implicit'] is not None:
2349 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt'
2350 self.logMsg('diag', '# Generating include file:', filename)
2351 fp = open(filename, 'w')
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)
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
2368 # Check if the parameter passed in is a pointer
2369 def paramIsPointer(self, param):
2371 paramtype = param.find('type')
2372 if paramtype.tail is not None and '*' in paramtype.tail:
2377 # Turn the "name[].member[]" notation into plain English.
2378 def makeThreadDereferenceHumanReadable(self, dereference):
2379 matches = re.findall(r"[\w]+[^\w]*",dereference)
2381 for match in reversed(matches):
2382 if '->' in match or '.' in match:
2383 stringval += 'member of '
2385 stringval += 'each element of '
2388 stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0])
2391 stringval += 'parameter'
2393 return stringval[0].upper() + stringval[1:]
2395 def makeThreadSafetyBlocks(self, cmd, paramtext):
2396 protoname = cmd.find('proto/name').text
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(','):
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 '
2413 tempstring += 'The '
2415 tempstring += self.makeParameterName(paramname.text)
2416 tempstring += ' parameter'
2419 tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib)
2421 tempstring += ' in '
2422 tempstring += self.makeFLink(protoname)
2426 if ' element of ' in tempstring:
2427 self.threadsafety['parameterlists'] += tempstring
2429 self.threadsafety['parameters'] += tempstring
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'
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)
2450 self.threadsafety['implicit'] += '\n'
2453 # Command generation
2454 def genCmd(self, cmdinfo, name):
2455 OutputGenerator.genCmd(self, cmdinfo, name)
2457 # Get all thh parameters
2458 params = cmdinfo.elem.findall('param')
2459 usages = cmdinfo.elem.findall('validity/usage')
2461 self.makeThreadSafetyBlocks(cmdinfo.elem, 'param')
2465 # ThreadOutputGenerator - subclass of OutputGenerator.
2466 # Generates Thread checking framework
2469 # ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
2470 # OutputGenerator. Defines additional internal state.
2471 # ---- methods overriding base class ----
2472 # beginFile(genOpts)
2474 # beginFeature(interface, emit)
2476 # genType(typeinfo,name)
2477 # genStruct(typeinfo,name)
2478 # genGroup(groupinfo,name)
2479 # genEnum(enuminfo, name)
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']
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 = []
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
2500 # Check if the parameter passed in is a pointer
2501 def paramIsPointer(self, 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)
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:
2512 # write('is pointer', file=sys.stderr)
2514 def makeThreadUseBlock(self, cmd, functionprefix):
2515 """Generate C function pointer typedef for <command> Element"""
2517 thread_check_dispatchable_objects = [
2523 thread_check_nondispatchable_objects = [
2528 "VkDescriptorSetLayout",
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'
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'
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)
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++)\n'
2577 element = element.replace('[]','[index2]')
2578 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n'
2581 # externsync can list members to synchronize
2582 for member in externsync.split(","):
2583 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
2585 paramtype = param.find('type')
2586 if paramtype is not None:
2587 paramtype = paramtype.text
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'
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
2611 paramdecl += paramname.text
2613 paramdecl += externsyncattrib
2614 paramdecl += ' must be externally synchronized\n'
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:
2621 paramdecl += elem.text
2622 paramdecl += ' must be externally synchronized between host accesses\n'
2624 if (paramdecl == ''):
2628 def beginFile(self, genOpts):
2629 OutputGenerator.beginFile(self, genOpts)
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)
2638 write('namespace threading {', file=self.outFile)
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)
2647 # Finish C++ namespace and multiple inclusion protection
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)
2655 write('} // namespace threading', file=self.outFile)
2656 if (self.genOpts.protectFile and self.genOpts.filename):
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)
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):
2673 # Actually write the interface to the output file.
2674 #write('// starting endFeature', file=self.outFile)
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]
2690 write('\n'.join(contents), file=self.outFile)
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)
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)
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)
2710 def genType(self, typeinfo, name):
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
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)
2727 body += '} ' + typeName + ';\n'
2728 self.appendSection('struct', body)
2730 # Group (e.g. C "enum" type) generation.
2731 # These are concatenated together with other types.
2732 def genGroup(self, groupinfo, groupName):
2734 # Enumerant generation
2735 # <enum> tags may specify their values in several ways, but are usually
2737 def genEnum(self, enuminfo, name):
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',
2748 if name in interface_functions:
2750 special_functions = [
2751 'vkGetDeviceProcAddr',
2752 'vkGetInstanceProcAddr',
2756 'vkDestroyInstance',
2757 'vkAllocateCommandBuffers',
2758 'vkFreeCommandBuffers',
2759 'vkCreateDebugReportCallbackEXT',
2760 'vkDestroyDebugReportCallbackEXT',
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:]) ]
2770 self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
2772 if ("DebugMarker" in name) and ("EXT" in name):
2773 self.appendSection('command', '// TODO - not wrapping EXT function ' + name)
2775 # Determine first if this function needs to be intercepted
2776 startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
2777 if startthreadsafety is None:
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' ]
2787 OutputGenerator.genCmd(self, cmdinfo, name)
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;')
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'):
2807 if (resulttype != None):
2808 self.appendSection('command', ' ' + resulttype.text + ' result;')
2809 assignresult = 'result = '
2813 self.appendSection('command', ' bool threadChecks = startMultiThread();')
2814 self.appendSection('command', ' if (threadChecks) {')
2815 self.appendSection('command', " "+"\n ".join(str(startthreadsafety).rstrip().split("\n")))
2816 self.appendSection('command', ' }')
2817 params = cmdinfo.elem.findall('param/name')
2818 paramstext = ','.join([str(param.text) for param in params])
2819 API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
2820 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');')
2821 self.appendSection('command', ' if (threadChecks) {')
2822 self.appendSection('command', " "+"\n ".join(str(finishthreadsafety).rstrip().split("\n")))
2823 self.appendSection('command', ' } else {')
2824 self.appendSection('command', ' finishMultiThread();')
2825 self.appendSection('command', ' }')
2826 # Return result variable, if any.
2827 if (resulttype != None):
2828 self.appendSection('command', ' return result;')
2829 self.appendSection('command', '}')
2831 # override makeProtoName to drop the "vk" prefix
2832 def makeProtoName(self, name, tail):
2833 return self.genOpts.apientry + name[2:] + tail
2835 # ParamCheckerOutputGenerator - subclass of OutputGenerator.
2836 # Generates param checker layer code.
2839 # ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
2840 # OutputGenerator. Defines additional internal state.
2841 # ---- methods overriding base class ----
2842 # beginFile(genOpts)
2844 # beginFeature(interface, emit)
2846 # genType(typeinfo,name)
2847 # genStruct(typeinfo,name)
2848 # genGroup(groupinfo,name)
2849 # genEnum(enuminfo, name)
2851 class ParamCheckerOutputGenerator(OutputGenerator):
2852 """Generate ParamChecker code based on XML element attributes"""
2853 # This is an ordered list of sections in the header file.
2854 ALL_SECTIONS = ['command']
2856 errFile = sys.stderr,
2857 warnFile = sys.stderr,
2858 diagFile = sys.stdout):
2859 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2860 self.INDENT_SPACES = 4
2861 # Commands to ignore
2863 'vkGetInstanceProcAddr',
2864 'vkGetDeviceProcAddr',
2865 'vkEnumerateInstanceLayerProperties',
2866 'vkEnumerateInstanceExtensionsProperties',
2867 'vkEnumerateDeviceLayerProperties',
2868 'vkEnumerateDeviceExtensionsProperties',
2869 'vkCreateDebugReportCallbackEXT',
2870 'vkDebugReportMessageEXT']
2871 # Validation conditions for some special case struct members that are conditionally validated
2872 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
2874 self.headerVersion = None
2875 # Internal state - accumulators for different inner block text
2876 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2877 self.structNames = [] # List of Vulkan struct typenames
2878 self.stypes = [] # Values from the VkStructureType enumeration
2879 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
2880 self.handleTypes = set() # Set of handle type names
2881 self.commands = [] # List of CommandData records for all Vulkan commands
2882 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
2883 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
2884 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
2885 self.flags = set() # Map of flags typenames
2886 self.flagBits = dict() # Map of flag bits typename to list of values
2887 # Named tuples to store struct and command data
2888 self.StructType = namedtuple('StructType', ['name', 'value'])
2889 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
2890 'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
2891 'condition', 'cdecl'])
2892 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
2893 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
2895 def incIndent(self, indent):
2896 inc = ' ' * self.INDENT_SPACES
2901 def decIndent(self, indent):
2902 if indent and (len(indent) > self.INDENT_SPACES):
2903 return indent[:-self.INDENT_SPACES]
2906 def beginFile(self, genOpts):
2907 OutputGenerator.beginFile(self, genOpts)
2910 # User-supplied prefix text, if any (list of strings)
2911 if (genOpts.prefixText):
2912 for s in genOpts.prefixText:
2913 write(s, file=self.outFile)
2915 # Multiple inclusion protection & C++ wrappers.
2916 if (genOpts.protectFile and self.genOpts.filename):
2917 headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
2918 write('#ifndef', headerSym, file=self.outFile)
2919 write('#define', headerSym, '1', file=self.outFile)
2923 write('#include <string>', file=self.outFile)
2925 write('#include "vulkan/vulkan.h"', file=self.outFile)
2926 write('#include "vk_layer_extension_utils.h"', file=self.outFile)
2927 write('#include "parameter_validation_utils.h"', file=self.outFile)
2931 write('#ifndef UNUSED_PARAMETER', file=self.outFile)
2932 write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
2933 write('#endif // UNUSED_PARAMETER', file=self.outFile)
2937 write('namespace parameter_validation {', file = self.outFile)
2942 write('} // namespace parameter_validation', file = self.outFile)
2943 # Finish C++ wrapper and multiple inclusion protection
2944 if (self.genOpts.protectFile and self.genOpts.filename):
2946 write('#endif', file=self.outFile)
2947 # Finish processing in superclass
2948 OutputGenerator.endFile(self)
2949 def beginFeature(self, interface, emit):
2950 # Start processing in superclass
2951 OutputGenerator.beginFeature(self, interface, emit)
2953 # Accumulate includes, defines, types, enums, function pointer typedefs,
2954 # end function prototypes separately for this feature. They're only
2955 # printed in endFeature().
2956 self.headerVersion = None
2957 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2958 self.structNames = []
2960 self.structTypes = dict()
2961 self.handleTypes = set()
2963 self.structMembers = []
2964 self.validatedStructs = dict()
2965 self.enumRanges = dict()
2967 self.flagBits = dict()
2968 def endFeature(self):
2970 # Actually write the interface to the output file.
2973 # If type declarations are needed by other features based on
2974 # this one, it may be necessary to suppress the ExtraProtect,
2975 # or move it below the 'for section...' loop.
2976 if (self.featureExtraProtect != None):
2977 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2978 # Generate the struct member checking code from the captured data
2979 self.processStructMemberData()
2980 # Generate the command parameter checking code from the captured data
2981 self.processCmdData()
2982 # Write the declaration for the HeaderVersion
2983 if self.headerVersion:
2984 write('const uint32_t GeneratedHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
2986 # Write the declarations for the VkFlags values combining all flag bits
2987 for flag in sorted(self.flags):
2988 flagBits = flag.replace('Flags', 'FlagBits')
2989 if flagBits in self.flagBits:
2990 bits = self.flagBits[flagBits]
2991 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
2992 for bit in bits[1:]:
2995 write(decl, file=self.outFile)
2997 # Write the parameter validation code to the file
2998 if (self.sections['command']):
2999 if (self.genOpts.protectProto):
3000 write(self.genOpts.protectProto,
3001 self.genOpts.protectProtoStr, file=self.outFile)
3002 write('\n'.join(self.sections['command']), end='', file=self.outFile)
3003 if (self.featureExtraProtect != None):
3004 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
3007 # Finish processing in superclass
3008 OutputGenerator.endFeature(self)
3010 # Append a definition to the specified section
3011 def appendSection(self, section, text):
3012 # self.sections[section].append('SECTION: ' + section + '\n')
3013 self.sections[section].append(text)
3016 def genType(self, typeinfo, name):
3017 OutputGenerator.genType(self, typeinfo, name)
3018 typeElem = typeinfo.elem
3019 # If the type is a struct type, traverse the imbedded <member> tags
3020 # generating a structure. Otherwise, emit the tag text.
3021 category = typeElem.get('category')
3022 if (category == 'struct' or category == 'union'):
3023 self.structNames.append(name)
3024 self.genStruct(typeinfo, name)
3025 elif (category == 'handle'):
3026 self.handleTypes.add(name)
3027 elif (category == 'bitmask'):
3028 self.flags.add(name)
3029 elif (category == 'define'):
3030 if name == 'VK_HEADER_VERSION':
3031 nameElem = typeElem.find('name')
3032 self.headerVersion = noneStr(nameElem.tail).strip()
3034 # Struct parameter check generation.
3035 # This is a special case of the <type> tag where the contents are
3036 # interpreted as a set of <member> tags instead of freeform C
3037 # C type declarations. The <member> tags are just like <param>
3038 # tags - they are a declaration of a struct or union member.
3039 # Only simple member declarations are supported (no nested
3041 def genStruct(self, typeinfo, typeName):
3042 OutputGenerator.genStruct(self, typeinfo, typeName)
3043 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
3044 members = typeinfo.elem.findall('.//member')
3046 # Iterate over members once to get length parameters for arrays
3048 for member in members:
3049 len = self.getLen(member)
3053 # Generate member info
3055 for member in members:
3056 # Get the member's type and name
3057 info = self.getTypeNameTuple(member)
3061 cdecl = self.makeCParamDecl(member, 0)
3062 # Process VkStructureType
3063 if type == 'VkStructureType':
3064 # Extract the required struct type value from the comments
3065 # embedded in the original text defining the 'typeinfo' element
3066 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
3067 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
3069 value = result.group(0)
3071 value = self.genVkStructureType(typeName)
3072 # Store the required type value
3073 self.structTypes[typeName] = self.StructType(name=name, value=value)
3075 # Store pointer/array/string info
3076 # Check for parameter name in lens set
3080 # The pNext members are not tagged as optional, but are treated as
3081 # optional for parameter NULL checks. Static array members
3082 # are also treated as optional to skip NULL pointer validation, as
3083 # they won't be NULL.
3084 isstaticarray = self.paramIsStaticArray(member)
3086 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
3088 membersInfo.append(self.CommandParam(type=type, name=name,
3089 ispointer=self.paramIsPointer(member),
3090 isstaticarray=isstaticarray,
3091 isbool=True if type == 'VkBool32' else False,
3092 israngedenum=True if type in self.enumRanges else False,
3093 isconst=True if 'const' in cdecl else False,
3094 isoptional=isoptional,
3096 noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
3097 len=self.getLen(member),
3098 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
3099 condition=conditions[name] if conditions and name in conditions else None,
3101 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
3103 # Capture group (e.g. C "enum" type) info to be used for
3104 # param check code generation.
3105 # These are concatenated together with other types.
3106 def genGroup(self, groupinfo, groupName):
3107 OutputGenerator.genGroup(self, groupinfo, groupName)
3108 groupElem = groupinfo.elem
3110 # Store the sType values
3111 if groupName == 'VkStructureType':
3112 for elem in groupElem.findall('enum'):
3113 self.stypes.append(elem.get('name'))
3114 elif 'FlagBits' in groupName:
3116 for elem in groupElem.findall('enum'):
3117 bits.append(elem.get('name'))
3119 self.flagBits[groupName] = bits
3121 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
3122 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
3123 expandPrefix = expandName
3125 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
3126 if expandSuffixMatch:
3127 expandSuffix = '_' + expandSuffixMatch.group()
3128 # Strip off the suffix from the prefix
3129 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
3130 isEnum = ('FLAG_BITS' not in expandPrefix)
3132 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
3134 # Capture command parameter info to be used for param
3135 # check code generation.
3136 def genCmd(self, cmdinfo, name):
3137 OutputGenerator.genCmd(self, cmdinfo, name)
3138 if name not in self.blacklist:
3139 params = cmdinfo.elem.findall('param')
3140 # Get list of array lengths
3142 for param in params:
3143 len = self.getLen(param)
3148 for param in params:
3149 paramInfo = self.getTypeNameTuple(param)
3150 cdecl = self.makeCParamDecl(param, 0)
3151 # Check for parameter name in lens set
3153 if paramInfo[1] in lens:
3155 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
3156 ispointer=self.paramIsPointer(param),
3157 isstaticarray=self.paramIsStaticArray(param),
3158 isbool=True if paramInfo[0] == 'VkBool32' else False,
3159 israngedenum=True if paramInfo[0] in self.enumRanges else False,
3160 isconst=True if 'const' in cdecl else False,
3161 isoptional=self.paramIsOptional(param),
3163 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
3164 len=self.getLen(param),
3168 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
3170 # Check if the parameter passed in is a pointer
3171 def paramIsPointer(self, param):
3173 paramtype = param.find('type')
3174 if (paramtype.tail is not None) and ('*' in paramtype.tail):
3175 ispointer = paramtype.tail.count('*')
3176 elif paramtype.text[:4] == 'PFN_':
3177 # Treat function pointer typedefs as a pointer to a single value
3181 # Check if the parameter passed in is a static array
3182 def paramIsStaticArray(self, param):
3184 paramname = param.find('name')
3185 if (paramname.tail is not None) and ('[' in paramname.tail):
3186 isstaticarray = paramname.tail.count('[')
3187 return isstaticarray
3189 # Check if the parameter passed in is optional
3190 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
3191 def paramIsOptional(self, param):
3192 # See if the handle is optional
3194 # Simple, if it's optional, return true
3195 optString = param.attrib.get('optional')
3197 if optString == 'true':
3199 elif ',' in optString:
3201 for opt in optString.split(','):
3205 elif val == 'false':
3208 print('Unrecognized len attribute value',val)
3212 # Check if the handle passed in is optional
3213 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
3214 def isHandleOptional(self, param, lenParam):
3215 # Simple, if it's optional, return true
3216 if param.isoptional:
3218 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
3219 if param.noautovalidity:
3221 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
3222 if lenParam and lenParam.isoptional:
3226 # Generate a VkStructureType based on a structure typename
3227 def genVkStructureType(self, typename):
3228 # Add underscore between lowercase then uppercase
3229 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
3230 # Change to uppercase
3231 value = value.upper()
3232 # Add STRUCTURE_TYPE_
3233 return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
3235 # Get the cached VkStructureType value for the specified struct typename, or generate a VkStructureType
3236 # value assuming the struct is defined by a different feature
3237 def getStructType(self, typename):
3239 if typename in self.structTypes:
3240 value = self.structTypes[typename].value
3242 value = self.genVkStructureType(typename)
3243 self.logMsg('diag', 'ParameterValidation: Generating {} for {} structure type that was not defined by the current feature'.format(value, typename))
3246 # Retrieve the value of the len tag
3247 def getLen(self, param):
3249 len = param.attrib.get('len')
3250 if len and len != 'null-terminated':
3251 # For string arrays, 'len' can look like 'count,null-terminated',
3252 # indicating that we have a null terminated array of strings. We
3253 # strip the null-terminated from the 'len' field and only return
3254 # the parameter specifying the string count
3255 if 'null-terminated' in len:
3256 result = len.split(',')[0]
3261 # Retrieve the type and name for a parameter
3262 def getTypeNameTuple(self, param):
3266 if elem.tag == 'type':
3267 type = noneStr(elem.text)
3268 elif elem.tag == 'name':
3269 name = noneStr(elem.text)
3272 # Find a named parameter in a parameter list
3273 def getParamByName(self, params, name):
3274 for param in params:
3275 if param.name == name:
3279 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
3280 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
3281 def parseLateXMath(self, source):
3283 decoratedName = 'ERROR'
3284 if 'mathit' in source:
3285 # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
3286 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)
3287 if not match or match.group(1) != match.group(4):
3288 raise 'Unrecognized latexmath expression'
3289 name = match.group(2)
3290 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
3292 # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
3293 match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
3294 name = match.group(1)
3295 decoratedName = '{}/{}'.format(*match.group(1, 2))
3296 return name, decoratedName
3298 # Get the length paramater record for the specified parameter name
3299 def getLenParam(self, params, name):
3303 # The count is obtained by dereferencing a member of a struct parameter
3304 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
3305 isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
3306 condition=None, cdecl=None)
3307 elif 'latexmath' in name:
3308 lenName, decoratedName = self.parseLateXMath(name)
3309 lenParam = self.getParamByName(params, lenName)
3310 # TODO: Zero-check the result produced by the equation?
3311 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
3312 #param = self.getParamByName(params, lenName)
3313 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
3314 # isoptional=param.isoptional, type=param.type, len=param.len,
3315 # isstaticarray=param.isstaticarray, extstructs=param.extstructs,
3316 # noautovalidity=True, condition=None, cdecl=param.cdecl)
3318 lenParam = self.getParamByName(params, name)
3321 # Convert a vulkan.h command declaration into a parameter_validation.h definition
3322 def getCmdDef(self, cmd):
3324 # Strip the trailing ';' and split into individual lines
3325 lines = cmd.cdecl[:-1].split('\n')
3326 # Replace Vulkan prototype
3327 lines[0] = 'static bool parameter_validation_' + cmd.name + '('
3328 # Replace the first argument with debug_report_data, when the first
3329 # argument is a handle (not vkCreateInstance)
3330 reportData = ' debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
3331 if cmd.name != 'vkCreateInstance':
3332 lines[1] = reportData
3334 lines.insert(1, reportData)
3335 return '\n'.join(lines)
3337 # Generate the code to check for a NULL dereference before calling the
3338 # validation function
3339 def genCheckedLengthCall(self, name, exprs):
3340 count = name.count('->')
3344 elements = name.split('->')
3345 # Open the if expression blocks
3346 for i in range(0, count):
3347 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
3348 localIndent = self.incIndent(localIndent)
3349 # Add the validation expression
3351 checkedExpr.append(localIndent + expr)
3352 # Close the if blocks
3353 for i in range(0, count):
3354 localIndent = self.decIndent(localIndent)
3355 checkedExpr.append(localIndent + '}\n')
3356 return [checkedExpr]
3357 # No if statements were required
3360 # Generate code to check for a specific condition before executing validation code
3361 def genConditionalCall(self, prefix, condition, exprs):
3364 formattedCondition = condition.format(prefix)
3365 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
3366 checkedExpr.append(localIndent + '{\n')
3367 localIndent = self.incIndent(localIndent)
3369 checkedExpr.append(localIndent + expr)
3370 localIndent = self.decIndent(localIndent)
3371 checkedExpr.append(localIndent + '}\n')
3372 return [checkedExpr]
3374 # Generate the sType check string
3375 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3377 stype = self.structTypes[value.type]
3379 # This is an array with a pointer to a count value
3380 if lenValue.ispointer:
3381 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
3382 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
3383 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
3384 # This is an array with an integer count value
3386 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
3387 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
3388 # This is an individual struct
3390 checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {});\n'.format(
3391 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value, **postProcSpec))
3394 # Generate the handle check string
3395 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3398 if lenValue.ispointer:
3399 # This is assumed to be an output array with a pointer to a count value
3400 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
3402 # This is an array with an integer count value
3403 checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3404 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
3406 # This is assumed to be an output handle pointer
3407 raise('Unsupported parameter validation case: Output handles are not NULL checked')
3410 # Generate check string for an array of VkFlags values
3411 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3413 flagBitsName = value.type.replace('Flags', 'FlagBits')
3414 if not flagBitsName in self.flagBits:
3415 raise('Unsupported parameter validation case: array of reserved VkFlags')
3417 allFlags = 'All' + flagBitsName
3418 checkExpr.append('skipCall |= validate_flags_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix, **postProcSpec))
3421 # Generate pNext check string
3422 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec):
3424 # Generate an array of acceptable VkStructureType values for pNext
3426 extStructVar = 'NULL'
3427 extStructNames = 'NULL'
3428 if value.extstructs:
3429 structs = value.extstructs.split(',')
3430 checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.getStructType(s) for s in structs]) + '};\n')
3431 extStructCount = 'ARRAY_SIZE(allowedStructs)'
3432 extStructVar = 'allowedStructs'
3433 extStructNames = '"' + ', '.join(structs) + '"'
3434 checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion);\n'.format(
3435 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, **postProcSpec))
3438 # Generate the pointer check string
3439 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3442 # This is an array with a pointer to a count value
3443 if lenValue.ispointer:
3444 # If count and array parameters are optional, there will be no validation
3445 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
3446 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
3447 checkExpr.append('skipCall |= validate_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
3448 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
3449 # This is an array with an integer count value
3451 # If count and array parameters are optional, there will be no validation
3452 if valueRequired == 'true' or lenValueRequired == 'true':
3453 # Arrays of strings receive special processing
3454 validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
3455 checkExpr.append('skipCall |= {}(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3456 validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
3458 if lenValue and ('->' in lenValue.name):
3459 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
3460 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
3461 # This is an individual struct that is not allowed to be NULL
3462 elif not value.isoptional:
3463 # Function pointers need a reinterpret_cast to void*
3464 if value.type[:4] == 'PFN_':
3465 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
3467 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
3470 # Process struct member validation code, performing name suibstitution if required
3471 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
3472 # Build format specifier list
3474 if '{postProcPrefix}' in line:
3475 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3476 if type(memberDisplayNamePrefix) is tuple:
3477 kwargs['postProcPrefix'] = 'ParameterName('
3479 kwargs['postProcPrefix'] = postProcSpec['ppp']
3480 if '{postProcSuffix}' in line:
3481 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3482 if type(memberDisplayNamePrefix) is tuple:
3483 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3485 kwargs['postProcSuffix'] = postProcSpec['pps']
3486 if '{postProcInsert}' in line:
3487 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3488 if type(memberDisplayNamePrefix) is tuple:
3489 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3491 kwargs['postProcInsert'] = postProcSpec['ppi']
3492 if '{funcName}' in line:
3493 kwargs['funcName'] = funcName
3494 if '{valuePrefix}' in line:
3495 kwargs['valuePrefix'] = memberNamePrefix
3496 if '{displayNamePrefix}' in line:
3497 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
3498 if type(memberDisplayNamePrefix) is tuple:
3499 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
3501 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
3504 # Need to escape the C++ curly braces
3505 if 'IndexVector' in line:
3506 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
3507 line = line.replace(' }),', ' }}),')
3508 return line.format(**kwargs)
3511 # Process struct validation code for inclusion in function or parent struct validation code
3512 def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
3516 if type(line) is list:
3518 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
3520 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
3523 # Process struct pointer/array validation code, perfoeming name substitution if required
3524 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
3526 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
3528 indent = self.incIndent(None)
3530 # Need to process all elements in the array
3531 indexName = lenValue.name.replace('Count', 'Index')
3533 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
3534 expr.append(indent + '{')
3535 indent = self.incIndent(indent)
3536 # Prefix for value name to display in error message
3537 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
3538 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
3540 memberNamePrefix = '{}{}->'.format(prefix, value.name)
3541 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
3543 # Expand the struct validation lines
3544 expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
3547 # Close if and for scopes
3548 indent = self.decIndent(indent)
3549 expr.append(indent + '}\n')
3553 # Generate the parameter checking code
3554 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
3555 lines = [] # Generated lines of code
3556 unused = [] # Unused variable names
3557 for value in values:
3561 # Prefix and suffix for post processing of parameter names for struct members. Arrays of structures need special processing to include the array index in the full parameter name.
3563 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
3564 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
3565 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
3567 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
3568 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
3570 # Check for NULL pointers, ignore the inout count parameters that
3571 # will be validated with their associated array
3572 if (value.ispointer or value.isstaticarray) and not value.iscount:
3574 # Parameters for function argument generation
3575 req = 'true' # Paramerter cannot be NULL
3576 cpReq = 'true' # Count pointer cannot be NULL
3577 cvReq = 'true' # Count value cannot be 0
3578 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
3580 # Generate required/optional parameter strings for the pointer and count values
3581 if value.isoptional:
3584 # The parameter is an array with an explicit count parameter
3585 lenParam = self.getLenParam(values, value.len)
3586 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
3587 if lenParam.ispointer:
3588 # Count parameters that are pointers are inout
3589 if type(lenParam.isoptional) is list:
3590 if lenParam.isoptional[0]:
3592 if lenParam.isoptional[1]:
3595 if lenParam.isoptional:
3598 if lenParam.isoptional:
3601 # The parameter will not be processes when tagged as 'noautovalidity'
3602 # For the pointer to struct case, the struct pointer will not be validated, but any
3603 # members not tagged as 'noatuvalidity' will be validated
3604 if value.noautovalidity:
3605 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3606 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
3609 # If this is a pointer to a struct with an sType field, verify the type
3610 if value.type in self.structTypes:
3611 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3612 # 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
3613 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
3614 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3615 elif value.type in self.flags and value.isconst:
3616 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3617 elif value.isbool and value.isconst:
3618 usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
3619 elif value.israngedenum and value.isconst:
3620 enumRange = self.enumRanges[value.type]
3621 usedLines.append('skipCall |= validate_ranged_enum_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
3622 elif value.name == 'pNext':
3623 # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
3624 if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
3625 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec)
3627 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3629 # If this is a pointer to a struct (input), see if it contains members that need to be checked
3630 if value.type in self.validatedStructs and value.isconst:
3631 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
3635 # The parameter will not be processes when tagged as 'noautovalidity'
3636 # For the struct case, the struct type will not be validated, but any
3637 # members not tagged as 'noatuvalidity' will be validated
3638 if value.noautovalidity:
3639 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3640 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
3642 if value.type in self.structTypes:
3643 stype = self.structTypes[value.type]
3644 usedLines.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false);\n'.format(
3645 funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value, **postProcSpec))
3646 elif value.type in self.handleTypes:
3647 if not self.isHandleOptional(value, None):
3648 usedLines.append('skipCall |= validate_required_handle(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
3649 elif value.type in self.flags:
3650 flagBitsName = value.type.replace('Flags', 'FlagBits')
3651 if not flagBitsName in self.flagBits:
3652 usedLines.append('skipCall |= validate_reserved_flags(report_data, "{}", {ppp}"{}"{pps}, {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix, **postProcSpec))
3654 flagsRequired = 'false' if value.isoptional else 'true'
3655 allFlagsName = 'All' + flagBitsName
3656 usedLines.append('skipCall |= validate_flags(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix, **postProcSpec))
3658 usedLines.append('skipCall |= validate_bool32(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
3659 elif value.israngedenum:
3660 enumRange = self.enumRanges[value.type]
3661 usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name, **postProcSpec))
3663 # If this is a struct, see if it contains members that need to be checked
3664 if value.type in self.validatedStructs:
3665 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
3666 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
3667 usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
3669 # Append the parameter check to the function body for the current command
3671 # Apply special conditional checks
3673 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
3675 elif not value.iscount:
3676 # If no expression was generated for this value, it is unreferenced by the validation function, unless
3677 # it is an array count, which is indirectly referenced for array valiadation.
3678 unused.append(value.name)
3679 return lines, unused
3681 # Generate the struct member check code from the captured data
3682 def processStructMemberData(self):
3683 indent = self.incIndent(None)
3684 for struct in self.structMembers:
3686 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
3687 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
3689 self.validatedStructs[struct.name] = lines
3691 # Generate the command param check code from the captured data
3692 def processCmdData(self):
3693 indent = self.incIndent(None)
3694 for command in self.commands:
3695 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
3696 startIndex = 0 if command.name == 'vkCreateInstance' else 1
3697 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
3699 cmdDef = self.getCmdDef(command) + '\n'
3701 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
3702 # processed by parameter_validation (except for vkCreateInstance, which does not have a
3703 # handle as its first parameter)
3706 cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
3709 cmdDef += indent + 'bool skipCall = false;\n'
3712 if type(line) is list:
3714 cmdDef += indent + sub
3716 cmdDef += indent + line
3718 cmdDef += indent + 'return skipCall;\n'
3720 self.appendSection('command', cmdDef)