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