blob: a020d4d470fee324677f64515d498d38ac90fd96 [file] [log] [blame]
Mike Stroyandee76ef2016-01-07 15:35:37 -07001#!/usr/bin/python3 -i
Dustin Graves3ff520c2016-03-28 16:17:38 -06002#
3# Copyright (c) 2013-2016 The Khronos Group Inc.
4#
Jon Ashburn3ebf1252016-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 Graves3ff520c2016-03-28 16:17:38 -06008#
Jon Ashburn3ebf1252016-04-19 11:30:31 -06009# http://www.apache.org/licenses/LICENSE-2.0
Dustin Graves3ff520c2016-03-28 16:17:38 -060010#
Jon Ashburn3ebf1252016-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 Graves3ff520c2016-03-28 16:17:38 -060016
Mike Stroyandee76ef2016-01-07 15:35:37 -070017import os,re,sys
Dustin Gravesdfa6acf2016-02-11 10:10:14 -070018from collections import namedtuple
Mike Stroyan3c5a6e22016-04-05 16:40:30 -060019import xml.etree.ElementTree as etree
Mike Stroyandee76ef2016-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 Stroyan3c5a6e22016-04-05 16:40:30 -060028# Used in converting etree Elements into text.
Mike Stroyandee76ef2016-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 Stroyan845bdc42015-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,
Jamie Madille6f08932016-05-04 08:17:33 -0700325 alignFuncParam = 0,
326 genDirectory = None):
Mike Stroyan845bdc42015-11-02 15:30:20 -0700327 GeneratorOptions.__init__(self, filename, apiname, profile,
328 versions, emitversions, defaultExtensions,
329 addExtensions, removeExtensions, sortProcedure)
330 self.prefixText = prefixText
331 self.genFuncPointers = genFuncPointers
332 self.protectFile = protectFile
333 self.protectFeature = protectFeature
334 self.protectProto = protectProto
335 self.protectProtoStr = protectProtoStr
336 self.apicall = apicall
337 self.apientry = apientry
338 self.apientryp = apientryp
339 self.indentFuncProto = indentFuncProto
340 self.indentFuncPointer = indentFuncPointer
341 self.alignFuncParam = alignFuncParam
Jamie Madille6f08932016-05-04 08:17:33 -0700342 self.genDirectory = genDirectory
Mike Stroyan845bdc42015-11-02 15:30:20 -0700343
344
Dustin Gravesdfa6acf2016-02-11 10:10:14 -0700345# ParamCheckerGeneratorOptions - subclass of GeneratorOptions.
346#
Mark Lobodzinski739391a2016-03-17 15:08:18 -0600347# Adds options used by ParamCheckerOutputGenerator objects during parameter validation
Dustin Gravesdfa6acf2016-02-11 10:10:14 -0700348# generation.
349#
350# Additional members
351# prefixText - list of strings to prefix generated header with
352# (usually a copyright statement + calling convention macros).
353# protectFile - True if multiple inclusion protection should be
354# generated (based on the filename) around the entire header.
355# protectFeature - True if #ifndef..#endif protection should be
356# generated around a feature interface in the header file.
357# genFuncPointers - True if function pointer typedefs should be
358# generated
359# protectProto - If conditional protection should be generated
360# around prototype declarations, set to either '#ifdef'
361# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
362# to require opt-out (#ifndef protectProtoStr). Otherwise
363# set to None.
364# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
365# declarations, if protectProto is set
366# apicall - string to use for the function declaration prefix,
367# such as APICALL on Windows.
368# apientry - string to use for the calling convention macro,
369# in typedefs, such as APIENTRY.
370# apientryp - string to use for the calling convention macro
371# in function pointer typedefs, such as APIENTRYP.
372# indentFuncProto - True if prototype declarations should put each
373# parameter on a separate line
374# indentFuncPointer - True if typedefed function pointers should put each
375# parameter on a separate line
376# alignFuncParam - if nonzero and parameters are being put on a
377# separate line, align parameter names at the specified column
378class ParamCheckerGeneratorOptions(GeneratorOptions):
379 """Represents options during C interface generation for headers"""
380 def __init__(self,
381 filename = None,
382 apiname = None,
383 profile = None,
384 versions = '.*',
385 emitversions = '.*',
386 defaultExtensions = None,
387 addExtensions = None,
388 removeExtensions = None,
389 sortProcedure = regSortFeatures,
390 prefixText = "",
391 genFuncPointers = True,
392 protectFile = True,
393 protectFeature = True,
394 protectProto = None,
395 protectProtoStr = None,
396 apicall = '',
397 apientry = '',
398 apientryp = '',
399 indentFuncProto = True,
400 indentFuncPointer = False,
Jamie Madille6f08932016-05-04 08:17:33 -0700401 alignFuncParam = 0,
402 genDirectory = None):
Dustin Gravesdfa6acf2016-02-11 10:10:14 -0700403 GeneratorOptions.__init__(self, filename, apiname, profile,
404 versions, emitversions, defaultExtensions,
405 addExtensions, removeExtensions, sortProcedure)
406 self.prefixText = prefixText
407 self.genFuncPointers = genFuncPointers
408 self.protectFile = protectFile
409 self.protectFeature = protectFeature
410 self.protectProto = protectProto
411 self.protectProtoStr = protectProtoStr
412 self.apicall = apicall
413 self.apientry = apientry
414 self.apientryp = apientryp
415 self.indentFuncProto = indentFuncProto
416 self.indentFuncPointer = indentFuncPointer
417 self.alignFuncParam = alignFuncParam
Jamie Madille6f08932016-05-04 08:17:33 -0700418 self.genDirectory = genDirectory
Dustin Gravesdfa6acf2016-02-11 10:10:14 -0700419
420
Mike Stroyandee76ef2016-01-07 15:35:37 -0700421# OutputGenerator - base class for generating API interfaces.
422# Manages basic logic, logging, and output file control
423# Derived classes actually generate formatted output.
424#
425# ---- methods ----
426# OutputGenerator(errFile, warnFile, diagFile)
427# errFile, warnFile, diagFile - file handles to write errors,
428# warnings, diagnostics to. May be None to not write.
429# logMsg(level, *args) - log messages of different categories
430# level - 'error', 'warn', or 'diag'. 'error' will also
431# raise a UserWarning exception
432# *args - print()-style arguments
433# setExtMap(map) - specify a dictionary map from extension names to
434# numbers, used in creating values for extension enumerants.
435# beginFile(genOpts) - start a new interface file
436# genOpts - GeneratorOptions controlling what's generated and how
437# endFile() - finish an interface file, closing it when done
438# beginFeature(interface, emit) - write interface for a feature
439# and tag generated features as having been done.
440# interface - element for the <version> / <extension> to generate
441# emit - actually write to the header only when True
442# endFeature() - finish an interface.
443# genType(typeinfo,name) - generate interface for a type
444# typeinfo - TypeInfo for a type
445# genStruct(typeinfo,name) - generate interface for a C "struct" type.
446# typeinfo - TypeInfo for a type interpreted as a struct
447# genGroup(groupinfo,name) - generate interface for a group of enums (C "enum")
448# groupinfo - GroupInfo for a group
449# genEnum(enuminfo, name) - generate interface for an enum (constant)
450# enuminfo - EnumInfo for an enum
451# name - enum name
452# genCmd(cmdinfo) - generate interface for a command
453# cmdinfo - CmdInfo for a command
454# makeCDecls(cmd) - return C prototype and function pointer typedef for a
455# <command> Element, as a list of two strings
456# cmd - Element for the <command>
457# newline() - print a newline to the output file (utility function)
458#
459class OutputGenerator:
460 """Generate specified API interfaces in a specific style, such as a C header"""
461 def __init__(self,
462 errFile = sys.stderr,
463 warnFile = sys.stderr,
464 diagFile = sys.stdout):
465 self.outFile = None
466 self.errFile = errFile
467 self.warnFile = warnFile
468 self.diagFile = diagFile
469 # Internal state
470 self.featureName = None
471 self.genOpts = None
472 self.registry = None
473 # Used for extension enum value generation
474 self.extBase = 1000000000
475 self.extBlockSize = 1000
476 #
477 # logMsg - write a message of different categories to different
478 # destinations.
479 # level -
480 # 'diag' (diagnostic, voluminous)
481 # 'warn' (warning)
482 # 'error' (fatal error - raises exception after logging)
483 # *args - print()-style arguments to direct to corresponding log
484 def logMsg(self, level, *args):
485 """Log a message at the given level. Can be ignored or log to a file"""
486 if (level == 'error'):
487 strfile = io.StringIO()
488 write('ERROR:', *args, file=strfile)
489 if (self.errFile != None):
490 write(strfile.getvalue(), file=self.errFile)
491 raise UserWarning(strfile.getvalue())
492 elif (level == 'warn'):
493 if (self.warnFile != None):
494 write('WARNING:', *args, file=self.warnFile)
495 elif (level == 'diag'):
496 if (self.diagFile != None):
497 write('DIAG:', *args, file=self.diagFile)
498 else:
499 raise UserWarning(
500 '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
501 #
502 # enumToValue - parses and converts an <enum> tag into a value.
503 # Returns a list
504 # first element - integer representation of the value, or None
505 # if needsNum is False. The value must be a legal number
506 # if needsNum is True.
507 # second element - string representation of the value
508 # There are several possible representations of values.
509 # A 'value' attribute simply contains the value.
510 # A 'bitpos' attribute defines a value by specifying the bit
511 # position which is set in that value.
512 # A 'offset','extbase','extends' triplet specifies a value
513 # as an offset to a base value defined by the specified
514 # 'extbase' extension name, which is then cast to the
515 # typename specified by 'extends'. This requires probing
516 # the registry database, and imbeds knowledge of the
517 # Vulkan extension enum scheme in this function.
518 def enumToValue(self, elem, needsNum):
519 name = elem.get('name')
520 numVal = None
521 if ('value' in elem.keys()):
522 value = elem.get('value')
523 # print('About to translate value =', value, 'type =', type(value))
524 if (needsNum):
525 numVal = int(value, 0)
526 # If there's a non-integer, numeric 'type' attribute (e.g. 'u' or
527 # 'ull'), append it to the string value.
528 # t = enuminfo.elem.get('type')
529 # if (t != None and t != '' and t != 'i' and t != 's'):
530 # value += enuminfo.type
531 self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
532 return [numVal, value]
533 if ('bitpos' in elem.keys()):
534 value = elem.get('bitpos')
535 numVal = int(value, 0)
536 numVal = 1 << numVal
537 value = '0x%08x' % numVal
538 self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
539 return [numVal, value]
540 if ('offset' in elem.keys()):
541 # Obtain values in the mapping from the attributes
542 enumNegative = False
543 offset = int(elem.get('offset'),0)
544 extnumber = int(elem.get('extnumber'),0)
545 extends = elem.get('extends')
546 if ('dir' in elem.keys()):
547 enumNegative = True
548 self.logMsg('diag', 'Enum', name, 'offset =', offset,
549 'extnumber =', extnumber, 'extends =', extends,
550 'enumNegative =', enumNegative)
551 # Now determine the actual enumerant value, as defined
552 # in the "Layers and Extensions" appendix of the spec.
553 numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
554 if (enumNegative):
555 numVal = -numVal
556 value = '%d' % numVal
557 # More logic needed!
558 self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
559 return [numVal, value]
560 return [None, None]
561 #
562 def beginFile(self, genOpts):
563 self.genOpts = genOpts
564 #
565 # Open specified output file. Not done in constructor since a
566 # Generator can be used without writing to a file.
567 if (self.genOpts.filename != None):
Jamie Madille6f08932016-05-04 08:17:33 -0700568 if (self.genOpts.genDirectory != None):
569 self.outFile = open(os.path.join(self.genOpts.genDirectory, self.genOpts.filename), 'w')
570 else:
571 self.outFile = open(self.genOpts.filename, 'w')
Mike Stroyandee76ef2016-01-07 15:35:37 -0700572 else:
573 self.outFile = sys.stdout
574 def endFile(self):
575 self.errFile and self.errFile.flush()
576 self.warnFile and self.warnFile.flush()
577 self.diagFile and self.diagFile.flush()
578 self.outFile.flush()
579 if (self.outFile != sys.stdout and self.outFile != sys.stderr):
580 self.outFile.close()
581 self.genOpts = None
582 #
583 def beginFeature(self, interface, emit):
584 self.emit = emit
585 self.featureName = interface.get('name')
586 # If there's an additional 'protect' attribute in the feature, save it
587 self.featureExtraProtect = interface.get('protect')
588 def endFeature(self):
589 # Derived classes responsible for emitting feature
590 self.featureName = None
591 self.featureExtraProtect = None
592 # Utility method to validate we're generating something only inside a
593 # <feature> tag
594 def validateFeature(self, featureType, featureName):
595 if (self.featureName == None):
596 raise UserWarning('Attempt to generate', featureType, name,
597 'when not in feature')
598 #
599 # Type generation
600 def genType(self, typeinfo, name):
601 self.validateFeature('type', name)
602 #
603 # Struct (e.g. C "struct" type) generation
604 def genStruct(self, typeinfo, name):
605 self.validateFeature('struct', name)
606 #
607 # Group (e.g. C "enum" type) generation
608 def genGroup(self, groupinfo, name):
609 self.validateFeature('group', name)
610 #
611 # Enumerant (really, constant) generation
612 def genEnum(self, enuminfo, name):
613 self.validateFeature('enum', name)
614 #
615 # Command generation
616 def genCmd(self, cmd, name):
617 self.validateFeature('command', name)
618 #
619 # Utility functions - turn a <proto> <name> into C-language prototype
620 # and typedef declarations for that name.
621 # name - contents of <name> tag
622 # tail - whatever text follows that tag in the Element
623 def makeProtoName(self, name, tail):
624 return self.genOpts.apientry + name + tail
625 def makeTypedefName(self, name, tail):
626 return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')'
627 #
628 # makeCParamDecl - return a string which is an indented, formatted
629 # declaration for a <param> or <member> block (e.g. function parameter
630 # or structure/union member).
631 # param - Element (<param> or <member>) to format
632 # aligncol - if non-zero, attempt to align the nested <name> element
633 # at this column
634 def makeCParamDecl(self, param, aligncol):
635 paramdecl = ' ' + noneStr(param.text)
636 for elem in param:
637 text = noneStr(elem.text)
638 tail = noneStr(elem.tail)
639 if (elem.tag == 'name' and aligncol > 0):
640 self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam)
641 # Align at specified column, if possible
642 paramdecl = paramdecl.rstrip()
643 oldLen = len(paramdecl)
644 paramdecl = paramdecl.ljust(aligncol)
645 newLen = len(paramdecl)
646 self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl)
647 paramdecl += text + tail
648 return paramdecl
649 #
650 # getCParamTypeLength - return the length of the type field is an indented, formatted
651 # declaration for a <param> or <member> block (e.g. function parameter
652 # or structure/union member).
653 # param - Element (<param> or <member>) to identify
654 def getCParamTypeLength(self, param):
655 paramdecl = ' ' + noneStr(param.text)
656 for elem in param:
657 text = noneStr(elem.text)
658 tail = noneStr(elem.tail)
659 if (elem.tag == 'name'):
660 # Align at specified column, if possible
661 newLen = len(paramdecl.rstrip())
662 self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen)
663 paramdecl += text + tail
664 return newLen
665 #
666 # makeCDecls - return C prototype and function pointer typedef for a
667 # command, as a two-element list of strings.
668 # cmd - Element containing a <command> tag
669 def makeCDecls(self, cmd):
670 """Generate C function pointer typedef for <command> Element"""
671 proto = cmd.find('proto')
672 params = cmd.findall('param')
673 # Begin accumulating prototype and typedef strings
674 pdecl = self.genOpts.apicall
675 tdecl = 'typedef '
676 #
677 # Insert the function return type/name.
678 # For prototypes, add APIENTRY macro before the name
679 # For typedefs, add (APIENTRY *<name>) around the name and
680 # use the PFN_cmdnameproc naming convention.
681 # Done by walking the tree for <proto> element by element.
Mike Stroyan3c5a6e22016-04-05 16:40:30 -0600682 # etree has elem.text followed by (elem[i], elem[i].tail)
Mike Stroyandee76ef2016-01-07 15:35:37 -0700683 # for each child element and any following text
684 # Leading text
685 pdecl += noneStr(proto.text)
686 tdecl += noneStr(proto.text)
687 # For each child element, if it's a <name> wrap in appropriate
688 # declaration. Otherwise append its contents and tail contents.
689 for elem in proto:
690 text = noneStr(elem.text)
691 tail = noneStr(elem.tail)
692 if (elem.tag == 'name'):
693 pdecl += self.makeProtoName(text, tail)
694 tdecl += self.makeTypedefName(text, tail)
695 else:
696 pdecl += text + tail
697 tdecl += text + tail
698 # Now add the parameter declaration list, which is identical
699 # for prototypes and typedefs. Concatenate all the text from
700 # a <param> node without the tags. No tree walking required
701 # since all tags are ignored.
702 # Uses: self.indentFuncProto
703 # self.indentFuncPointer
704 # self.alignFuncParam
705 # Might be able to doubly-nest the joins, e.g.
706 # ','.join(('_'.join([l[i] for i in range(0,len(l))])
707 n = len(params)
708 # Indented parameters
709 if n > 0:
710 indentdecl = '(\n'
711 for i in range(0,n):
712 paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam)
713 if (i < n - 1):
714 paramdecl += ',\n'
715 else:
716 paramdecl += ');'
717 indentdecl += paramdecl
718 else:
719 indentdecl = '(void);'
720 # Non-indented parameters
721 paramdecl = '('
722 if n > 0:
723 for i in range(0,n):
724 paramdecl += ''.join([t for t in params[i].itertext()])
725 if (i < n - 1):
726 paramdecl += ', '
727 else:
728 paramdecl += 'void'
729 paramdecl += ");";
730 return [ pdecl + indentdecl, tdecl + paramdecl ]
731 #
732 def newline(self):
733 write('', file=self.outFile)
734
735 def setRegistry(self, registry):
736 self.registry = registry
737 #
738
739# COutputGenerator - subclass of OutputGenerator.
740# Generates C-language API interfaces.
741#
742# ---- methods ----
743# COutputGenerator(errFile, warnFile, diagFile) - args as for
744# OutputGenerator. Defines additional internal state.
745# ---- methods overriding base class ----
746# beginFile(genOpts)
747# endFile()
748# beginFeature(interface, emit)
749# endFeature()
750# genType(typeinfo,name)
751# genStruct(typeinfo,name)
752# genGroup(groupinfo,name)
753# genEnum(enuminfo, name)
754# genCmd(cmdinfo)
755class COutputGenerator(OutputGenerator):
756 """Generate specified API interfaces in a specific style, such as a C header"""
757 # This is an ordered list of sections in the header file.
758 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
759 'group', 'bitmask', 'funcpointer', 'struct']
760 ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
761 def __init__(self,
762 errFile = sys.stderr,
763 warnFile = sys.stderr,
764 diagFile = sys.stdout):
765 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
766 # Internal state - accumulators for different inner block text
767 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
768 #
769 def beginFile(self, genOpts):
770 OutputGenerator.beginFile(self, genOpts)
771 # C-specific
772 #
773 # Multiple inclusion protection & C++ wrappers.
774 if (genOpts.protectFile and self.genOpts.filename):
Dustin Graves3ff520c2016-03-28 16:17:38 -0600775 headerSym = re.sub('\.h', '_h_',
776 os.path.basename(self.genOpts.filename)).upper()
Mike Stroyandee76ef2016-01-07 15:35:37 -0700777 write('#ifndef', headerSym, file=self.outFile)
778 write('#define', headerSym, '1', file=self.outFile)
779 self.newline()
780 write('#ifdef __cplusplus', file=self.outFile)
781 write('extern "C" {', file=self.outFile)
782 write('#endif', file=self.outFile)
783 self.newline()
784 #
785 # User-supplied prefix text, if any (list of strings)
786 if (genOpts.prefixText):
787 for s in genOpts.prefixText:
788 write(s, file=self.outFile)
789 #
790 # Some boilerplate describing what was generated - this
791 # will probably be removed later since the extensions
792 # pattern may be very long.
793 # write('/* Generated C header for:', file=self.outFile)
794 # write(' * API:', genOpts.apiname, file=self.outFile)
795 # if (genOpts.profile):
796 # write(' * Profile:', genOpts.profile, file=self.outFile)
797 # write(' * Versions considered:', genOpts.versions, file=self.outFile)
798 # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile)
799 # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile)
800 # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile)
801 # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile)
802 # write(' */', file=self.outFile)
803 def endFile(self):
804 # C-specific
805 # Finish C++ wrapper and multiple inclusion protection
806 self.newline()
807 write('#ifdef __cplusplus', file=self.outFile)
808 write('}', file=self.outFile)
809 write('#endif', file=self.outFile)
810 if (self.genOpts.protectFile and self.genOpts.filename):
811 self.newline()
812 write('#endif', file=self.outFile)
813 # Finish processing in superclass
814 OutputGenerator.endFile(self)
815 def beginFeature(self, interface, emit):
816 # Start processing in superclass
817 OutputGenerator.beginFeature(self, interface, emit)
818 # C-specific
819 # Accumulate includes, defines, types, enums, function pointer typedefs,
820 # end function prototypes separately for this feature. They're only
821 # printed in endFeature().
822 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
823 def endFeature(self):
824 # C-specific
825 # Actually write the interface to the output file.
826 if (self.emit):
827 self.newline()
828 if (self.genOpts.protectFeature):
829 write('#ifndef', self.featureName, file=self.outFile)
830 # If type declarations are needed by other features based on
831 # this one, it may be necessary to suppress the ExtraProtect,
832 # or move it below the 'for section...' loop.
833 if (self.featureExtraProtect != None):
834 write('#ifdef', self.featureExtraProtect, file=self.outFile)
835 write('#define', self.featureName, '1', file=self.outFile)
836 for section in self.TYPE_SECTIONS:
837 contents = self.sections[section]
838 if contents:
839 write('\n'.join(contents), file=self.outFile)
840 self.newline()
841 if (self.genOpts.genFuncPointers and self.sections['commandPointer']):
842 write('\n'.join(self.sections['commandPointer']), file=self.outFile)
843 self.newline()
844 if (self.sections['command']):
845 if (self.genOpts.protectProto):
846 write(self.genOpts.protectProto,
847 self.genOpts.protectProtoStr, file=self.outFile)
848 write('\n'.join(self.sections['command']), end='', file=self.outFile)
849 if (self.genOpts.protectProto):
850 write('#endif', file=self.outFile)
851 else:
852 self.newline()
853 if (self.featureExtraProtect != None):
854 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
855 if (self.genOpts.protectFeature):
856 write('#endif /*', self.featureName, '*/', file=self.outFile)
857 # Finish processing in superclass
858 OutputGenerator.endFeature(self)
859 #
860 # Append a definition to the specified section
861 def appendSection(self, section, text):
862 # self.sections[section].append('SECTION: ' + section + '\n')
863 self.sections[section].append(text)
864 #
865 # Type generation
866 def genType(self, typeinfo, name):
867 OutputGenerator.genType(self, typeinfo, name)
868 typeElem = typeinfo.elem
869 # If the type is a struct type, traverse the imbedded <member> tags
870 # generating a structure. Otherwise, emit the tag text.
871 category = typeElem.get('category')
872 if (category == 'struct' or category == 'union'):
873 self.genStruct(typeinfo, name)
874 else:
875 # Replace <apientry /> tags with an APIENTRY-style string
876 # (from self.genOpts). Copy other text through unchanged.
877 # If the resulting text is an empty string, don't emit it.
878 s = noneStr(typeElem.text)
879 for elem in typeElem:
880 if (elem.tag == 'apientry'):
881 s += self.genOpts.apientry + noneStr(elem.tail)
882 else:
883 s += noneStr(elem.text) + noneStr(elem.tail)
884 if s:
885 # Add extra newline after multi-line entries.
886 if '\n' in s:
887 s += '\n'
888 self.appendSection(category, s)
889 #
890 # Struct (e.g. C "struct" type) generation.
891 # This is a special case of the <type> tag where the contents are
892 # interpreted as a set of <member> tags instead of freeform C
893 # C type declarations. The <member> tags are just like <param>
894 # tags - they are a declaration of a struct or union member.
895 # Only simple member declarations are supported (no nested
896 # structs etc.)
897 def genStruct(self, typeinfo, typeName):
898 OutputGenerator.genStruct(self, typeinfo, typeName)
899 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
900 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
901 targetLen = 0;
902 for member in typeinfo.elem.findall('.//member'):
903 targetLen = max(targetLen, self.getCParamTypeLength(member))
904 for member in typeinfo.elem.findall('.//member'):
905 body += self.makeCParamDecl(member, targetLen + 4)
906 body += ';\n'
907 body += '} ' + typeName + ';\n'
908 self.appendSection('struct', body)
909 #
910 # Group (e.g. C "enum" type) generation.
911 # These are concatenated together with other types.
912 def genGroup(self, groupinfo, groupName):
913 OutputGenerator.genGroup(self, groupinfo, groupName)
914 groupElem = groupinfo.elem
Dustin Graves3ff520c2016-03-28 16:17:38 -0600915
916 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
917
918 expandPrefix = expandName
919 expandSuffix = ''
920 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
921 if expandSuffixMatch:
922 expandSuffix = '_' + expandSuffixMatch.group()
923 # Strip off the suffix from the prefix
924 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
925
Mike Stroyandee76ef2016-01-07 15:35:37 -0700926 # Prefix
927 body = "\ntypedef enum " + groupName + " {\n"
928
Dustin Graves3ff520c2016-03-28 16:17:38 -0600929 isEnum = ('FLAG_BITS' not in expandPrefix)
930
Mike Stroyandee76ef2016-01-07 15:35:37 -0700931 # Loop over the nested 'enum' tags. Keep track of the minimum and
932 # maximum numeric values, if they can be determined; but only for
933 # core API enumerants, not extension enumerants. This is inferred
934 # by looking for 'extends' attributes.
935 minName = None
936 for elem in groupElem.findall('enum'):
937 # Convert the value to an integer and use that to track min/max.
938 # Values of form -(number) are accepted but nothing more complex.
939 # Should catch exceptions here for more complex constructs. Not yet.
940 (numVal,strVal) = self.enumToValue(elem, True)
941 name = elem.get('name')
Dustin Graves3ff520c2016-03-28 16:17:38 -0600942
943 # Extension enumerants are only included if they are requested
944 # in addExtensions or match defaultExtensions.
945 if (elem.get('extname') is None or
946 re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
947 self.genOpts.defaultExtensions == elem.get('supported')):
948 body += " " + name + " = " + strVal + ",\n"
949
950 if (isEnum and elem.get('extends') is None):
Mike Stroyandee76ef2016-01-07 15:35:37 -0700951 if (minName == None):
952 minName = maxName = name
953 minValue = maxValue = numVal
954 elif (numVal < minValue):
955 minName = name
956 minValue = numVal
957 elif (numVal > maxValue):
958 maxName = name
959 maxValue = numVal
960 # Generate min/max value tokens and a range-padding enum. Need some
961 # additional padding to generate correct names...
Dustin Graves3ff520c2016-03-28 16:17:38 -0600962 if isEnum:
963 body += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
964 body += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
965 body += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
966
967 body += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
968
Mike Stroyandee76ef2016-01-07 15:35:37 -0700969 # Postfix
970 body += "} " + groupName + ";"
971 if groupElem.get('type') == 'bitmask':
972 section = 'bitmask'
973 else:
974 section = 'group'
975 self.appendSection(section, body)
976 # Enumerant generation
977 # <enum> tags may specify their values in several ways, but are usually
978 # just integers.
979 def genEnum(self, enuminfo, name):
980 OutputGenerator.genEnum(self, enuminfo, name)
981 (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
982 body = '#define ' + name.ljust(33) + ' ' + strVal
983 self.appendSection('enum', body)
984 #
985 # Command generation
986 def genCmd(self, cmdinfo, name):
987 OutputGenerator.genCmd(self, cmdinfo, name)
988 #
989 decls = self.makeCDecls(cmdinfo.elem)
990 self.appendSection('command', decls[0] + '\n')
991 if (self.genOpts.genFuncPointers):
992 self.appendSection('commandPointer', decls[1])
993
994# DocOutputGenerator - subclass of OutputGenerator.
995# Generates AsciiDoc includes with C-language API interfaces, for reference
996# pages and the Vulkan specification. Similar to COutputGenerator, but
997# each interface is written into a different file as determined by the
998# options, only actual C types are emitted, and none of the boilerplate
999# preprocessor code is emitted.
1000#
1001# ---- methods ----
1002# DocOutputGenerator(errFile, warnFile, diagFile) - args as for
1003# OutputGenerator. Defines additional internal state.
1004# ---- methods overriding base class ----
1005# beginFile(genOpts)
1006# endFile()
1007# beginFeature(interface, emit)
1008# endFeature()
1009# genType(typeinfo,name)
1010# genStruct(typeinfo,name)
1011# genGroup(groupinfo,name)
1012# genEnum(enuminfo, name)
1013# genCmd(cmdinfo)
1014class DocOutputGenerator(OutputGenerator):
1015 """Generate specified API interfaces in a specific style, such as a C header"""
1016 def __init__(self,
1017 errFile = sys.stderr,
1018 warnFile = sys.stderr,
1019 diagFile = sys.stdout):
1020 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1021 #
1022 def beginFile(self, genOpts):
1023 OutputGenerator.beginFile(self, genOpts)
1024 def endFile(self):
1025 OutputGenerator.endFile(self)
1026 def beginFeature(self, interface, emit):
1027 # Start processing in superclass
1028 OutputGenerator.beginFeature(self, interface, emit)
1029 def endFeature(self):
1030 # Finish processing in superclass
1031 OutputGenerator.endFeature(self)
1032 #
1033 # Generate an include file
1034 #
1035 # directory - subdirectory to put file in
1036 # basename - base name of the file
1037 # contents - contents of the file (Asciidoc boilerplate aside)
1038 def writeInclude(self, directory, basename, contents):
1039 # Create file
1040 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1041 self.logMsg('diag', '# Generating include file:', filename)
1042 fp = open(filename, 'w')
1043 # Asciidoc anchor
Dustin Graves3ff520c2016-03-28 16:17:38 -06001044 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
1045 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001046 write('[[{0},{0}]]'.format(basename), file=fp)
1047 write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001048 write('endif::doctype-manpage[]', file=fp)
1049 write('ifdef::doctype-manpage[]', file=fp)
1050 write('["source","{basebackend@docbook:c++:cpp}"]', file=fp)
1051 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001052 write('------------------------------------------------------------------------------', file=fp)
1053 write(contents, file=fp)
1054 write('------------------------------------------------------------------------------', file=fp)
1055 fp.close()
1056 #
1057 # Type generation
1058 def genType(self, typeinfo, name):
1059 OutputGenerator.genType(self, typeinfo, name)
1060 typeElem = typeinfo.elem
1061 # If the type is a struct type, traverse the imbedded <member> tags
1062 # generating a structure. Otherwise, emit the tag text.
1063 category = typeElem.get('category')
1064 if (category == 'struct' or category == 'union'):
1065 self.genStruct(typeinfo, name)
1066 else:
1067 # Replace <apientry /> tags with an APIENTRY-style string
1068 # (from self.genOpts). Copy other text through unchanged.
1069 # If the resulting text is an empty string, don't emit it.
1070 s = noneStr(typeElem.text)
1071 for elem in typeElem:
1072 if (elem.tag == 'apientry'):
1073 s += self.genOpts.apientry + noneStr(elem.tail)
1074 else:
1075 s += noneStr(elem.text) + noneStr(elem.tail)
1076 if (len(s) > 0):
1077 if (category == 'bitmask'):
1078 self.writeInclude('flags', name, s + '\n')
1079 elif (category == 'enum'):
1080 self.writeInclude('enums', name, s + '\n')
1081 elif (category == 'funcpointer'):
1082 self.writeInclude('funcpointers', name, s+ '\n')
1083 else:
1084 self.logMsg('diag', '# NOT writing include file for type:',
1085 name, 'category: ', category)
1086 else:
1087 self.logMsg('diag', '# NOT writing empty include file for type', name)
1088 #
1089 # Struct (e.g. C "struct" type) generation.
1090 # This is a special case of the <type> tag where the contents are
1091 # interpreted as a set of <member> tags instead of freeform C
1092 # C type declarations. The <member> tags are just like <param>
1093 # tags - they are a declaration of a struct or union member.
1094 # Only simple member declarations are supported (no nested
1095 # structs etc.)
1096 def genStruct(self, typeinfo, typeName):
1097 OutputGenerator.genStruct(self, typeinfo, typeName)
1098 s = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
1099 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
1100 targetLen = 0;
1101 for member in typeinfo.elem.findall('.//member'):
1102 targetLen = max(targetLen, self.getCParamTypeLength(member))
1103 for member in typeinfo.elem.findall('.//member'):
1104 s += self.makeCParamDecl(member, targetLen + 4)
1105 s += ';\n'
1106 s += '} ' + typeName + ';'
1107 self.writeInclude('structs', typeName, s)
1108 #
1109 # Group (e.g. C "enum" type) generation.
1110 # These are concatenated together with other types.
1111 def genGroup(self, groupinfo, groupName):
1112 OutputGenerator.genGroup(self, groupinfo, groupName)
1113 groupElem = groupinfo.elem
Dustin Graves3ff520c2016-03-28 16:17:38 -06001114
1115 # See if we need min/max/num/padding at end
1116 expand = self.genOpts.expandEnumerants
1117
1118 if expand:
1119 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
1120 isEnum = ('FLAG_BITS' not in expandName)
1121
1122 expandPrefix = expandName
1123 expandSuffix = ''
1124
1125 # Look for a suffix
1126 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
1127 if expandSuffixMatch:
1128 expandSuffix = '_' + expandSuffixMatch.group()
1129 # Strip off the suffix from the prefix
1130 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
1131
Mike Stroyandee76ef2016-01-07 15:35:37 -07001132 # Prefix
1133 s = "typedef enum " + groupName + " {\n"
1134
1135 # Loop over the nested 'enum' tags. Keep track of the minimum and
1136 # maximum numeric values, if they can be determined.
1137 minName = None
1138 for elem in groupElem.findall('enum'):
1139 # Convert the value to an integer and use that to track min/max.
1140 # Values of form -(number) are accepted but nothing more complex.
1141 # Should catch exceptions here for more complex constructs. Not yet.
1142 (numVal,strVal) = self.enumToValue(elem, True)
1143 name = elem.get('name')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001144
1145 # Extension enumerants are only included if they are requested
1146 # in addExtensions or match defaultExtensions.
1147 if (elem.get('extname') is None or
1148 re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
1149 self.genOpts.defaultExtensions == elem.get('supported')):
1150 s += " " + name + " = " + strVal + ",\n"
1151
1152 if (expand and isEnum and elem.get('extends') is None):
Mike Stroyandee76ef2016-01-07 15:35:37 -07001153 if (minName == None):
1154 minName = maxName = name
1155 minValue = maxValue = numVal
1156 elif (numVal < minValue):
1157 minName = name
1158 minValue = numVal
1159 elif (numVal > maxValue):
1160 maxName = name
1161 maxValue = numVal
1162 # Generate min/max value tokens and a range-padding enum. Need some
1163 # additional padding to generate correct names...
1164 if (expand):
1165 s += "\n"
Dustin Graves3ff520c2016-03-28 16:17:38 -06001166 if isEnum:
1167 s += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
1168 s += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
1169 s += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
1170
1171 s += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
Mike Stroyandee76ef2016-01-07 15:35:37 -07001172 # Postfix
1173 s += "} " + groupName + ";"
1174 self.writeInclude('enums', groupName, s)
1175 # Enumerant generation
1176 # <enum> tags may specify their values in several ways, but are usually
1177 # just integers.
1178 def genEnum(self, enuminfo, name):
1179 OutputGenerator.genEnum(self, enuminfo, name)
1180 (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
1181 s = '#define ' + name.ljust(33) + ' ' + strVal
1182 self.logMsg('diag', '# NOT writing compile-time constant', name)
1183 # self.writeInclude('consts', name, s)
1184 #
1185 # Command generation
1186 def genCmd(self, cmdinfo, name):
1187 OutputGenerator.genCmd(self, cmdinfo, name)
1188 #
1189 decls = self.makeCDecls(cmdinfo.elem)
1190 self.writeInclude('protos', name, decls[0])
1191
1192# PyOutputGenerator - subclass of OutputGenerator.
1193# Generates Python data structures describing API names.
1194# Similar to DocOutputGenerator, but writes a single
1195# file.
1196#
1197# ---- methods ----
1198# PyOutputGenerator(errFile, warnFile, diagFile) - args as for
1199# OutputGenerator. Defines additional internal state.
1200# ---- methods overriding base class ----
1201# beginFile(genOpts)
1202# endFile()
1203# genType(typeinfo,name)
1204# genStruct(typeinfo,name)
1205# genGroup(groupinfo,name)
1206# genEnum(enuminfo, name)
1207# genCmd(cmdinfo)
1208class PyOutputGenerator(OutputGenerator):
1209 """Generate specified API interfaces in a specific style, such as a C header"""
1210 def __init__(self,
1211 errFile = sys.stderr,
1212 warnFile = sys.stderr,
1213 diagFile = sys.stdout):
1214 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1215 #
1216 def beginFile(self, genOpts):
1217 OutputGenerator.beginFile(self, genOpts)
1218 for dict in [ 'flags', 'enums', 'structs', 'consts', 'enums',
1219 'consts', 'protos', 'funcpointers' ]:
1220 write(dict, '= {}', file=self.outFile)
1221 def endFile(self):
1222 OutputGenerator.endFile(self)
1223 #
1224 # Add a name from the interface
1225 #
1226 # dict - type of name (see beginFile above)
1227 # name - name to add
1228 # value - A serializable Python value for the name
1229 def addName(self, dict, name, value=None):
1230 write(dict + "['" + name + "'] = ", value, file=self.outFile)
1231 #
1232 # Type generation
1233 # For 'struct' or 'union' types, defer to genStruct() to
1234 # add to the dictionary.
1235 # For 'bitmask' types, add the type name to the 'flags' dictionary,
1236 # with the value being the corresponding 'enums' name defining
1237 # the acceptable flag bits.
1238 # For 'enum' types, add the type name to the 'enums' dictionary,
1239 # with the value being '@STOPHERE@' (because this case seems
1240 # never to happen).
1241 # For 'funcpointer' types, add the type name to the 'funcpointers'
1242 # dictionary.
1243 # For 'handle' and 'define' types, add the handle or #define name
1244 # to the 'struct' dictionary, because that's how the spec sources
1245 # tag these types even though they aren't structs.
1246 def genType(self, typeinfo, name):
1247 OutputGenerator.genType(self, typeinfo, name)
1248 typeElem = typeinfo.elem
1249 # If the type is a struct type, traverse the imbedded <member> tags
1250 # generating a structure. Otherwise, emit the tag text.
1251 category = typeElem.get('category')
1252 if (category == 'struct' or category == 'union'):
1253 self.genStruct(typeinfo, name)
1254 else:
1255 # Extract the type name
1256 # (from self.genOpts). Copy other text through unchanged.
1257 # If the resulting text is an empty string, don't emit it.
1258 count = len(noneStr(typeElem.text))
1259 for elem in typeElem:
1260 count += len(noneStr(elem.text)) + len(noneStr(elem.tail))
1261 if (count > 0):
1262 if (category == 'bitmask'):
1263 requiredEnum = typeElem.get('requires')
1264 self.addName('flags', name, enquote(requiredEnum))
1265 elif (category == 'enum'):
1266 # This case never seems to come up!
1267 # @enums C 'enum' name Dictionary of enumerant names
1268 self.addName('enums', name, enquote('@STOPHERE@'))
1269 elif (category == 'funcpointer'):
1270 self.addName('funcpointers', name, None)
1271 elif (category == 'handle' or category == 'define'):
1272 self.addName('structs', name, None)
1273 else:
1274 write('# Unprocessed type:', name, 'category:', category, file=self.outFile)
1275 else:
1276 write('# Unprocessed type:', name, file=self.outFile)
1277 #
1278 # Struct (e.g. C "struct" type) generation.
1279 #
1280 # Add the struct name to the 'structs' dictionary, with the
1281 # value being an ordered list of the struct member names.
1282 def genStruct(self, typeinfo, typeName):
1283 OutputGenerator.genStruct(self, typeinfo, typeName)
1284
1285 members = [member.text for member in typeinfo.elem.findall('.//member/name')]
1286 self.addName('structs', typeName, members)
1287 #
1288 # Group (e.g. C "enum" type) generation.
1289 # These are concatenated together with other types.
1290 #
1291 # Add the enum type name to the 'enums' dictionary, with
1292 # the value being an ordered list of the enumerant names.
1293 # Add each enumerant name to the 'consts' dictionary, with
1294 # the value being the enum type the enumerant is part of.
1295 def genGroup(self, groupinfo, groupName):
1296 OutputGenerator.genGroup(self, groupinfo, groupName)
1297 groupElem = groupinfo.elem
1298
1299 # @enums C 'enum' name Dictionary of enumerant names
1300 # @consts C enumerant/const name Name of corresponding 'enums' key
1301
1302 # Loop over the nested 'enum' tags. Keep track of the minimum and
1303 # maximum numeric values, if they can be determined.
1304 enumerants = [elem.get('name') for elem in groupElem.findall('enum')]
1305 for name in enumerants:
1306 self.addName('consts', name, enquote(groupName))
1307 self.addName('enums', groupName, enumerants)
1308 # Enumerant generation (compile-time constants)
1309 #
1310 # Add the constant name to the 'consts' dictionary, with the
1311 # value being None to indicate that the constant isn't
1312 # an enumeration value.
1313 def genEnum(self, enuminfo, name):
1314 OutputGenerator.genEnum(self, enuminfo, name)
1315
1316 # @consts C enumerant/const name Name of corresponding 'enums' key
1317
1318 self.addName('consts', name, None)
1319 #
1320 # Command generation
1321 #
1322 # Add the command name to the 'protos' dictionary, with the
1323 # value being an ordered list of the parameter names.
1324 def genCmd(self, cmdinfo, name):
1325 OutputGenerator.genCmd(self, cmdinfo, name)
1326
1327 params = [param.text for param in cmdinfo.elem.findall('param/name')]
1328 self.addName('protos', name, params)
1329
1330# ValidityOutputGenerator - subclass of OutputGenerator.
1331# Generates AsciiDoc includes of valid usage information, for reference
1332# pages and the Vulkan specification. Similar to DocOutputGenerator.
1333#
1334# ---- methods ----
1335# ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
1336# OutputGenerator. Defines additional internal state.
1337# ---- methods overriding base class ----
1338# beginFile(genOpts)
1339# endFile()
1340# beginFeature(interface, emit)
1341# endFeature()
1342# genCmd(cmdinfo)
1343class ValidityOutputGenerator(OutputGenerator):
1344 """Generate specified API interfaces in a specific style, such as a C header"""
1345 def __init__(self,
1346 errFile = sys.stderr,
1347 warnFile = sys.stderr,
1348 diagFile = sys.stdout):
1349 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1350
1351 def beginFile(self, genOpts):
1352 OutputGenerator.beginFile(self, genOpts)
1353 def endFile(self):
1354 OutputGenerator.endFile(self)
1355 def beginFeature(self, interface, emit):
1356 # Start processing in superclass
1357 OutputGenerator.beginFeature(self, interface, emit)
1358 def endFeature(self):
1359 # Finish processing in superclass
1360 OutputGenerator.endFeature(self)
1361
1362 def makeParameterName(self, name):
1363 return 'pname:' + name
1364
1365 def makeStructName(self, name):
1366 return 'sname:' + name
1367
1368 def makeBaseTypeName(self, name):
1369 return 'basetype:' + name
1370
1371 def makeEnumerationName(self, name):
1372 return 'elink:' + name
1373
1374 def makeEnumerantName(self, name):
1375 return 'ename:' + name
1376
1377 def makeFLink(self, name):
1378 return 'flink:' + name
1379
1380 #
1381 # Generate an include file
1382 #
1383 # directory - subdirectory to put file in
1384 # basename - base name of the file
1385 # contents - contents of the file (Asciidoc boilerplate aside)
1386 def writeInclude(self, directory, basename, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes):
1387 # Create file
1388 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1389 self.logMsg('diag', '# Generating include file:', filename)
1390 fp = open(filename, 'w')
1391 # Asciidoc anchor
Dustin Graves3ff520c2016-03-28 16:17:38 -06001392 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001393
1394 # Valid Usage
1395 if validity is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001396 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001397 write('.Valid Usage', file=fp)
1398 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001399 write('endif::doctype-manpage[]', file=fp)
1400 write('ifdef::doctype-manpage[]', file=fp)
1401 write('Valid Usage', file=fp)
1402 write('-----------', file=fp)
1403 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001404 write(validity, file=fp, end='')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001405 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001406 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001407 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001408 write('', file=fp)
1409
1410 # Host Synchronization
1411 if threadsafety is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001412 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001413 write('.Host Synchronization', file=fp)
1414 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001415 write('endif::doctype-manpage[]', file=fp)
1416 write('ifdef::doctype-manpage[]', file=fp)
1417 write('Host Synchronization', file=fp)
1418 write('--------------------', file=fp)
1419 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001420 write(threadsafety, file=fp, end='')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001421 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001422 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001423 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001424 write('', file=fp)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07001425
Mike Stroyandee76ef2016-01-07 15:35:37 -07001426 # Command Properties - contained within a block, to avoid table numbering
1427 if commandpropertiesentry is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001428 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001429 write('.Command Properties', file=fp)
1430 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001431 write('endif::doctype-manpage[]', file=fp)
1432 write('ifdef::doctype-manpage[]', file=fp)
1433 write('Command Properties', file=fp)
1434 write('------------------', file=fp)
1435 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001436 write('[options="header", width="100%"]', file=fp)
1437 write('|=====================', file=fp)
1438 write('|Command Buffer Levels|Render Pass Scope|Supported Queue Types', file=fp)
1439 write(commandpropertiesentry, file=fp)
1440 write('|=====================', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001441 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001442 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001443 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001444 write('', file=fp)
1445
1446 # Success Codes - contained within a block, to avoid table numbering
1447 if successcodes is not None or errorcodes is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001448 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001449 write('.Return Codes', file=fp)
1450 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001451 write('endif::doctype-manpage[]', file=fp)
1452 write('ifdef::doctype-manpage[]', file=fp)
1453 write('Return Codes', file=fp)
1454 write('------------', file=fp)
1455 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001456 if successcodes is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001457 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001458 write('<<fundamentals-successcodes,Success>>::', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001459 write('endif::doctype-manpage[]', file=fp)
1460 write('ifdef::doctype-manpage[]', file=fp)
1461 write('On success, this command returns::', file=fp)
1462 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001463 write(successcodes, file=fp)
1464 if errorcodes is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001465 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001466 write('<<fundamentals-errorcodes,Failure>>::', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001467 write('endif::doctype-manpage[]', file=fp)
1468 write('ifdef::doctype-manpage[]', file=fp)
1469 write('On failure, this command returns::', file=fp)
1470 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001471 write(errorcodes, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001472 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001473 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001474 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001475 write('', file=fp)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07001476
Mike Stroyandee76ef2016-01-07 15:35:37 -07001477 fp.close()
1478
1479 #
1480 # Check if the parameter passed in is a pointer
1481 def paramIsPointer(self, param):
1482 ispointer = False
1483 paramtype = param.find('type')
1484 if paramtype.tail is not None and '*' in paramtype.tail:
1485 ispointer = True
1486
1487 return ispointer
1488
1489 #
1490 # Check if the parameter passed in is a static array
1491 def paramIsStaticArray(self, param):
1492 if param.find('name').tail is not None:
1493 if param.find('name').tail[0] == '[':
1494 return True
1495
1496 #
1497 # Get the length of a parameter that's been identified as a static array
1498 def staticArrayLength(self, param):
1499 paramname = param.find('name')
1500 paramenumsize = param.find('enum')
1501
1502 if paramenumsize is not None:
1503 return paramenumsize.text
1504 else:
1505 return paramname.tail[1:-1]
1506
1507 #
1508 # Check if the parameter passed in is a pointer to an array
1509 def paramIsArray(self, param):
1510 return param.attrib.get('len') is not None
1511
1512 #
1513 # Get the parent of a handle object
1514 def getHandleParent(self, typename):
1515 types = self.registry.findall("types/type")
1516 for elem in types:
1517 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1518 return elem.attrib.get('parent')
1519
1520 #
1521 # Check if a parent object is dispatchable or not
1522 def isHandleTypeDispatchable(self, handlename):
1523 handle = self.registry.find("types/type/[name='" + handlename + "'][@category='handle']")
1524 if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
1525 return True
1526 else:
1527 return False
1528
1529 def isHandleOptional(self, param, params):
1530
1531 # See if the handle is optional
1532 isOptional = False
1533
1534 # Simple, if it's optional, return true
1535 if param.attrib.get('optional') is not None:
1536 return True
1537
1538 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
1539 if param.attrib.get('noautovalidity') is not None:
1540 return True
1541
1542 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
1543 if self.paramIsArray(param):
1544 lengths = param.attrib.get('len').split(',')
1545 for length in lengths:
1546 if (length) != 'null-terminated' and (length) != '1':
1547 for otherparam in params:
1548 if otherparam.find('name').text == length:
1549 if otherparam.attrib.get('optional') is not None:
1550 return True
1551
1552 return False
1553 #
1554 # Get the category of a type
1555 def getTypeCategory(self, typename):
1556 types = self.registry.findall("types/type")
1557 for elem in types:
1558 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1559 return elem.attrib.get('category')
1560
1561 #
1562 # Make a chunk of text for the end of a parameter if it is an array
1563 def makeAsciiDocPreChunk(self, param, params):
1564 paramname = param.find('name')
1565 paramtype = param.find('type')
1566
1567 # General pre-amble. Check optionality and add stuff.
1568 asciidoc = '* '
1569
1570 if self.paramIsStaticArray(param):
1571 asciidoc += 'Any given element of '
1572
1573 elif self.paramIsArray(param):
1574 lengths = param.attrib.get('len').split(',')
1575
1576 # Find all the parameters that are called out as optional, so we can document that they might be zero, and the array may be ignored
1577 optionallengths = []
1578 for length in lengths:
1579 if (length) != 'null-terminated' and (length) != '1':
1580 for otherparam in params:
1581 if otherparam.find('name').text == length:
1582 if otherparam.attrib.get('optional') is not None:
1583 if self.paramIsPointer(otherparam):
1584 optionallengths.append('the value referenced by ' + self.makeParameterName(length))
1585 else:
1586 optionallengths.append(self.makeParameterName(length))
1587
1588 # Document that these arrays may be ignored if any of the length values are 0
1589 if len(optionallengths) != 0 or param.attrib.get('optional') is not None:
1590 asciidoc += 'If '
1591
1592
1593 if len(optionallengths) != 0:
1594 if len(optionallengths) == 1:
1595
1596 asciidoc += optionallengths[0]
1597 asciidoc += ' is '
1598
1599 else:
1600 asciidoc += ' or '.join(optionallengths)
1601 asciidoc += ' are '
1602
1603 asciidoc += 'not `0`, '
1604
1605 if len(optionallengths) != 0 and param.attrib.get('optional') is not None:
1606 asciidoc += 'and '
1607
1608 if param.attrib.get('optional') is not None:
1609 asciidoc += self.makeParameterName(paramname.text)
1610 asciidoc += ' is not `NULL`, '
1611
1612 elif param.attrib.get('optional') is not None:
1613 # Don't generate this stub for bitflags
1614 if self.getTypeCategory(paramtype.text) != 'bitmask':
1615 if param.attrib.get('optional').split(',')[0] == 'true':
1616 asciidoc += 'If '
1617 asciidoc += self.makeParameterName(paramname.text)
1618 asciidoc += ' is not '
1619 if self.paramIsArray(param) or self.paramIsPointer(param) or self.isHandleTypeDispatchable(paramtype.text):
1620 asciidoc += '`NULL`'
1621 elif self.getTypeCategory(paramtype.text) == 'handle':
1622 asciidoc += 'sname:VK_NULL_HANDLE'
1623 else:
1624 asciidoc += '`0`'
1625
1626 asciidoc += ', '
1627
1628 return asciidoc
1629
1630 #
1631 # Make the generic asciidoc line chunk portion used for all parameters.
1632 # May return an empty string if nothing to validate.
1633 def createValidationLineForParameterIntroChunk(self, param, params, typetext):
1634 asciidoc = ''
1635 paramname = param.find('name')
1636 paramtype = param.find('type')
1637
1638 asciidoc += self.makeAsciiDocPreChunk(param, params)
1639
1640 asciidoc += self.makeParameterName(paramname.text)
1641 asciidoc += ' must: be '
1642
1643 if self.paramIsArray(param):
1644 # Arrays. These are hard to get right, apparently
1645
1646 lengths = param.attrib.get('len').split(',')
1647
1648 if (lengths[0]) == 'null-terminated':
1649 asciidoc += 'a null-terminated '
1650 elif (lengths[0]) == '1':
1651 asciidoc += 'a pointer to '
1652 else:
1653 asciidoc += 'a pointer to an array of '
1654
1655 # Handle equations, which are currently denoted with latex
1656 if 'latexmath:' in lengths[0]:
1657 asciidoc += lengths[0]
1658 else:
1659 asciidoc += self.makeParameterName(lengths[0])
1660 asciidoc += ' '
1661
1662 for length in lengths[1:]:
1663 if (length) == 'null-terminated': # This should always be the last thing. If it ever isn't for some bizarre reason, then this will need some massaging.
1664 asciidoc += 'null-terminated '
1665 elif (length) == '1':
1666 asciidoc += 'pointers to '
1667 else:
1668 asciidoc += 'pointers to arrays of '
1669 # Handle equations, which are currently denoted with latex
1670 if 'latex:' in length:
1671 asciidoc += length
1672 else:
1673 asciidoc += self.makeParameterName(length)
1674 asciidoc += ' '
1675
1676 # Void pointers don't actually point at anything - remove the word "to"
1677 if paramtype.text == 'void':
1678 if lengths[-1] == '1':
1679 if len(lengths) > 1:
1680 asciidoc = asciidoc[:-5] # Take care of the extra s added by the post array chunk function. #HACK#
1681 else:
1682 asciidoc = asciidoc[:-4]
1683 else:
1684 # An array of void values is a byte array.
1685 asciidoc += 'byte'
1686
1687 elif paramtype.text == 'char':
1688 # A null terminated array of chars is a string
1689 if lengths[-1] == 'null-terminated':
1690 asciidoc += 'string'
1691 else:
1692 # Else it's just a bunch of chars
1693 asciidoc += 'char value'
1694 elif param.text is not None:
1695 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1696 if 'const' in param.text:
1697 typecategory = self.getTypeCategory(paramtype.text)
1698 if (typecategory != 'struct' and typecategory != 'union' and typecategory != 'basetype' and typecategory is not None) or not self.isStructAlwaysValid(paramtype.text):
1699 asciidoc += 'valid '
1700
1701 asciidoc += typetext
1702
1703 # pluralize
1704 if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'):
1705 asciidoc += 's'
1706
1707 elif self.paramIsPointer(param):
1708 # Handle pointers - which are really special case arrays (i.e. they don't have a length)
1709 pointercount = paramtype.tail.count('*')
1710
1711 # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that.
1712 for i in range(0, pointercount):
1713 asciidoc += 'a pointer to '
1714
1715 if paramtype.text == 'void':
1716 # If there's only one pointer, it's optional, and it doesn't point at anything in particular - we don't need any language.
1717 if pointercount == 1 and param.attrib.get('optional') is not None:
1718 return '' # early return
1719 else:
1720 # Pointer to nothing in particular - delete the " to " portion
1721 asciidoc = asciidoc[:-4]
1722 else:
1723 # Add an article for English semantic win
1724 asciidoc += 'a '
1725
1726 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1727 if param.text is not None and paramtype.text != 'void':
1728 if 'const' in param.text:
1729 asciidoc += 'valid '
1730
1731 asciidoc += typetext
1732
1733 else:
1734 # Non-pointer, non-optional things must be valid
1735 asciidoc += 'a valid '
1736 asciidoc += typetext
1737
1738 if asciidoc != '':
1739 asciidoc += '\n'
1740
1741 # Add additional line for non-optional bitmasks
1742 if self.getTypeCategory(paramtype.text) == 'bitmask':
1743 if param.attrib.get('optional') is None:
1744 asciidoc += '* '
1745 if self.paramIsArray(param):
1746 asciidoc += 'Each element of '
1747 asciidoc += 'pname:'
1748 asciidoc += paramname.text
1749 asciidoc += ' mustnot: be `0`'
1750 asciidoc += '\n'
1751
1752 return asciidoc
1753
1754 def makeAsciiDocLineForParameter(self, param, params, typetext):
1755 if param.attrib.get('noautovalidity') is not None:
1756 return ''
1757 asciidoc = self.createValidationLineForParameterIntroChunk(param, params, typetext)
1758
1759 return asciidoc
1760
1761 # Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance)
1762 def isStructAlwaysValid(self, structname):
1763
1764 struct = self.registry.find("types/type[@name='" + structname + "']")
1765
1766 params = struct.findall('member')
1767 validity = struct.find('validity')
1768
1769 if validity is not None:
1770 return False
1771
1772 for param in params:
1773 paramname = param.find('name')
1774 paramtype = param.find('type')
1775 typecategory = self.getTypeCategory(paramtype.text)
1776
1777 if paramname.text == 'pNext':
1778 return False
1779
1780 if paramname.text == 'sType':
1781 return False
1782
1783 if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param):
1784 if self.makeAsciiDocLineForParameter(param, params, '') != '':
1785 return False
1786 elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask' or param.attrib.get('returnedonly') == 'true':
1787 return False
1788 elif typecategory == 'struct' or typecategory == 'union':
1789 if self.isStructAlwaysValid(paramtype.text) is False:
1790 return False
1791
1792 return True
1793
1794 #
1795 # Make an entire asciidoc line for a given parameter
1796 def createValidationLineForParameter(self, param, params, typecategory):
1797 asciidoc = ''
1798 paramname = param.find('name')
1799 paramtype = param.find('type')
1800
1801 if paramtype.text == 'void' or paramtype.text == 'char':
1802 # Chars and void are special cases - needs care inside the generator functions
1803 # A null-terminated char array is a string, else it's chars.
1804 # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular
1805 asciidoc += self.makeAsciiDocLineForParameter(param, params, '')
1806 elif typecategory == 'bitmask':
1807 bitsname = paramtype.text.replace('Flags', 'FlagBits')
1808 if self.registry.find("enums[@name='" + bitsname + "']") is None:
1809 asciidoc += '* '
1810 asciidoc += self.makeParameterName(paramname.text)
1811 asciidoc += ' must: be `0`'
1812 asciidoc += '\n'
1813 else:
1814 if self.paramIsArray(param):
1815 asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value')
1816 else:
1817 asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values')
1818 elif typecategory == 'handle':
1819 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' handle')
1820 elif typecategory == 'enum':
1821 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeEnumerationName(paramtype.text) + ' value')
1822 elif typecategory == 'struct':
1823 if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1824 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' structure')
1825 elif typecategory == 'union':
1826 if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1827 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' union')
1828 elif self.paramIsArray(param) or self.paramIsPointer(param):
1829 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeBaseTypeName(paramtype.text) + ' value')
1830
1831 return asciidoc
1832
1833 #
1834 # Make an asciidoc validity entry for a handle's parent object
1835 def makeAsciiDocHandleParent(self, param, params):
1836 asciidoc = ''
1837 paramname = param.find('name')
1838 paramtype = param.find('type')
1839
1840 # Deal with handle parents
1841 handleparent = self.getHandleParent(paramtype.text)
1842 if handleparent is not None:
1843 parentreference = None
1844 for otherparam in params:
1845 if otherparam.find('type').text == handleparent:
1846 parentreference = otherparam.find('name').text
1847 if parentreference is not None:
1848 asciidoc += '* '
1849
1850 if self.isHandleOptional(param, params):
1851 if self.paramIsArray(param):
1852 asciidoc += 'Each element of '
1853 asciidoc += self.makeParameterName(paramname.text)
1854 asciidoc += ' that is a valid handle'
1855 else:
1856 asciidoc += 'If '
1857 asciidoc += self.makeParameterName(paramname.text)
1858 asciidoc += ' is a valid handle, it'
1859 else:
1860 if self.paramIsArray(param):
1861 asciidoc += 'Each element of '
1862 asciidoc += self.makeParameterName(paramname.text)
1863 asciidoc += ' must: have been created, allocated or retrieved from '
1864 asciidoc += self.makeParameterName(parentreference)
1865
1866 asciidoc += '\n'
1867 return asciidoc
1868
1869 #
1870 # Generate an asciidoc validity line for the sType value of a struct
1871 def makeStructureType(self, blockname, param):
1872 asciidoc = '* '
1873 paramname = param.find('name')
1874 paramtype = param.find('type')
1875
1876 asciidoc += self.makeParameterName(paramname.text)
1877 asciidoc += ' must: be '
1878
1879 structuretype = ''
1880 for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname):
1881 if elem[0] == 'Vk':
1882 structuretype += 'VK_STRUCTURE_TYPE_'
1883 else:
1884 structuretype += elem[0].upper()
1885 structuretype += '_'
1886
1887 asciidoc += self.makeEnumerantName(structuretype[:-1])
1888 asciidoc += '\n'
1889
1890 return asciidoc
1891
1892 #
1893 # Generate an asciidoc validity line for the pNext value of a struct
1894 def makeStructureExtensionPointer(self, param):
1895 asciidoc = '* '
1896 paramname = param.find('name')
1897 paramtype = param.find('type')
1898
1899 asciidoc += self.makeParameterName(paramname.text)
1900
1901 validextensionstructs = param.attrib.get('validextensionstructs')
Dustin Graves94f19142016-05-10 16:44:16 -06001902 asciidoc += ' must: be `NULL`'
1903 if validextensionstructs is not None:
1904 extensionstructs = ['slink:' + x for x in validextensionstructs.split(',')]
1905 asciidoc += ', or a pointer to a valid instance of '
1906 if len(extensionstructs) == 1:
1907 asciidoc += validextensionstructs
1908 else:
1909 asciidoc += (', ').join(extensionstructs[:-1]) + ' or ' + extensionstructs[-1]
Mike Stroyandee76ef2016-01-07 15:35:37 -07001910
1911 asciidoc += '\n'
1912
1913 return asciidoc
1914
1915 #
1916 # Generate all the valid usage information for a given struct or command
1917 def makeValidUsageStatements(self, cmd, blockname, params, usages):
1918 # Start the asciidoc block for this
1919 asciidoc = ''
1920
1921 handles = []
1922 anyparentedhandlesoptional = False
1923 parentdictionary = {}
1924 arraylengths = set()
1925 for param in params:
1926 paramname = param.find('name')
1927 paramtype = param.find('type')
1928
1929 # Get the type's category
1930 typecategory = self.getTypeCategory(paramtype.text)
1931
1932 # Generate language to independently validate a parameter
1933 if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
1934 asciidoc += self.makeStructureType(blockname, param)
1935 elif paramtype.text == 'void' and paramname.text == 'pNext':
1936 asciidoc += self.makeStructureExtensionPointer(param)
1937 else:
1938 asciidoc += self.createValidationLineForParameter(param, params, typecategory)
1939
1940 # Ensure that any parenting is properly validated, and list that a handle was found
1941 if typecategory == 'handle':
1942 # Don't detect a parent for return values!
1943 if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
1944 parent = self.getHandleParent(paramtype.text)
1945 if parent is not None:
1946 handles.append(param)
1947
1948 # If any param is optional, it affects the output
1949 if self.isHandleOptional(param, params):
1950 anyparentedhandlesoptional = True
1951
1952 # Find the first dispatchable parent
1953 ancestor = parent
1954 while ancestor is not None and not self.isHandleTypeDispatchable(ancestor):
1955 ancestor = self.getHandleParent(ancestor)
1956
1957 # If one was found, add this parameter to the parent dictionary
1958 if ancestor is not None:
1959 if ancestor not in parentdictionary:
1960 parentdictionary[ancestor] = []
1961
1962 if self.paramIsArray(param):
1963 parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text))
1964 else:
1965 parentdictionary[ancestor].append(self.makeParameterName(paramname.text))
1966
1967 # Get the array length for this parameter
1968 arraylength = param.attrib.get('len')
1969 if arraylength is not None:
1970 for onelength in arraylength.split(','):
1971 arraylengths.add(onelength)
1972
1973 # For any vkQueue* functions, there might be queue type data
1974 if 'vkQueue' in blockname:
1975 # The queue type must be valid
1976 queuetypes = cmd.attrib.get('queues')
1977 if queuetypes is not None:
1978 queuebits = []
1979 for queuetype in re.findall(r'([^,]+)', queuetypes):
1980 queuebits.append(queuetype.replace('_',' '))
1981
1982 asciidoc += '* '
1983 asciidoc += 'The pname:queue must: support '
1984 if len(queuebits) == 1:
1985 asciidoc += queuebits[0]
1986 else:
1987 asciidoc += (', ').join(queuebits[:-1])
1988 asciidoc += ' or '
1989 asciidoc += queuebits[-1]
1990 asciidoc += ' operations'
1991 asciidoc += '\n'
1992
1993 if 'vkCmd' in blockname:
1994 # The commandBuffer parameter must be being recorded
1995 asciidoc += '* '
1996 asciidoc += 'pname:commandBuffer must: be in the recording state'
1997 asciidoc += '\n'
1998
1999 # The queue type must be valid
2000 queuetypes = cmd.attrib.get('queues')
2001 queuebits = []
2002 for queuetype in re.findall(r'([^,]+)', queuetypes):
2003 queuebits.append(queuetype.replace('_',' '))
2004
2005 asciidoc += '* '
2006 asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
2007 if len(queuebits) == 1:
2008 asciidoc += queuebits[0]
2009 else:
2010 asciidoc += (', ').join(queuebits[:-1])
2011 asciidoc += ' or '
2012 asciidoc += queuebits[-1]
2013 asciidoc += ' operations'
2014 asciidoc += '\n'
2015
2016 # Must be called inside/outside a renderpass appropriately
2017 renderpass = cmd.attrib.get('renderpass')
2018
2019 if renderpass != 'both':
2020 asciidoc += '* This command must: only be called '
2021 asciidoc += renderpass
2022 asciidoc += ' of a render pass instance'
2023 asciidoc += '\n'
2024
2025 # Must be in the right level command buffer
2026 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2027
2028 if cmdbufferlevel != 'primary,secondary':
2029 asciidoc += '* pname:commandBuffer must: be a '
2030 asciidoc += cmdbufferlevel
2031 asciidoc += ' sname:VkCommandBuffer'
2032 asciidoc += '\n'
2033
2034 # Any non-optional arraylengths should specify they must be greater than 0
2035 for param in params:
2036 paramname = param.find('name')
2037
2038 for arraylength in arraylengths:
2039 if paramname.text == arraylength and param.attrib.get('optional') is None:
2040 # Get all the array dependencies
2041 arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2042
2043 # Get all the optional array dependencies, including those not generating validity for some reason
2044 optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2045 optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']"))
2046
2047 asciidoc += '* '
2048
2049 # Allow lengths to be arbitrary if all their dependents are optional
2050 if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
2051 asciidoc += 'If '
2052 if len(optionalarrays) > 1:
2053 asciidoc += 'any of '
2054
2055 for array in optionalarrays[:-1]:
2056 asciidoc += self.makeParameterName(optionalarrays.find('name').text)
2057 asciidoc += ', '
2058
2059 if len(optionalarrays) > 1:
2060 asciidoc += 'and '
2061 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2062 asciidoc += ' are '
2063 else:
2064 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2065 asciidoc += ' is '
2066
2067 asciidoc += 'not `NULL`, '
2068
2069 if self.paramIsPointer(param):
2070 asciidoc += 'the value referenced by '
Mike Stroyandee76ef2016-01-07 15:35:37 -07002071
2072 elif self.paramIsPointer(param):
2073 asciidoc += 'The value referenced by '
Mike Stroyandee76ef2016-01-07 15:35:37 -07002074
2075 asciidoc += self.makeParameterName(arraylength)
2076 asciidoc += ' must: be greater than `0`'
2077 asciidoc += '\n'
2078
2079 # Find the parents of all objects referenced in this command
2080 for param in handles:
2081 asciidoc += self.makeAsciiDocHandleParent(param, params)
2082
2083 # Find the common ancestors of objects
2084 noancestorscount = 0
2085 while noancestorscount < len(parentdictionary):
2086 noancestorscount = 0
2087 oldparentdictionary = parentdictionary.copy()
2088 for parent in oldparentdictionary.items():
2089 ancestor = self.getHandleParent(parent[0])
2090
2091 while ancestor is not None and ancestor not in parentdictionary:
2092 ancestor = self.getHandleParent(ancestor)
2093
2094 if ancestor is not None:
2095 parentdictionary[ancestor] += parentdictionary.pop(parent[0])
2096 else:
2097 # No ancestors possible - so count it up
2098 noancestorscount += 1
2099
2100 # Add validation language about common ancestors
2101 for parent in parentdictionary.items():
2102 if len(parent[1]) > 1:
2103 parentlanguage = '* '
2104
2105 parentlanguage += 'Each of '
2106 parentlanguage += ", ".join(parent[1][:-1])
2107 parentlanguage += ' and '
2108 parentlanguage += parent[1][-1]
2109 if anyparentedhandlesoptional is True:
2110 parentlanguage += ' that are valid handles'
2111 parentlanguage += ' must: have been created, allocated or retrieved from the same '
2112 parentlanguage += self.makeStructName(parent[0])
2113 parentlanguage += '\n'
2114
2115 # Capitalize and add to the main language
2116 asciidoc += parentlanguage
2117
Dustin Graves3ff520c2016-03-28 16:17:38 -06002118 # Add in any plain-text validation language that should be added
Mike Stroyandee76ef2016-01-07 15:35:37 -07002119 for usage in usages:
2120 asciidoc += '* '
Dustin Graves3ff520c2016-03-28 16:17:38 -06002121 asciidoc += usage
Mike Stroyandee76ef2016-01-07 15:35:37 -07002122 asciidoc += '\n'
2123
2124 # In case there's nothing to report, return None
2125 if asciidoc == '':
2126 return None
2127 # Delimit the asciidoc block
2128 return asciidoc
2129
2130 def makeThreadSafetyBlock(self, cmd, paramtext):
2131 """Generate C function pointer typedef for <command> Element"""
2132 paramdecl = ''
2133
2134 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2135 if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name'):
2136 paramdecl += '* '
2137 paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from'
2138 paramdecl += '\n'
2139
2140 # Find and add any parameters that are thread unsafe
2141 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2142 if (explicitexternsyncparams is not None):
2143 for param in explicitexternsyncparams:
2144 externsyncattribs = param.attrib.get('externsync')
2145 paramname = param.find('name')
2146 for externsyncattrib in externsyncattribs.split(','):
2147 paramdecl += '* '
2148 paramdecl += 'Host access to '
2149 if externsyncattrib == 'true':
2150 if self.paramIsArray(param):
2151 paramdecl += 'each member of ' + self.makeParameterName(paramname.text)
2152 elif self.paramIsPointer(param):
2153 paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text)
2154 else:
2155 paramdecl += self.makeParameterName(paramname.text)
2156 else:
2157 paramdecl += 'pname:'
2158 paramdecl += externsyncattrib
2159 paramdecl += ' must: be externally synchronized\n'
2160
2161 # Find and add any "implicit" parameters that are thread unsafe
2162 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2163 if (implicitexternsyncparams is not None):
2164 for elem in implicitexternsyncparams:
2165 paramdecl += '* '
2166 paramdecl += 'Host access to '
2167 paramdecl += elem.text
2168 paramdecl += ' must: be externally synchronized\n'
2169
2170 if (paramdecl == ''):
2171 return None
2172 else:
2173 return paramdecl
2174
2175 def makeCommandPropertiesTableEntry(self, cmd, name):
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002176
Mike Stroyandee76ef2016-01-07 15:35:37 -07002177 if 'vkCmd' in name:
2178 # Must be called inside/outside a renderpass appropriately
2179 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2180 cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002181
Mike Stroyandee76ef2016-01-07 15:35:37 -07002182 renderpass = cmd.attrib.get('renderpass')
2183 renderpass = renderpass.capitalize()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002184
Mike Stroyandee76ef2016-01-07 15:35:37 -07002185 queues = cmd.attrib.get('queues')
2186 queues = (' + \n').join(queues.upper().split(','))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002187
2188 return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues
Mike Stroyandee76ef2016-01-07 15:35:37 -07002189 elif 'vkQueue' in name:
2190 # Must be called inside/outside a renderpass appropriately
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002191
Mike Stroyandee76ef2016-01-07 15:35:37 -07002192 queues = cmd.attrib.get('queues')
2193 if queues is None:
2194 queues = 'Any'
2195 else:
2196 queues = (' + \n').join(queues.upper().split(','))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002197
2198 return '|-|-|' + queues
Mike Stroyandee76ef2016-01-07 15:35:37 -07002199
2200 return None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002201
Mike Stroyandee76ef2016-01-07 15:35:37 -07002202 def makeSuccessCodes(self, cmd, name):
2203
2204 successcodes = cmd.attrib.get('successcodes')
2205 if successcodes is not None:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002206
Mike Stroyandee76ef2016-01-07 15:35:37 -07002207 successcodeentry = ''
2208 successcodes = successcodes.split(',')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002209 return '* ename:' + '\n* ename:'.join(successcodes)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002210
2211 return None
2212
2213 def makeErrorCodes(self, cmd, name):
2214
2215 errorcodes = cmd.attrib.get('errorcodes')
2216 if errorcodes is not None:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002217
Mike Stroyandee76ef2016-01-07 15:35:37 -07002218 errorcodeentry = ''
2219 errorcodes = errorcodes.split(',')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002220 return '* ename:' + '\n* ename:'.join(errorcodes)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002221
2222 return None
2223
2224 #
2225 # Command generation
2226 def genCmd(self, cmdinfo, name):
2227 OutputGenerator.genCmd(self, cmdinfo, name)
2228 #
Dustin Graves3ff520c2016-03-28 16:17:38 -06002229 # Get all the parameters
Mike Stroyandee76ef2016-01-07 15:35:37 -07002230 params = cmdinfo.elem.findall('param')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002231 usageelements = cmdinfo.elem.findall('validity/usage')
2232 usages = []
2233
2234 for usage in usageelements:
2235 usages.append(usage.text)
2236 for usage in cmdinfo.additionalValidity:
2237 usages.append(usage.text)
2238 for usage in cmdinfo.removedValidity:
2239 usages.remove(usage.text)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002240
2241 validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages)
2242 threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
2243 commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name)
2244 successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
2245 errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
2246
2247 self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
2248
2249 #
2250 # Struct Generation
2251 def genStruct(self, typeinfo, typename):
2252 OutputGenerator.genStruct(self, typeinfo, typename)
2253
2254 # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
2255 if typeinfo.elem.attrib.get('returnedonly') is None:
2256 params = typeinfo.elem.findall('member')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002257
2258 usageelements = typeinfo.elem.findall('validity/usage')
2259 usages = []
2260
2261 for usage in usageelements:
2262 usages.append(usage.text)
2263 for usage in typeinfo.additionalValidity:
2264 usages.append(usage.text)
2265 for usage in typeinfo.removedValidity:
2266 usages.remove(usage.text)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002267
2268 validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
2269 threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
2270
2271 self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None)
2272 else:
2273 # Still generate files for return only structs, in case this state changes later
2274 self.writeInclude('validity/structs', typename, None, None, None, None, None)
2275
2276 #
2277 # Type Generation
2278 def genType(self, typeinfo, typename):
2279 OutputGenerator.genType(self, typeinfo, typename)
2280
2281 category = typeinfo.elem.get('category')
2282 if (category == 'struct' or category == 'union'):
2283 self.genStruct(typeinfo, typename)
2284
2285# HostSynchronizationOutputGenerator - subclass of OutputGenerator.
2286# Generates AsciiDoc includes of the externsync parameter table for the
2287# fundamentals chapter of the Vulkan specification. Similar to
2288# DocOutputGenerator.
2289#
2290# ---- methods ----
2291# HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for
2292# OutputGenerator. Defines additional internal state.
2293# ---- methods overriding base class ----
2294# genCmd(cmdinfo)
2295class HostSynchronizationOutputGenerator(OutputGenerator):
2296 # Generate Host Synchronized Parameters in a table at the top of the spec
2297 def __init__(self,
2298 errFile = sys.stderr,
2299 warnFile = sys.stderr,
2300 diagFile = sys.stdout):
2301 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2302
2303 threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''}
2304
2305 def makeParameterName(self, name):
2306 return 'pname:' + name
2307
2308 def makeFLink(self, name):
2309 return 'flink:' + name
2310
2311 #
2312 # Generate an include file
2313 #
2314 # directory - subdirectory to put file in
2315 # basename - base name of the file
2316 # contents - contents of the file (Asciidoc boilerplate aside)
2317 def writeInclude(self):
2318
2319 if self.threadsafety['parameters'] is not None:
2320 # Create file
2321 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt'
2322 self.logMsg('diag', '# Generating include file:', filename)
2323 fp = open(filename, 'w')
2324
2325 # Host Synchronization
Dustin Graves3ff520c2016-03-28 16:17:38 -06002326 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002327 write('.Externally Synchronized Parameters', file=fp)
2328 write('*' * 80, file=fp)
2329 write(self.threadsafety['parameters'], file=fp, end='')
2330 write('*' * 80, file=fp)
2331 write('', file=fp)
2332
2333 if self.threadsafety['parameterlists'] is not None:
2334 # Create file
2335 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt'
2336 self.logMsg('diag', '# Generating include file:', filename)
2337 fp = open(filename, 'w')
2338
2339 # Host Synchronization
Dustin Graves3ff520c2016-03-28 16:17:38 -06002340 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002341 write('.Externally Synchronized Parameter Lists', file=fp)
2342 write('*' * 80, file=fp)
2343 write(self.threadsafety['parameterlists'], file=fp, end='')
2344 write('*' * 80, file=fp)
2345 write('', file=fp)
2346
2347 if self.threadsafety['implicit'] is not None:
2348 # Create file
2349 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt'
2350 self.logMsg('diag', '# Generating include file:', filename)
2351 fp = open(filename, 'w')
2352
2353 # Host Synchronization
Dustin Graves3ff520c2016-03-28 16:17:38 -06002354 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002355 write('.Implicit Externally Synchronized Parameters', file=fp)
2356 write('*' * 80, file=fp)
2357 write(self.threadsafety['implicit'], file=fp, end='')
2358 write('*' * 80, file=fp)
2359 write('', file=fp)
2360
2361 fp.close()
2362
2363 #
2364 # Check if the parameter passed in is a pointer to an array
2365 def paramIsArray(self, param):
2366 return param.attrib.get('len') is not None
2367
2368 # Check if the parameter passed in is a pointer
2369 def paramIsPointer(self, param):
2370 ispointer = False
2371 paramtype = param.find('type')
2372 if paramtype.tail is not None and '*' in paramtype.tail:
2373 ispointer = True
2374
2375 return ispointer
2376
2377 # Turn the "name[].member[]" notation into plain English.
2378 def makeThreadDereferenceHumanReadable(self, dereference):
2379 matches = re.findall(r"[\w]+[^\w]*",dereference)
2380 stringval = ''
2381 for match in reversed(matches):
2382 if '->' in match or '.' in match:
2383 stringval += 'member of '
2384 if '[]' in match:
2385 stringval += 'each element of '
2386
2387 stringval += 'the '
2388 stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0])
2389 stringval += ' '
2390
2391 stringval += 'parameter'
2392
2393 return stringval[0].upper() + stringval[1:]
2394
2395 def makeThreadSafetyBlocks(self, cmd, paramtext):
2396 protoname = cmd.find('proto/name').text
2397
2398 # Find and add any parameters that are thread unsafe
2399 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2400 if (explicitexternsyncparams is not None):
2401 for param in explicitexternsyncparams:
2402 externsyncattribs = param.attrib.get('externsync')
2403 paramname = param.find('name')
2404 for externsyncattrib in externsyncattribs.split(','):
2405
2406 tempstring = '* '
2407 if externsyncattrib == 'true':
2408 if self.paramIsArray(param):
2409 tempstring += 'Each element of the '
2410 elif self.paramIsPointer(param):
2411 tempstring += 'The object referenced by the '
2412 else:
2413 tempstring += 'The '
2414
2415 tempstring += self.makeParameterName(paramname.text)
2416 tempstring += ' parameter'
2417
2418 else:
2419 tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib)
2420
2421 tempstring += ' in '
2422 tempstring += self.makeFLink(protoname)
2423 tempstring += '\n'
2424
2425
2426 if ' element of ' in tempstring:
2427 self.threadsafety['parameterlists'] += tempstring
2428 else:
2429 self.threadsafety['parameters'] += tempstring
2430
2431
2432 # Find and add any "implicit" parameters that are thread unsafe
2433 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2434 if (implicitexternsyncparams is not None):
2435 for elem in implicitexternsyncparams:
2436 self.threadsafety['implicit'] += '* '
2437 self.threadsafety['implicit'] += elem.text[0].upper()
2438 self.threadsafety['implicit'] += elem.text[1:]
2439 self.threadsafety['implicit'] += ' in '
2440 self.threadsafety['implicit'] += self.makeFLink(protoname)
2441 self.threadsafety['implicit'] += '\n'
2442
2443
2444 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2445 if protoname is not None and 'vkCmd' in protoname:
2446 self.threadsafety['implicit'] += '* '
2447 self.threadsafety['implicit'] += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in '
2448 self.threadsafety['implicit'] += self.makeFLink(protoname)
2449
2450 self.threadsafety['implicit'] += '\n'
2451
2452 #
2453 # Command generation
2454 def genCmd(self, cmdinfo, name):
2455 OutputGenerator.genCmd(self, cmdinfo, name)
2456 #
2457 # Get all thh parameters
2458 params = cmdinfo.elem.findall('param')
2459 usages = cmdinfo.elem.findall('validity/usage')
2460
2461 self.makeThreadSafetyBlocks(cmdinfo.elem, 'param')
2462
2463 self.writeInclude()
Mike Stroyan845bdc42015-11-02 15:30:20 -07002464
2465# ThreadOutputGenerator - subclass of OutputGenerator.
2466# Generates Thread checking framework
2467#
2468# ---- methods ----
2469# ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
2470# OutputGenerator. Defines additional internal state.
2471# ---- methods overriding base class ----
2472# beginFile(genOpts)
2473# endFile()
2474# beginFeature(interface, emit)
2475# endFeature()
2476# genType(typeinfo,name)
2477# genStruct(typeinfo,name)
2478# genGroup(groupinfo,name)
2479# genEnum(enuminfo, name)
2480# genCmd(cmdinfo)
2481class ThreadOutputGenerator(OutputGenerator):
2482 """Generate specified API interfaces in a specific style, such as a C header"""
2483 # This is an ordered list of sections in the header file.
2484 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
2485 'group', 'bitmask', 'funcpointer', 'struct']
2486 ALL_SECTIONS = TYPE_SECTIONS + ['command']
2487 def __init__(self,
2488 errFile = sys.stderr,
2489 warnFile = sys.stderr,
2490 diagFile = sys.stdout):
2491 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2492 # Internal state - accumulators for different inner block text
2493 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2494 self.intercepts = []
2495
2496 # Check if the parameter passed in is a pointer to an array
2497 def paramIsArray(self, param):
2498 return param.attrib.get('len') is not None
2499
2500 # Check if the parameter passed in is a pointer
2501 def paramIsPointer(self, param):
2502 ispointer = False
2503 for elem in param:
2504 #write('paramIsPointer '+elem.text, file=sys.stderr)
2505 #write('elem.tag '+elem.tag, file=sys.stderr)
2506 #if (elem.tail is None):
2507 # write('elem.tail is None', file=sys.stderr)
2508 #else:
2509 # write('elem.tail '+elem.tail, file=sys.stderr)
2510 if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
2511 ispointer = True
2512 # write('is pointer', file=sys.stderr)
2513 return ispointer
2514 def makeThreadUseBlock(self, cmd, functionprefix):
2515 """Generate C function pointer typedef for <command> Element"""
2516 paramdecl = ''
2517 thread_check_dispatchable_objects = [
2518 "VkCommandBuffer",
2519 "VkDevice",
2520 "VkInstance",
2521 "VkQueue",
2522 ]
2523 thread_check_nondispatchable_objects = [
2524 "VkBuffer",
2525 "VkBufferView",
2526 "VkCommandPool",
2527 "VkDescriptorPool",
2528 "VkDescriptorSetLayout",
2529 "VkDeviceMemory",
2530 "VkEvent",
2531 "VkFence",
2532 "VkFramebuffer",
2533 "VkImage",
2534 "VkImageView",
2535 "VkPipeline",
2536 "VkPipelineCache",
2537 "VkPipelineLayout",
2538 "VkQueryPool",
2539 "VkRenderPass",
2540 "VkSampler",
2541 "VkSemaphore",
2542 "VkShaderModule",
2543 ]
2544
2545 # Find and add any parameters that are thread unsafe
2546 params = cmd.findall('param')
2547 for param in params:
2548 paramname = param.find('name')
2549 if False: # self.paramIsPointer(param):
2550 paramdecl += ' // not watching use of pointer ' + paramname.text + '\n'
2551 else:
2552 externsync = param.attrib.get('externsync')
2553 if externsync == 'true':
2554 if self.paramIsArray(param):
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002555 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002556 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + '[index]);\n'
2557 paramdecl += ' }\n'
2558 else:
2559 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + ');\n'
2560 elif (param.attrib.get('externsync')):
2561 if self.paramIsArray(param):
2562 # Externsync can list pointers to arrays of members to synchronize
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002563 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002564 for member in externsync.split(","):
2565 # Replace first empty [] in member name with index
2566 element = member.replace('[]','[index]',1)
2567 if '[]' in element:
2568 # Replace any second empty [] in element name with
2569 # inner array index based on mapping array names like
2570 # "pSomeThings[]" to "someThingCount" array size.
2571 # This could be more robust by mapping a param member
2572 # name to a struct type and "len" attribute.
2573 limit = element[0:element.find('s[]')] + 'Count'
2574 dotp = limit.rfind('.p')
2575 limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:]
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002576 paramdecl += ' for(uint32_t index2=0;index2<'+limit+';index2++)\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002577 element = element.replace('[]','[index2]')
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002578 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002579 paramdecl += ' }\n'
2580 else:
2581 # externsync can list members to synchronize
2582 for member in externsync.split(","):
Mark Lobodzinski3b34af02016-09-27 13:08:15 -06002583 member = str(member).replace("::", "->")
Mike Stroyan845bdc42015-11-02 15:30:20 -07002584 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
2585 else:
2586 paramtype = param.find('type')
2587 if paramtype is not None:
2588 paramtype = paramtype.text
2589 else:
2590 paramtype = 'None'
2591 if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
2592 if self.paramIsArray(param) and ('pPipelines' != paramname.text):
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002593 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002594 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
2595 paramdecl += ' }\n'
2596 elif not self.paramIsPointer(param):
2597 # Pointer params are often being created.
2598 # They are not being read from.
2599 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
2600 explicitexternsyncparams = cmd.findall("param[@externsync]")
2601 if (explicitexternsyncparams is not None):
2602 for param in explicitexternsyncparams:
2603 externsyncattrib = param.attrib.get('externsync')
2604 paramname = param.find('name')
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002605 paramdecl += ' // Host access to '
Mike Stroyan845bdc42015-11-02 15:30:20 -07002606 if externsyncattrib == 'true':
2607 if self.paramIsArray(param):
2608 paramdecl += 'each member of ' + paramname.text
2609 elif self.paramIsPointer(param):
2610 paramdecl += 'the object referenced by ' + paramname.text
2611 else:
2612 paramdecl += paramname.text
2613 else:
2614 paramdecl += externsyncattrib
2615 paramdecl += ' must be externally synchronized\n'
2616
2617 # Find and add any "implicit" parameters that are thread unsafe
2618 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2619 if (implicitexternsyncparams is not None):
2620 for elem in implicitexternsyncparams:
2621 paramdecl += ' // '
2622 paramdecl += elem.text
2623 paramdecl += ' must be externally synchronized between host accesses\n'
2624
2625 if (paramdecl == ''):
2626 return None
2627 else:
2628 return paramdecl
2629 def beginFile(self, genOpts):
2630 OutputGenerator.beginFile(self, genOpts)
2631 # C-specific
2632 #
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002633 # Multiple inclusion protection & C++ namespace.
Mike Stroyan845bdc42015-11-02 15:30:20 -07002634 if (genOpts.protectFile and self.genOpts.filename):
2635 headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
2636 write('#ifndef', headerSym, file=self.outFile)
2637 write('#define', headerSym, '1', file=self.outFile)
2638 self.newline()
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002639 write('namespace threading {', file=self.outFile)
Mike Stroyan845bdc42015-11-02 15:30:20 -07002640 self.newline()
2641 #
2642 # User-supplied prefix text, if any (list of strings)
2643 if (genOpts.prefixText):
2644 for s in genOpts.prefixText:
2645 write(s, file=self.outFile)
2646 def endFile(self):
2647 # C-specific
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002648 # Finish C++ namespace and multiple inclusion protection
Mike Stroyan845bdc42015-11-02 15:30:20 -07002649 self.newline()
2650 # record intercepted procedures
2651 write('// intercepts', file=self.outFile)
2652 write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
2653 write('\n'.join(self.intercepts), file=self.outFile)
2654 write('};\n', file=self.outFile)
2655 self.newline()
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002656 write('} // namespace threading', file=self.outFile)
Mike Stroyan845bdc42015-11-02 15:30:20 -07002657 if (self.genOpts.protectFile and self.genOpts.filename):
2658 self.newline()
2659 write('#endif', file=self.outFile)
2660 # Finish processing in superclass
2661 OutputGenerator.endFile(self)
2662 def beginFeature(self, interface, emit):
2663 #write('// starting beginFeature', file=self.outFile)
2664 # Start processing in superclass
2665 OutputGenerator.beginFeature(self, interface, emit)
2666 # C-specific
2667 # Accumulate includes, defines, types, enums, function pointer typedefs,
2668 # end function prototypes separately for this feature. They're only
2669 # printed in endFeature().
2670 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2671 #write('// ending beginFeature', file=self.outFile)
2672 def endFeature(self):
2673 # C-specific
2674 # Actually write the interface to the output file.
2675 #write('// starting endFeature', file=self.outFile)
2676 if (self.emit):
2677 self.newline()
2678 if (self.genOpts.protectFeature):
2679 write('#ifndef', self.featureName, file=self.outFile)
2680 # If type declarations are needed by other features based on
2681 # this one, it may be necessary to suppress the ExtraProtect,
2682 # or move it below the 'for section...' loop.
2683 #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
2684 if (self.featureExtraProtect != None):
2685 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2686 #write('#define', self.featureName, '1', file=self.outFile)
2687 for section in self.TYPE_SECTIONS:
2688 #write('// endFeature writing section'+section, file=self.outFile)
2689 contents = self.sections[section]
2690 if contents:
2691 write('\n'.join(contents), file=self.outFile)
2692 self.newline()
2693 #write('// endFeature looking at self.sections[command]', file=self.outFile)
2694 if (self.sections['command']):
2695 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2696 self.newline()
2697 if (self.featureExtraProtect != None):
2698 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2699 if (self.genOpts.protectFeature):
2700 write('#endif /*', self.featureName, '*/', file=self.outFile)
2701 # Finish processing in superclass
2702 OutputGenerator.endFeature(self)
2703 #write('// ending endFeature', file=self.outFile)
2704 #
2705 # Append a definition to the specified section
2706 def appendSection(self, section, text):
2707 # self.sections[section].append('SECTION: ' + section + '\n')
2708 self.sections[section].append(text)
2709 #
2710 # Type generation
2711 def genType(self, typeinfo, name):
2712 pass
2713 #
2714 # Struct (e.g. C "struct" type) generation.
2715 # This is a special case of the <type> tag where the contents are
2716 # interpreted as a set of <member> tags instead of freeform C
2717 # C type declarations. The <member> tags are just like <param>
2718 # tags - they are a declaration of a struct or union member.
2719 # Only simple member declarations are supported (no nested
2720 # structs etc.)
2721 def genStruct(self, typeinfo, typeName):
2722 OutputGenerator.genStruct(self, typeinfo, typeName)
2723 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
2724 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
2725 for member in typeinfo.elem.findall('.//member'):
2726 body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
2727 body += ';\n'
2728 body += '} ' + typeName + ';\n'
2729 self.appendSection('struct', body)
2730 #
2731 # Group (e.g. C "enum" type) generation.
2732 # These are concatenated together with other types.
2733 def genGroup(self, groupinfo, groupName):
2734 pass
2735 # Enumerant generation
2736 # <enum> tags may specify their values in several ways, but are usually
2737 # just integers.
2738 def genEnum(self, enuminfo, name):
2739 pass
2740 #
2741 # Command generation
2742 def genCmd(self, cmdinfo, name):
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002743 # Commands shadowed by interface functions and are not implemented
2744 interface_functions = [
2745 'vkEnumerateInstanceLayerProperties',
2746 'vkEnumerateInstanceExtensionProperties',
2747 'vkEnumerateDeviceLayerProperties',
2748 ]
2749 if name in interface_functions:
2750 return
Mike Stroyan845bdc42015-11-02 15:30:20 -07002751 special_functions = [
2752 'vkGetDeviceProcAddr',
2753 'vkGetInstanceProcAddr',
2754 'vkCreateDevice',
2755 'vkDestroyDevice',
2756 'vkCreateInstance',
2757 'vkDestroyInstance',
Mike Stroyan845bdc42015-11-02 15:30:20 -07002758 'vkAllocateCommandBuffers',
2759 'vkFreeCommandBuffers',
2760 'vkCreateDebugReportCallbackEXT',
2761 'vkDestroyDebugReportCallbackEXT',
2762 ]
2763 if name in special_functions:
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002764 decls = self.makeCDecls(cmdinfo.elem)
2765 self.appendSection('command', '')
2766 self.appendSection('command', '// declare only')
2767 self.appendSection('command', decls[0])
Chia-I Wu33b4ce82016-05-16 10:02:06 +08002768 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
Mike Stroyan845bdc42015-11-02 15:30:20 -07002769 return
2770 if "KHR" in name:
2771 self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
2772 return
Dustin Graves94f19142016-05-10 16:44:16 -06002773 if ("DebugMarker" in name) and ("EXT" in name):
2774 self.appendSection('command', '// TODO - not wrapping EXT function ' + name)
2775 return
Mike Stroyan845bdc42015-11-02 15:30:20 -07002776 # Determine first if this function needs to be intercepted
2777 startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
2778 if startthreadsafety is None:
2779 return
2780 finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
2781 # record that the function will be intercepted
2782 if (self.featureExtraProtect != None):
2783 self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
Chia-I Wu33b4ce82016-05-16 10:02:06 +08002784 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
Mike Stroyan845bdc42015-11-02 15:30:20 -07002785 if (self.featureExtraProtect != None):
2786 self.intercepts += [ '#endif' ]
2787
2788 OutputGenerator.genCmd(self, cmdinfo, name)
2789 #
2790 decls = self.makeCDecls(cmdinfo.elem)
2791 self.appendSection('command', '')
2792 self.appendSection('command', decls[0][:-1])
2793 self.appendSection('command', '{')
2794 # setup common to call wrappers
2795 # first parameter is always dispatchable
2796 dispatchable_type = cmdinfo.elem.find('param/type').text
2797 dispatchable_name = cmdinfo.elem.find('param/name').text
2798 self.appendSection('command', ' dispatch_key key = get_dispatch_key('+dispatchable_name+');')
2799 self.appendSection('command', ' layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
2800 if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
2801 self.appendSection('command', ' VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
2802 else:
2803 self.appendSection('command', ' VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
2804 # Declare result variable, if any.
2805 resulttype = cmdinfo.elem.find('proto/type')
2806 if (resulttype != None and resulttype.text == 'void'):
2807 resulttype = None
2808 if (resulttype != None):
2809 self.appendSection('command', ' ' + resulttype.text + ' result;')
2810 assignresult = 'result = '
2811 else:
2812 assignresult = ''
2813
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002814 self.appendSection('command', ' bool threadChecks = startMultiThread();')
2815 self.appendSection('command', ' if (threadChecks) {')
2816 self.appendSection('command', " "+"\n ".join(str(startthreadsafety).rstrip().split("\n")))
2817 self.appendSection('command', ' }')
Mike Stroyan845bdc42015-11-02 15:30:20 -07002818 params = cmdinfo.elem.findall('param/name')
2819 paramstext = ','.join([str(param.text) for param in params])
2820 API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
2821 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');')
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002822 self.appendSection('command', ' if (threadChecks) {')
2823 self.appendSection('command', " "+"\n ".join(str(finishthreadsafety).rstrip().split("\n")))
2824 self.appendSection('command', ' } else {')
2825 self.appendSection('command', ' finishMultiThread();')
2826 self.appendSection('command', ' }')
Mike Stroyan845bdc42015-11-02 15:30:20 -07002827 # Return result variable, if any.
2828 if (resulttype != None):
2829 self.appendSection('command', ' return result;')
2830 self.appendSection('command', '}')
Chia-I Wu33b4ce82016-05-16 10:02:06 +08002831 #
2832 # override makeProtoName to drop the "vk" prefix
2833 def makeProtoName(self, name, tail):
2834 return self.genOpts.apientry + name[2:] + tail
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002835
2836# ParamCheckerOutputGenerator - subclass of OutputGenerator.
2837# Generates param checker layer code.
2838#
2839# ---- methods ----
2840# ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
2841# OutputGenerator. Defines additional internal state.
2842# ---- methods overriding base class ----
2843# beginFile(genOpts)
2844# endFile()
2845# beginFeature(interface, emit)
2846# endFeature()
2847# genType(typeinfo,name)
2848# genStruct(typeinfo,name)
2849# genGroup(groupinfo,name)
2850# genEnum(enuminfo, name)
2851# genCmd(cmdinfo)
2852class ParamCheckerOutputGenerator(OutputGenerator):
2853 """Generate ParamChecker code based on XML element attributes"""
2854 # This is an ordered list of sections in the header file.
2855 ALL_SECTIONS = ['command']
2856 def __init__(self,
2857 errFile = sys.stderr,
2858 warnFile = sys.stderr,
2859 diagFile = sys.stdout):
2860 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2861 self.INDENT_SPACES = 4
2862 # Commands to ignore
Dustin Graveseec48bf2016-03-02 18:23:29 -07002863 self.blacklist = [
Dustin Graves842621d2016-03-03 14:17:08 -07002864 'vkGetInstanceProcAddr',
2865 'vkGetDeviceProcAddr',
Dustin Graveseec48bf2016-03-02 18:23:29 -07002866 'vkEnumerateInstanceLayerProperties',
2867 'vkEnumerateInstanceExtensionsProperties',
2868 'vkEnumerateDeviceLayerProperties',
2869 'vkEnumerateDeviceExtensionsProperties',
2870 'vkCreateDebugReportCallbackEXT',
2871 'vkDebugReportMessageEXT']
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06002872 # Validation conditions for some special case struct members that are conditionally validated
2873 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
Dustin Graves6e4c2be2016-07-19 13:17:35 -06002874 # Header version
2875 self.headerVersion = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002876 # Internal state - accumulators for different inner block text
2877 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002878 self.structNames = [] # List of Vulkan struct typenames
2879 self.stypes = [] # Values from the VkStructureType enumeration
2880 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
Dustin Graves20fd66f2016-04-18 18:33:21 -06002881 self.handleTypes = set() # Set of handle type names
Dustin Graveseec48bf2016-03-02 18:23:29 -07002882 self.commands = [] # List of CommandData records for all Vulkan commands
2883 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06002884 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
Dustin Graves29148ff2016-03-23 19:44:00 -06002885 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Dustin Gravesc900f572016-05-16 11:07:59 -06002886 self.flags = set() # Map of flags typenames
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002887 self.flagBits = dict() # Map of flag bits typename to list of values
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002888 # Named tuples to store struct and command data
2889 self.StructType = namedtuple('StructType', ['name', 'value'])
Dustin Graves29148ff2016-03-23 19:44:00 -06002890 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Dustin Graves20fd66f2016-04-18 18:33:21 -06002891 'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
2892 'condition', 'cdecl'])
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002893 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002894 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002895 #
2896 def incIndent(self, indent):
2897 inc = ' ' * self.INDENT_SPACES
2898 if indent:
2899 return indent + inc
2900 return inc
Dustin Graves842621d2016-03-03 14:17:08 -07002901 #
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002902 def decIndent(self, indent):
2903 if indent and (len(indent) > self.INDENT_SPACES):
2904 return indent[:-self.INDENT_SPACES]
2905 return ''
2906 #
2907 def beginFile(self, genOpts):
2908 OutputGenerator.beginFile(self, genOpts)
2909 # C-specific
2910 #
2911 # User-supplied prefix text, if any (list of strings)
2912 if (genOpts.prefixText):
2913 for s in genOpts.prefixText:
2914 write(s, file=self.outFile)
2915 #
2916 # Multiple inclusion protection & C++ wrappers.
2917 if (genOpts.protectFile and self.genOpts.filename):
2918 headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
2919 write('#ifndef', headerSym, file=self.outFile)
2920 write('#define', headerSym, '1', file=self.outFile)
2921 self.newline()
2922 #
2923 # Headers
Dustin Gravesb210cee2016-03-31 09:50:42 -06002924 write('#include <string>', file=self.outFile)
2925 self.newline()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002926 write('#include "vulkan/vulkan.h"', file=self.outFile)
Dustin Graves58c2f662016-03-08 17:48:20 -07002927 write('#include "vk_layer_extension_utils.h"', file=self.outFile)
Mark Lobodzinski739391a2016-03-17 15:08:18 -06002928 write('#include "parameter_validation_utils.h"', file=self.outFile)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002929 #
2930 # Macros
2931 self.newline()
2932 write('#ifndef UNUSED_PARAMETER', file=self.outFile)
2933 write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
2934 write('#endif // UNUSED_PARAMETER', file=self.outFile)
Dustin Gravesb83fc2d2016-05-04 12:56:08 -06002935 #
2936 # Namespace
2937 self.newline()
2938 write('namespace parameter_validation {', file = self.outFile)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002939 def endFile(self):
2940 # C-specific
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002941 self.newline()
Dustin Gravesb83fc2d2016-05-04 12:56:08 -06002942 # Namespace
2943 write('} // namespace parameter_validation', file = self.outFile)
2944 # Finish C++ wrapper and multiple inclusion protection
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002945 if (self.genOpts.protectFile and self.genOpts.filename):
2946 self.newline()
2947 write('#endif', file=self.outFile)
2948 # Finish processing in superclass
2949 OutputGenerator.endFile(self)
2950 def beginFeature(self, interface, emit):
2951 # Start processing in superclass
2952 OutputGenerator.beginFeature(self, interface, emit)
2953 # C-specific
2954 # Accumulate includes, defines, types, enums, function pointer typedefs,
2955 # end function prototypes separately for this feature. They're only
2956 # printed in endFeature().
Dustin Graves6e4c2be2016-07-19 13:17:35 -06002957 self.headerVersion = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002958 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002959 self.structNames = []
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002960 self.stypes = []
2961 self.structTypes = dict()
Dustin Graves20fd66f2016-04-18 18:33:21 -06002962 self.handleTypes = set()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002963 self.commands = []
Dustin Graveseec48bf2016-03-02 18:23:29 -07002964 self.structMembers = []
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06002965 self.validatedStructs = dict()
Dustin Graves29148ff2016-03-23 19:44:00 -06002966 self.enumRanges = dict()
Dustin Gravesc900f572016-05-16 11:07:59 -06002967 self.flags = set()
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002968 self.flagBits = dict()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002969 def endFeature(self):
2970 # C-specific
2971 # Actually write the interface to the output file.
2972 if (self.emit):
2973 self.newline()
2974 # If type declarations are needed by other features based on
2975 # this one, it may be necessary to suppress the ExtraProtect,
2976 # or move it below the 'for section...' loop.
2977 if (self.featureExtraProtect != None):
2978 write('#ifdef', self.featureExtraProtect, file=self.outFile)
Dustin Graveseec48bf2016-03-02 18:23:29 -07002979 # Generate the struct member checking code from the captured data
Dustin Graveseec48bf2016-03-02 18:23:29 -07002980 self.processStructMemberData()
2981 # Generate the command parameter checking code from the captured data
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002982 self.processCmdData()
Dustin Graves6e4c2be2016-07-19 13:17:35 -06002983 # Write the declaration for the HeaderVersion
2984 if self.headerVersion:
2985 write('const uint32_t GeneratedHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
2986 self.newline()
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002987 # Write the declarations for the VkFlags values combining all flag bits
2988 for flag in sorted(self.flags):
Dustin Gravesc900f572016-05-16 11:07:59 -06002989 flagBits = flag.replace('Flags', 'FlagBits')
2990 if flagBits in self.flagBits:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002991 bits = self.flagBits[flagBits]
2992 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
2993 for bit in bits[1:]:
2994 decl += '|' + bit
2995 decl += ';'
2996 write(decl, file=self.outFile)
2997 self.newline()
2998 # Write the parameter validation code to the file
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002999 if (self.sections['command']):
3000 if (self.genOpts.protectProto):
3001 write(self.genOpts.protectProto,
3002 self.genOpts.protectProtoStr, file=self.outFile)
3003 write('\n'.join(self.sections['command']), end='', file=self.outFile)
3004 if (self.featureExtraProtect != None):
3005 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
3006 else:
3007 self.newline()
3008 # Finish processing in superclass
3009 OutputGenerator.endFeature(self)
3010 #
3011 # Append a definition to the specified section
3012 def appendSection(self, section, text):
3013 # self.sections[section].append('SECTION: ' + section + '\n')
3014 self.sections[section].append(text)
3015 #
3016 # Type generation
3017 def genType(self, typeinfo, name):
3018 OutputGenerator.genType(self, typeinfo, name)
3019 typeElem = typeinfo.elem
3020 # If the type is a struct type, traverse the imbedded <member> tags
3021 # generating a structure. Otherwise, emit the tag text.
3022 category = typeElem.get('category')
3023 if (category == 'struct' or category == 'union'):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003024 self.structNames.append(name)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003025 self.genStruct(typeinfo, name)
Dustin Graves20fd66f2016-04-18 18:33:21 -06003026 elif (category == 'handle'):
3027 self.handleTypes.add(name)
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003028 elif (category == 'bitmask'):
Dustin Gravesc900f572016-05-16 11:07:59 -06003029 self.flags.add(name)
Dustin Graves6e4c2be2016-07-19 13:17:35 -06003030 elif (category == 'define'):
3031 if name == 'VK_HEADER_VERSION':
3032 nameElem = typeElem.find('name')
3033 self.headerVersion = noneStr(nameElem.tail).strip()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003034 #
3035 # Struct parameter check generation.
3036 # This is a special case of the <type> tag where the contents are
3037 # interpreted as a set of <member> tags instead of freeform C
3038 # C type declarations. The <member> tags are just like <param>
3039 # tags - they are a declaration of a struct or union member.
3040 # Only simple member declarations are supported (no nested
3041 # structs etc.)
3042 def genStruct(self, typeinfo, typeName):
3043 OutputGenerator.genStruct(self, typeinfo, typeName)
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003044 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
Dustin Graveseec48bf2016-03-02 18:23:29 -07003045 members = typeinfo.elem.findall('.//member')
3046 #
3047 # Iterate over members once to get length parameters for arrays
3048 lens = set()
3049 for member in members:
3050 len = self.getLen(member)
3051 if len:
3052 lens.add(len)
3053 #
3054 # Generate member info
3055 membersInfo = []
3056 for member in members:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003057 # Get the member's type and name
Dustin Graveseec48bf2016-03-02 18:23:29 -07003058 info = self.getTypeNameTuple(member)
3059 type = info[0]
3060 name = info[1]
3061 stypeValue = ''
Dustin Graves29148ff2016-03-23 19:44:00 -06003062 cdecl = self.makeCParamDecl(member, 0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003063 # Process VkStructureType
3064 if type == 'VkStructureType':
3065 # Extract the required struct type value from the comments
3066 # embedded in the original text defining the 'typeinfo' element
3067 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003068 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003069 if result:
3070 value = result.group(0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003071 else:
Dustin Graves94f19142016-05-10 16:44:16 -06003072 value = self.genVkStructureType(typeName)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003073 # Store the required type value
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003074 self.structTypes[typeName] = self.StructType(name=name, value=value)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003075 #
3076 # Store pointer/array/string info
3077 # Check for parameter name in lens set
3078 iscount = False
3079 if name in lens:
3080 iscount = True
3081 # The pNext members are not tagged as optional, but are treated as
3082 # optional for parameter NULL checks. Static array members
3083 # are also treated as optional to skip NULL pointer validation, as
3084 # they won't be NULL.
3085 isstaticarray = self.paramIsStaticArray(member)
3086 isoptional = False
3087 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
3088 isoptional = True
3089 membersInfo.append(self.CommandParam(type=type, name=name,
3090 ispointer=self.paramIsPointer(member),
3091 isstaticarray=isstaticarray,
Dustin Graves29148ff2016-03-23 19:44:00 -06003092 isbool=True if type == 'VkBool32' else False,
3093 israngedenum=True if type in self.enumRanges else False,
3094 isconst=True if 'const' in cdecl else False,
Dustin Graveseec48bf2016-03-02 18:23:29 -07003095 isoptional=isoptional,
3096 iscount=iscount,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003097 noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
Dustin Graveseec48bf2016-03-02 18:23:29 -07003098 len=self.getLen(member),
Dustin Graves58c2f662016-03-08 17:48:20 -07003099 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003100 condition=conditions[name] if conditions and name in conditions else None,
Dustin Graves29148ff2016-03-23 19:44:00 -06003101 cdecl=cdecl))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003102 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003103 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003104 # Capture group (e.g. C "enum" type) info to be used for
3105 # param check code generation.
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003106 # These are concatenated together with other types.
3107 def genGroup(self, groupinfo, groupName):
3108 OutputGenerator.genGroup(self, groupinfo, groupName)
Dustin Graves29148ff2016-03-23 19:44:00 -06003109 groupElem = groupinfo.elem
3110 #
3111 # Store the sType values
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003112 if groupName == 'VkStructureType':
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003113 for elem in groupElem.findall('enum'):
Dustin Graves29148ff2016-03-23 19:44:00 -06003114 self.stypes.append(elem.get('name'))
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003115 elif 'FlagBits' in groupName:
3116 bits = []
3117 for elem in groupElem.findall('enum'):
3118 bits.append(elem.get('name'))
3119 if bits:
3120 self.flagBits[groupName] = bits
Dustin Graves29148ff2016-03-23 19:44:00 -06003121 else:
3122 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
3123 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
3124 expandPrefix = expandName
3125 expandSuffix = ''
3126 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
3127 if expandSuffixMatch:
3128 expandSuffix = '_' + expandSuffixMatch.group()
3129 # Strip off the suffix from the prefix
3130 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
3131 isEnum = ('FLAG_BITS' not in expandPrefix)
3132 if isEnum:
3133 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003134 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003135 # Capture command parameter info to be used for param
3136 # check code generation.
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003137 def genCmd(self, cmdinfo, name):
3138 OutputGenerator.genCmd(self, cmdinfo, name)
3139 if name not in self.blacklist:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003140 params = cmdinfo.elem.findall('param')
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003141 # Get list of array lengths
3142 lens = set()
3143 for param in params:
3144 len = self.getLen(param)
3145 if len:
3146 lens.add(len)
3147 # Get param info
3148 paramsInfo = []
3149 for param in params:
3150 paramInfo = self.getTypeNameTuple(param)
Dustin Graves29148ff2016-03-23 19:44:00 -06003151 cdecl = self.makeCParamDecl(param, 0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003152 # Check for parameter name in lens set
3153 iscount = False
3154 if paramInfo[1] in lens:
3155 iscount = True
3156 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
3157 ispointer=self.paramIsPointer(param),
3158 isstaticarray=self.paramIsStaticArray(param),
Dustin Graves29148ff2016-03-23 19:44:00 -06003159 isbool=True if paramInfo[0] == 'VkBool32' else False,
3160 israngedenum=True if paramInfo[0] in self.enumRanges else False,
3161 isconst=True if 'const' in cdecl else False,
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003162 isoptional=self.paramIsOptional(param),
3163 iscount=iscount,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003164 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003165 len=self.getLen(param),
Dustin Graves58c2f662016-03-08 17:48:20 -07003166 extstructs=None,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003167 condition=None,
Dustin Graves29148ff2016-03-23 19:44:00 -06003168 cdecl=cdecl))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003169 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
3170 #
3171 # Check if the parameter passed in is a pointer
3172 def paramIsPointer(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003173 ispointer = 0
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003174 paramtype = param.find('type')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003175 if (paramtype.tail is not None) and ('*' in paramtype.tail):
3176 ispointer = paramtype.tail.count('*')
Dustin Graves27a912a2016-03-07 17:52:14 -07003177 elif paramtype.text[:4] == 'PFN_':
3178 # Treat function pointer typedefs as a pointer to a single value
3179 ispointer = 1
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003180 return ispointer
3181 #
3182 # Check if the parameter passed in is a static array
3183 def paramIsStaticArray(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003184 isstaticarray = 0
3185 paramname = param.find('name')
3186 if (paramname.tail is not None) and ('[' in paramname.tail):
3187 isstaticarray = paramname.tail.count('[')
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003188 return isstaticarray
3189 #
3190 # Check if the parameter passed in is optional
3191 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
3192 def paramIsOptional(self, param):
3193 # See if the handle is optional
3194 isoptional = False
3195 # Simple, if it's optional, return true
3196 optString = param.attrib.get('optional')
3197 if optString:
3198 if optString == 'true':
3199 isoptional = True
3200 elif ',' in optString:
3201 opts = []
3202 for opt in optString.split(','):
3203 val = opt.strip()
3204 if val == 'true':
3205 opts.append(True)
3206 elif val == 'false':
3207 opts.append(False)
3208 else:
3209 print('Unrecognized len attribute value',val)
3210 isoptional = opts
3211 return isoptional
3212 #
Dustin Graves20fd66f2016-04-18 18:33:21 -06003213 # Check if the handle passed in is optional
3214 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
3215 def isHandleOptional(self, param, lenParam):
3216 # Simple, if it's optional, return true
3217 if param.isoptional:
3218 return True
3219 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
3220 if param.noautovalidity:
3221 return True
3222 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
3223 if lenParam and lenParam.isoptional:
3224 return True
3225 return False
3226 #
Dustin Graves94f19142016-05-10 16:44:16 -06003227 # Generate a VkStructureType based on a structure typename
3228 def genVkStructureType(self, typename):
3229 # Add underscore between lowercase then uppercase
3230 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
3231 # Change to uppercase
3232 value = value.upper()
3233 # Add STRUCTURE_TYPE_
3234 return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
3235 #
3236 # Get the cached VkStructureType value for the specified struct typename, or generate a VkStructureType
3237 # value assuming the struct is defined by a different feature
3238 def getStructType(self, typename):
3239 value = None
3240 if typename in self.structTypes:
3241 value = self.structTypes[typename].value
3242 else:
3243 value = self.genVkStructureType(typename)
Dustin Gravesc900f572016-05-16 11:07:59 -06003244 self.logMsg('diag', 'ParameterValidation: Generating {} for {} structure type that was not defined by the current feature'.format(value, typename))
Dustin Graves94f19142016-05-10 16:44:16 -06003245 return value
3246 #
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003247 # Retrieve the value of the len tag
3248 def getLen(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003249 result = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003250 len = param.attrib.get('len')
3251 if len and len != 'null-terminated':
Dustin Graveseec48bf2016-03-02 18:23:29 -07003252 # For string arrays, 'len' can look like 'count,null-terminated',
3253 # indicating that we have a null terminated array of strings. We
3254 # strip the null-terminated from the 'len' field and only return
3255 # the parameter specifying the string count
3256 if 'null-terminated' in len:
3257 result = len.split(',')[0]
3258 else:
3259 result = len
Mark Lobodzinski3b34af02016-09-27 13:08:15 -06003260 result = str(result).replace('::', '->')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003261 return result
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003262 #
3263 # Retrieve the type and name for a parameter
3264 def getTypeNameTuple(self, param):
3265 type = ''
3266 name = ''
3267 for elem in param:
3268 if elem.tag == 'type':
3269 type = noneStr(elem.text)
3270 elif elem.tag == 'name':
3271 name = noneStr(elem.text)
3272 return (type, name)
3273 #
3274 # Find a named parameter in a parameter list
3275 def getParamByName(self, params, name):
3276 for param in params:
3277 if param.name == name:
3278 return param
3279 return None
Dustin Graveseec48bf2016-03-02 18:23:29 -07003280 #
Dustin Graves3ff520c2016-03-28 16:17:38 -06003281 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
3282 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
3283 def parseLateXMath(self, source):
3284 name = 'ERROR'
3285 decoratedName = 'ERROR'
3286 if 'mathit' in source:
3287 # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
3288 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)
3289 if not match or match.group(1) != match.group(4):
3290 raise 'Unrecognized latexmath expression'
3291 name = match.group(2)
3292 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
3293 else:
3294 # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
3295 match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
3296 name = match.group(1)
3297 decoratedName = '{}/{}'.format(*match.group(1, 2))
3298 return name, decoratedName
3299 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003300 # Get the length paramater record for the specified parameter name
3301 def getLenParam(self, params, name):
3302 lenParam = None
3303 if name:
3304 if '->' in name:
3305 # The count is obtained by dereferencing a member of a struct parameter
Dustin Graves29148ff2016-03-23 19:44:00 -06003306 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003307 isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
3308 condition=None, cdecl=None)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003309 elif 'latexmath' in name:
Dustin Graves3ff520c2016-03-28 16:17:38 -06003310 lenName, decoratedName = self.parseLateXMath(name)
3311 lenParam = self.getParamByName(params, lenName)
3312 # TODO: Zero-check the result produced by the equation?
3313 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
3314 #param = self.getParamByName(params, lenName)
3315 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
3316 # isoptional=param.isoptional, type=param.type, len=param.len,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003317 # isstaticarray=param.isstaticarray, extstructs=param.extstructs,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003318 # noautovalidity=True, condition=None, cdecl=param.cdecl)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003319 else:
3320 lenParam = self.getParamByName(params, name)
3321 return lenParam
3322 #
Mark Lobodzinski739391a2016-03-17 15:08:18 -06003323 # Convert a vulkan.h command declaration into a parameter_validation.h definition
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003324 def getCmdDef(self, cmd):
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003325 #
3326 # Strip the trailing ';' and split into individual lines
3327 lines = cmd.cdecl[:-1].split('\n')
3328 # Replace Vulkan prototype
Dustin Graves080069b2016-04-05 13:48:15 -06003329 lines[0] = 'static bool parameter_validation_' + cmd.name + '('
Dustin Graves842621d2016-03-03 14:17:08 -07003330 # Replace the first argument with debug_report_data, when the first
3331 # argument is a handle (not vkCreateInstance)
3332 reportData = ' debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
3333 if cmd.name != 'vkCreateInstance':
3334 lines[1] = reportData
3335 else:
3336 lines.insert(1, reportData)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003337 return '\n'.join(lines)
3338 #
Dustin Graves35328c12016-02-29 13:35:07 -07003339 # Generate the code to check for a NULL dereference before calling the
3340 # validation function
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003341 def genCheckedLengthCall(self, name, exprs):
Dustin Graves35328c12016-02-29 13:35:07 -07003342 count = name.count('->')
3343 if count:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003344 checkedExpr = []
3345 localIndent = ''
Dustin Graves35328c12016-02-29 13:35:07 -07003346 elements = name.split('->')
3347 # Open the if expression blocks
3348 for i in range(0, count):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003349 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
Dustin Graves35328c12016-02-29 13:35:07 -07003350 localIndent = self.incIndent(localIndent)
3351 # Add the validation expression
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003352 for expr in exprs:
3353 checkedExpr.append(localIndent + expr)
Dustin Graves35328c12016-02-29 13:35:07 -07003354 # Close the if blocks
3355 for i in range(0, count):
3356 localIndent = self.decIndent(localIndent)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003357 checkedExpr.append(localIndent + '}\n')
3358 return [checkedExpr]
Dustin Graves35328c12016-02-29 13:35:07 -07003359 # No if statements were required
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003360 return exprs
3361 #
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003362 # Generate code to check for a specific condition before executing validation code
3363 def genConditionalCall(self, prefix, condition, exprs):
3364 checkedExpr = []
3365 localIndent = ''
3366 formattedCondition = condition.format(prefix)
3367 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
3368 checkedExpr.append(localIndent + '{\n')
3369 localIndent = self.incIndent(localIndent)
3370 for expr in exprs:
3371 checkedExpr.append(localIndent + expr)
3372 localIndent = self.decIndent(localIndent)
3373 checkedExpr.append(localIndent + '}\n')
3374 return [checkedExpr]
3375 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003376 # Generate the sType check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003377 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003378 checkExpr = []
3379 stype = self.structTypes[value.type]
3380 if lenValue:
3381 # This is an array with a pointer to a count value
3382 if lenValue.ispointer:
3383 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Graves32b3c692016-07-22 13:20:44 -06003384 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
3385 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003386 # This is an array with an integer count value
3387 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003388 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
3389 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003390 # This is an individual struct
3391 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003392 checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {});\n'.format(
3393 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003394 return checkExpr
3395 #
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003396 # Generate the handle check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003397 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Graves20fd66f2016-04-18 18:33:21 -06003398 checkExpr = []
3399 if lenValue:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003400 if lenValue.ispointer:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003401 # This is assumed to be an output array with a pointer to a count value
Dustin Graves20fd66f2016-04-18 18:33:21 -06003402 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
Dustin Graves20fd66f2016-04-18 18:33:21 -06003403 else:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003404 # This is an array with an integer count value
Dustin Graves32b3c692016-07-22 13:20:44 -06003405 checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3406 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Dustin Graves20fd66f2016-04-18 18:33:21 -06003407 else:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003408 # This is assumed to be an output handle pointer
Dustin Graves20fd66f2016-04-18 18:33:21 -06003409 raise('Unsupported parameter validation case: Output handles are not NULL checked')
3410 return checkExpr
3411 #
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003412 # Generate check string for an array of VkFlags values
Dustin Graves32b3c692016-07-22 13:20:44 -06003413 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003414 checkExpr = []
3415 flagBitsName = value.type.replace('Flags', 'FlagBits')
3416 if not flagBitsName in self.flagBits:
3417 raise('Unsupported parameter validation case: array of reserved VkFlags')
3418 else:
3419 allFlags = 'All' + flagBitsName
Dustin Graves32b3c692016-07-22 13:20:44 -06003420 checkExpr.append('skipCall |= validate_flags_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix, **postProcSpec))
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003421 return checkExpr
3422 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003423 # Generate pNext check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003424 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003425 checkExpr = []
3426 # Generate an array of acceptable VkStructureType values for pNext
3427 extStructCount = 0
3428 extStructVar = 'NULL'
3429 extStructNames = 'NULL'
3430 if value.extstructs:
3431 structs = value.extstructs.split(',')
Dustin Graves94f19142016-05-10 16:44:16 -06003432 checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.getStructType(s) for s in structs]) + '};\n')
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003433 extStructCount = 'ARRAY_SIZE(allowedStructs)'
3434 extStructVar = 'allowedStructs'
3435 extStructNames = '"' + ', '.join(structs) + '"'
Dustin Graves32b3c692016-07-22 13:20:44 -06003436 checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion);\n'.format(
3437 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003438 return checkExpr
3439 #
3440 # Generate the pointer check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003441 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003442 checkExpr = []
3443 if lenValue:
3444 # This is an array with a pointer to a count value
3445 if lenValue.ispointer:
3446 # If count and array parameters are optional, there will be no validation
3447 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
3448 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Graves32b3c692016-07-22 13:20:44 -06003449 checkExpr.append('skipCall |= validate_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
3450 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003451 # This is an array with an integer count value
3452 else:
3453 # If count and array parameters are optional, there will be no validation
3454 if valueRequired == 'true' or lenValueRequired == 'true':
3455 # Arrays of strings receive special processing
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003456 validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
Dustin Graves32b3c692016-07-22 13:20:44 -06003457 checkExpr.append('skipCall |= {}(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3458 validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003459 if checkExpr:
3460 if lenValue and ('->' in lenValue.name):
3461 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
3462 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
3463 # This is an individual struct that is not allowed to be NULL
3464 elif not value.isoptional:
3465 # Function pointers need a reinterpret_cast to void*
3466 if value.type[:4] == 'PFN_':
Dustin Graves32b3c692016-07-22 13:20:44 -06003467 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003468 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003469 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003470 return checkExpr
Dustin Graveseec48bf2016-03-02 18:23:29 -07003471 #
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003472 # Process struct member validation code, performing name suibstitution if required
Dustin Graves32b3c692016-07-22 13:20:44 -06003473 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
3474 # Build format specifier list
3475 kwargs = {}
3476 if '{postProcPrefix}' in line:
3477 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3478 if type(memberDisplayNamePrefix) is tuple:
3479 kwargs['postProcPrefix'] = 'ParameterName('
3480 else:
3481 kwargs['postProcPrefix'] = postProcSpec['ppp']
3482 if '{postProcSuffix}' in line:
3483 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3484 if type(memberDisplayNamePrefix) is tuple:
3485 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3486 else:
3487 kwargs['postProcSuffix'] = postProcSpec['pps']
3488 if '{postProcInsert}' in line:
3489 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3490 if type(memberDisplayNamePrefix) is tuple:
3491 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3492 else:
3493 kwargs['postProcInsert'] = postProcSpec['ppi']
3494 if '{funcName}' in line:
3495 kwargs['funcName'] = funcName
3496 if '{valuePrefix}' in line:
3497 kwargs['valuePrefix'] = memberNamePrefix
3498 if '{displayNamePrefix}' in line:
3499 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
3500 if type(memberDisplayNamePrefix) is tuple:
3501 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
3502 else:
3503 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
3504
3505 if kwargs:
3506 # Need to escape the C++ curly braces
3507 if 'IndexVector' in line:
3508 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
3509 line = line.replace(' }),', ' }}),')
3510 return line.format(**kwargs)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003511 return line
3512 #
3513 # Process struct validation code for inclusion in function or parent struct validation code
Dustin Graves32b3c692016-07-22 13:20:44 -06003514 def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003515 for line in lines:
3516 if output:
3517 output[-1] += '\n'
3518 if type(line) is list:
3519 for sub in line:
Dustin Graves32b3c692016-07-22 13:20:44 -06003520 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003521 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003522 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003523 return output
3524 #
3525 # Process struct pointer/array validation code, perfoeming name substitution if required
Dustin Graves32b3c692016-07-22 13:20:44 -06003526 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003527 expr = []
3528 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
3529 expr.append('{')
3530 indent = self.incIndent(None)
3531 if lenValue:
3532 # Need to process all elements in the array
3533 indexName = lenValue.name.replace('Count', 'Index')
3534 expr[-1] += '\n'
3535 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
3536 expr.append(indent + '{')
3537 indent = self.incIndent(indent)
3538 # Prefix for value name to display in error message
3539 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
Dustin Graves32b3c692016-07-22 13:20:44 -06003540 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003541 else:
3542 memberNamePrefix = '{}{}->'.format(prefix, value.name)
3543 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
3544 #
3545 # Expand the struct validation lines
Dustin Graves32b3c692016-07-22 13:20:44 -06003546 expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003547 #
3548 if lenValue:
3549 # Close if and for scopes
3550 indent = self.decIndent(indent)
3551 expr.append(indent + '}\n')
3552 expr.append('}\n')
3553 return expr
3554 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003555 # Generate the parameter checking code
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003556 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
3557 lines = [] # Generated lines of code
3558 unused = [] # Unused variable names
Dustin Graveseec48bf2016-03-02 18:23:29 -07003559 for value in values:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003560 usedLines = []
Dustin Graves29148ff2016-03-23 19:44:00 -06003561 lenParam = None
3562 #
Dustin Graves32b3c692016-07-22 13:20:44 -06003563 # Prefix and suffix for post processing of parameter names for struct members. Arrays of structures need special processing to include the array index in the full parameter name.
3564 postProcSpec = {}
3565 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
3566 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
3567 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
3568 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003569 # 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 Gravesb2e9ff72016-04-14 15:03:31 -06003570 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003571 #
3572 # Check for NULL pointers, ignore the inout count parameters that
3573 # will be validated with their associated array
3574 if (value.ispointer or value.isstaticarray) and not value.iscount:
3575 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003576 # Parameters for function argument generation
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003577 req = 'true' # Paramerter cannot be NULL
3578 cpReq = 'true' # Count pointer cannot be NULL
3579 cvReq = 'true' # Count value cannot be 0
Dustin Graves29148ff2016-03-23 19:44:00 -06003580 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Dustin Graveseec48bf2016-03-02 18:23:29 -07003581 #
3582 # Generate required/optional parameter strings for the pointer and count values
3583 if value.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003584 req = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003585 if value.len:
3586 # The parameter is an array with an explicit count parameter
3587 lenParam = self.getLenParam(values, value.len)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003588 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003589 if lenParam.ispointer:
3590 # Count parameters that are pointers are inout
3591 if type(lenParam.isoptional) is list:
3592 if lenParam.isoptional[0]:
Dustin Graves080069b2016-04-05 13:48:15 -06003593 cpReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003594 if lenParam.isoptional[1]:
Dustin Graves080069b2016-04-05 13:48:15 -06003595 cvReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003596 else:
3597 if lenParam.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003598 cpReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003599 else:
3600 if lenParam.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003601 cvReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003602 #
Dustin Gravesc900f572016-05-16 11:07:59 -06003603 # The parameter will not be processes when tagged as 'noautovalidity'
3604 # For the pointer to struct case, the struct pointer will not be validated, but any
3605 # members not tagged as 'noatuvalidity' will be validated
3606 if value.noautovalidity:
3607 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3608 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003609 else:
Dustin Gravesc900f572016-05-16 11:07:59 -06003610 #
3611 # If this is a pointer to a struct with an sType field, verify the type
3612 if value.type in self.structTypes:
Dustin Graves32b3c692016-07-22 13:20:44 -06003613 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003614 # If this is an input handle array that is not allowed to contain NULL handles, verify that none of the handles are VK_NULL_HANDLE
3615 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
Dustin Graves32b3c692016-07-22 13:20:44 -06003616 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003617 elif value.type in self.flags and value.isconst:
Dustin Graves32b3c692016-07-22 13:20:44 -06003618 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003619 elif value.isbool and value.isconst:
Dustin Graves32b3c692016-07-22 13:20:44 -06003620 usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003621 elif value.israngedenum and value.isconst:
3622 enumRange = self.enumRanges[value.type]
Dustin Graves32b3c692016-07-22 13:20:44 -06003623 usedLines.append('skipCall |= validate_ranged_enum_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003624 elif value.name == 'pNext':
3625 # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
3626 if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
Dustin Graves32b3c692016-07-22 13:20:44 -06003627 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003628 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003629 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Graves629259b2016-05-30 16:14:27 -06003630 #
3631 # If this is a pointer to a struct (input), see if it contains members that need to be checked
3632 if value.type in self.validatedStructs and value.isconst:
Dustin Graves32b3c692016-07-22 13:20:44 -06003633 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003634 # Non-pointer types
Dustin Gravesc900f572016-05-16 11:07:59 -06003635 else:
3636 #
3637 # The parameter will not be processes when tagged as 'noautovalidity'
3638 # For the struct case, the struct type will not be validated, but any
3639 # members not tagged as 'noatuvalidity' will be validated
3640 if value.noautovalidity:
3641 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3642 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
3643 else:
3644 if value.type in self.structTypes:
3645 stype = self.structTypes[value.type]
Dustin Graves32b3c692016-07-22 13:20:44 -06003646 usedLines.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false);\n'.format(
3647 funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003648 elif value.type in self.handleTypes:
3649 if not self.isHandleOptional(value, None):
Dustin Graves32b3c692016-07-22 13:20:44 -06003650 usedLines.append('skipCall |= validate_required_handle(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003651 elif value.type in self.flags:
3652 flagBitsName = value.type.replace('Flags', 'FlagBits')
3653 if not flagBitsName in self.flagBits:
Dustin Graves32b3c692016-07-22 13:20:44 -06003654 usedLines.append('skipCall |= validate_reserved_flags(report_data, "{}", {ppp}"{}"{pps}, {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003655 else:
3656 flagsRequired = 'false' if value.isoptional else 'true'
3657 allFlagsName = 'All' + flagBitsName
Dustin Graves32b3c692016-07-22 13:20:44 -06003658 usedLines.append('skipCall |= validate_flags(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003659 elif value.isbool:
Dustin Graves32b3c692016-07-22 13:20:44 -06003660 usedLines.append('skipCall |= validate_bool32(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003661 elif value.israngedenum:
3662 enumRange = self.enumRanges[value.type]
Dustin Graves32b3c692016-07-22 13:20:44 -06003663 usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name, **postProcSpec))
Dustin Graves629259b2016-05-30 16:14:27 -06003664 #
3665 # If this is a struct, see if it contains members that need to be checked
3666 if value.type in self.validatedStructs:
3667 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
3668 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Dustin Graves32b3c692016-07-22 13:20:44 -06003669 usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003670 #
3671 # Append the parameter check to the function body for the current command
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003672 if usedLines:
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003673 # Apply special conditional checks
3674 if value.condition:
3675 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003676 lines += usedLines
Dustin Graves842621d2016-03-03 14:17:08 -07003677 elif not value.iscount:
Dustin Graves29148ff2016-03-23 19:44:00 -06003678 # If no expression was generated for this value, it is unreferenced by the validation function, unless
3679 # it is an array count, which is indirectly referenced for array valiadation.
Dustin Graves842621d2016-03-03 14:17:08 -07003680 unused.append(value.name)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003681 return lines, unused
Dustin Graveseec48bf2016-03-02 18:23:29 -07003682 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003683 # Generate the struct member check code from the captured data
3684 def processStructMemberData(self):
3685 indent = self.incIndent(None)
3686 for struct in self.structMembers:
Dustin Graves29148ff2016-03-23 19:44:00 -06003687 #
3688 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003689 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003690 if lines:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003691 self.validatedStructs[struct.name] = lines
Dustin Graveseec48bf2016-03-02 18:23:29 -07003692 #
3693 # Generate the command param check code from the captured data
3694 def processCmdData(self):
3695 indent = self.incIndent(None)
3696 for command in self.commands:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003697 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
3698 startIndex = 0 if command.name == 'vkCreateInstance' else 1
3699 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003700 if lines:
Dustin Graveseec48bf2016-03-02 18:23:29 -07003701 cmdDef = self.getCmdDef(command) + '\n'
3702 cmdDef += '{\n'
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003703 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
3704 # processed by parameter_validation (except for vkCreateInstance, which does not have a
3705 # handle as its first parameter)
3706 if unused:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003707 for name in unused:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003708 cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
Dustin Graves20fd66f2016-04-18 18:33:21 -06003709 if len(unused) > 0:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003710 cmdDef += '\n'
Dustin Graves080069b2016-04-05 13:48:15 -06003711 cmdDef += indent + 'bool skipCall = false;\n'
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003712 for line in lines:
3713 cmdDef += '\n'
3714 if type(line) is list:
3715 for sub in line:
3716 cmdDef += indent + sub
3717 else:
3718 cmdDef += indent + line
Dustin Graveseec48bf2016-03-02 18:23:29 -07003719 cmdDef += '\n'
3720 cmdDef += indent + 'return skipCall;\n'
3721 cmdDef += '}\n'
3722 self.appendSection('command', cmdDef)