blob: 286849656e06c9192fdb19cada31ac124a615557 [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(","):
2583 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
2584 else:
2585 paramtype = param.find('type')
2586 if paramtype is not None:
2587 paramtype = paramtype.text
2588 else:
2589 paramtype = 'None'
2590 if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
2591 if self.paramIsArray(param) and ('pPipelines' != paramname.text):
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002592 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002593 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
2594 paramdecl += ' }\n'
2595 elif not self.paramIsPointer(param):
2596 # Pointer params are often being created.
2597 # They are not being read from.
2598 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
2599 explicitexternsyncparams = cmd.findall("param[@externsync]")
2600 if (explicitexternsyncparams is not None):
2601 for param in explicitexternsyncparams:
2602 externsyncattrib = param.attrib.get('externsync')
2603 paramname = param.find('name')
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002604 paramdecl += ' // Host access to '
Mike Stroyan845bdc42015-11-02 15:30:20 -07002605 if externsyncattrib == 'true':
2606 if self.paramIsArray(param):
2607 paramdecl += 'each member of ' + paramname.text
2608 elif self.paramIsPointer(param):
2609 paramdecl += 'the object referenced by ' + paramname.text
2610 else:
2611 paramdecl += paramname.text
2612 else:
2613 paramdecl += externsyncattrib
2614 paramdecl += ' must be externally synchronized\n'
2615
2616 # Find and add any "implicit" parameters that are thread unsafe
2617 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2618 if (implicitexternsyncparams is not None):
2619 for elem in implicitexternsyncparams:
2620 paramdecl += ' // '
2621 paramdecl += elem.text
2622 paramdecl += ' must be externally synchronized between host accesses\n'
2623
2624 if (paramdecl == ''):
2625 return None
2626 else:
2627 return paramdecl
2628 def beginFile(self, genOpts):
2629 OutputGenerator.beginFile(self, genOpts)
2630 # C-specific
2631 #
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002632 # Multiple inclusion protection & C++ namespace.
Mike Stroyan845bdc42015-11-02 15:30:20 -07002633 if (genOpts.protectFile and self.genOpts.filename):
2634 headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
2635 write('#ifndef', headerSym, file=self.outFile)
2636 write('#define', headerSym, '1', file=self.outFile)
2637 self.newline()
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002638 write('namespace threading {', file=self.outFile)
Mike Stroyan845bdc42015-11-02 15:30:20 -07002639 self.newline()
2640 #
2641 # User-supplied prefix text, if any (list of strings)
2642 if (genOpts.prefixText):
2643 for s in genOpts.prefixText:
2644 write(s, file=self.outFile)
2645 def endFile(self):
2646 # C-specific
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002647 # Finish C++ namespace and multiple inclusion protection
Mike Stroyan845bdc42015-11-02 15:30:20 -07002648 self.newline()
2649 # record intercepted procedures
2650 write('// intercepts', file=self.outFile)
2651 write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
2652 write('\n'.join(self.intercepts), file=self.outFile)
2653 write('};\n', file=self.outFile)
2654 self.newline()
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002655 write('} // namespace threading', file=self.outFile)
Mike Stroyan845bdc42015-11-02 15:30:20 -07002656 if (self.genOpts.protectFile and self.genOpts.filename):
2657 self.newline()
2658 write('#endif', file=self.outFile)
2659 # Finish processing in superclass
2660 OutputGenerator.endFile(self)
2661 def beginFeature(self, interface, emit):
2662 #write('// starting beginFeature', file=self.outFile)
2663 # Start processing in superclass
2664 OutputGenerator.beginFeature(self, interface, emit)
2665 # C-specific
2666 # Accumulate includes, defines, types, enums, function pointer typedefs,
2667 # end function prototypes separately for this feature. They're only
2668 # printed in endFeature().
2669 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2670 #write('// ending beginFeature', file=self.outFile)
2671 def endFeature(self):
2672 # C-specific
2673 # Actually write the interface to the output file.
2674 #write('// starting endFeature', file=self.outFile)
2675 if (self.emit):
2676 self.newline()
2677 if (self.genOpts.protectFeature):
2678 write('#ifndef', self.featureName, file=self.outFile)
2679 # If type declarations are needed by other features based on
2680 # this one, it may be necessary to suppress the ExtraProtect,
2681 # or move it below the 'for section...' loop.
2682 #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
2683 if (self.featureExtraProtect != None):
2684 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2685 #write('#define', self.featureName, '1', file=self.outFile)
2686 for section in self.TYPE_SECTIONS:
2687 #write('// endFeature writing section'+section, file=self.outFile)
2688 contents = self.sections[section]
2689 if contents:
2690 write('\n'.join(contents), file=self.outFile)
2691 self.newline()
2692 #write('// endFeature looking at self.sections[command]', file=self.outFile)
2693 if (self.sections['command']):
2694 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2695 self.newline()
2696 if (self.featureExtraProtect != None):
2697 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2698 if (self.genOpts.protectFeature):
2699 write('#endif /*', self.featureName, '*/', file=self.outFile)
2700 # Finish processing in superclass
2701 OutputGenerator.endFeature(self)
2702 #write('// ending endFeature', file=self.outFile)
2703 #
2704 # Append a definition to the specified section
2705 def appendSection(self, section, text):
2706 # self.sections[section].append('SECTION: ' + section + '\n')
2707 self.sections[section].append(text)
2708 #
2709 # Type generation
2710 def genType(self, typeinfo, name):
2711 pass
2712 #
2713 # Struct (e.g. C "struct" type) generation.
2714 # This is a special case of the <type> tag where the contents are
2715 # interpreted as a set of <member> tags instead of freeform C
2716 # C type declarations. The <member> tags are just like <param>
2717 # tags - they are a declaration of a struct or union member.
2718 # Only simple member declarations are supported (no nested
2719 # structs etc.)
2720 def genStruct(self, typeinfo, typeName):
2721 OutputGenerator.genStruct(self, typeinfo, typeName)
2722 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
2723 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
2724 for member in typeinfo.elem.findall('.//member'):
2725 body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
2726 body += ';\n'
2727 body += '} ' + typeName + ';\n'
2728 self.appendSection('struct', body)
2729 #
2730 # Group (e.g. C "enum" type) generation.
2731 # These are concatenated together with other types.
2732 def genGroup(self, groupinfo, groupName):
2733 pass
2734 # Enumerant generation
2735 # <enum> tags may specify their values in several ways, but are usually
2736 # just integers.
2737 def genEnum(self, enuminfo, name):
2738 pass
2739 #
2740 # Command generation
2741 def genCmd(self, cmdinfo, name):
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002742 # Commands shadowed by interface functions and are not implemented
2743 interface_functions = [
2744 'vkEnumerateInstanceLayerProperties',
2745 'vkEnumerateInstanceExtensionProperties',
2746 'vkEnumerateDeviceLayerProperties',
2747 ]
2748 if name in interface_functions:
2749 return
Mike Stroyan845bdc42015-11-02 15:30:20 -07002750 special_functions = [
2751 'vkGetDeviceProcAddr',
2752 'vkGetInstanceProcAddr',
2753 'vkCreateDevice',
2754 'vkDestroyDevice',
2755 'vkCreateInstance',
2756 'vkDestroyInstance',
Mike Stroyan845bdc42015-11-02 15:30:20 -07002757 'vkAllocateCommandBuffers',
2758 'vkFreeCommandBuffers',
2759 'vkCreateDebugReportCallbackEXT',
2760 'vkDestroyDebugReportCallbackEXT',
2761 ]
2762 if name in special_functions:
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002763 decls = self.makeCDecls(cmdinfo.elem)
2764 self.appendSection('command', '')
2765 self.appendSection('command', '// declare only')
2766 self.appendSection('command', decls[0])
Chia-I Wu33b4ce82016-05-16 10:02:06 +08002767 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
Mike Stroyan845bdc42015-11-02 15:30:20 -07002768 return
2769 if "KHR" in name:
2770 self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
2771 return
Dustin Graves94f19142016-05-10 16:44:16 -06002772 if ("DebugMarker" in name) and ("EXT" in name):
2773 self.appendSection('command', '// TODO - not wrapping EXT function ' + name)
2774 return
Mike Stroyan845bdc42015-11-02 15:30:20 -07002775 # Determine first if this function needs to be intercepted
2776 startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
2777 if startthreadsafety is None:
2778 return
2779 finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
2780 # record that the function will be intercepted
2781 if (self.featureExtraProtect != None):
2782 self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
Chia-I Wu33b4ce82016-05-16 10:02:06 +08002783 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
Mike Stroyan845bdc42015-11-02 15:30:20 -07002784 if (self.featureExtraProtect != None):
2785 self.intercepts += [ '#endif' ]
2786
2787 OutputGenerator.genCmd(self, cmdinfo, name)
2788 #
2789 decls = self.makeCDecls(cmdinfo.elem)
2790 self.appendSection('command', '')
2791 self.appendSection('command', decls[0][:-1])
2792 self.appendSection('command', '{')
2793 # setup common to call wrappers
2794 # first parameter is always dispatchable
2795 dispatchable_type = cmdinfo.elem.find('param/type').text
2796 dispatchable_name = cmdinfo.elem.find('param/name').text
2797 self.appendSection('command', ' dispatch_key key = get_dispatch_key('+dispatchable_name+');')
2798 self.appendSection('command', ' layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
2799 if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
2800 self.appendSection('command', ' VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
2801 else:
2802 self.appendSection('command', ' VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
2803 # Declare result variable, if any.
2804 resulttype = cmdinfo.elem.find('proto/type')
2805 if (resulttype != None and resulttype.text == 'void'):
2806 resulttype = None
2807 if (resulttype != None):
2808 self.appendSection('command', ' ' + resulttype.text + ' result;')
2809 assignresult = 'result = '
2810 else:
2811 assignresult = ''
2812
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002813 self.appendSection('command', ' bool threadChecks = startMultiThread();')
2814 self.appendSection('command', ' if (threadChecks) {')
2815 self.appendSection('command', " "+"\n ".join(str(startthreadsafety).rstrip().split("\n")))
2816 self.appendSection('command', ' }')
Mike Stroyan845bdc42015-11-02 15:30:20 -07002817 params = cmdinfo.elem.findall('param/name')
2818 paramstext = ','.join([str(param.text) for param in params])
2819 API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
2820 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');')
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002821 self.appendSection('command', ' if (threadChecks) {')
2822 self.appendSection('command', " "+"\n ".join(str(finishthreadsafety).rstrip().split("\n")))
2823 self.appendSection('command', ' } else {')
2824 self.appendSection('command', ' finishMultiThread();')
2825 self.appendSection('command', ' }')
Mike Stroyan845bdc42015-11-02 15:30:20 -07002826 # Return result variable, if any.
2827 if (resulttype != None):
2828 self.appendSection('command', ' return result;')
2829 self.appendSection('command', '}')
Chia-I Wu33b4ce82016-05-16 10:02:06 +08002830 #
2831 # override makeProtoName to drop the "vk" prefix
2832 def makeProtoName(self, name, tail):
2833 return self.genOpts.apientry + name[2:] + tail
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002834
2835# ParamCheckerOutputGenerator - subclass of OutputGenerator.
2836# Generates param checker layer code.
2837#
2838# ---- methods ----
2839# ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
2840# OutputGenerator. Defines additional internal state.
2841# ---- methods overriding base class ----
2842# beginFile(genOpts)
2843# endFile()
2844# beginFeature(interface, emit)
2845# endFeature()
2846# genType(typeinfo,name)
2847# genStruct(typeinfo,name)
2848# genGroup(groupinfo,name)
2849# genEnum(enuminfo, name)
2850# genCmd(cmdinfo)
2851class ParamCheckerOutputGenerator(OutputGenerator):
2852 """Generate ParamChecker code based on XML element attributes"""
2853 # This is an ordered list of sections in the header file.
2854 ALL_SECTIONS = ['command']
2855 def __init__(self,
2856 errFile = sys.stderr,
2857 warnFile = sys.stderr,
2858 diagFile = sys.stdout):
2859 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2860 self.INDENT_SPACES = 4
2861 # Commands to ignore
Dustin Graveseec48bf2016-03-02 18:23:29 -07002862 self.blacklist = [
Dustin Graves842621d2016-03-03 14:17:08 -07002863 'vkGetInstanceProcAddr',
2864 'vkGetDeviceProcAddr',
Dustin Graveseec48bf2016-03-02 18:23:29 -07002865 'vkEnumerateInstanceLayerProperties',
2866 'vkEnumerateInstanceExtensionsProperties',
2867 'vkEnumerateDeviceLayerProperties',
2868 'vkEnumerateDeviceExtensionsProperties',
2869 'vkCreateDebugReportCallbackEXT',
2870 'vkDebugReportMessageEXT']
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06002871 # Validation conditions for some special case struct members that are conditionally validated
2872 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
Dustin Graves6e4c2be2016-07-19 13:17:35 -06002873 # Header version
2874 self.headerVersion = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002875 # Internal state - accumulators for different inner block text
2876 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002877 self.structNames = [] # List of Vulkan struct typenames
2878 self.stypes = [] # Values from the VkStructureType enumeration
2879 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
Dustin Graves20fd66f2016-04-18 18:33:21 -06002880 self.handleTypes = set() # Set of handle type names
Dustin Graveseec48bf2016-03-02 18:23:29 -07002881 self.commands = [] # List of CommandData records for all Vulkan commands
2882 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06002883 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
Dustin Graves29148ff2016-03-23 19:44:00 -06002884 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Dustin Gravesc900f572016-05-16 11:07:59 -06002885 self.flags = set() # Map of flags typenames
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002886 self.flagBits = dict() # Map of flag bits typename to list of values
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002887 # Named tuples to store struct and command data
2888 self.StructType = namedtuple('StructType', ['name', 'value'])
Dustin Graves29148ff2016-03-23 19:44:00 -06002889 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Dustin Graves20fd66f2016-04-18 18:33:21 -06002890 'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
2891 'condition', 'cdecl'])
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002892 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002893 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002894 #
2895 def incIndent(self, indent):
2896 inc = ' ' * self.INDENT_SPACES
2897 if indent:
2898 return indent + inc
2899 return inc
Dustin Graves842621d2016-03-03 14:17:08 -07002900 #
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002901 def decIndent(self, indent):
2902 if indent and (len(indent) > self.INDENT_SPACES):
2903 return indent[:-self.INDENT_SPACES]
2904 return ''
2905 #
2906 def beginFile(self, genOpts):
2907 OutputGenerator.beginFile(self, genOpts)
2908 # C-specific
2909 #
2910 # User-supplied prefix text, if any (list of strings)
2911 if (genOpts.prefixText):
2912 for s in genOpts.prefixText:
2913 write(s, file=self.outFile)
2914 #
2915 # Multiple inclusion protection & C++ wrappers.
2916 if (genOpts.protectFile and self.genOpts.filename):
2917 headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
2918 write('#ifndef', headerSym, file=self.outFile)
2919 write('#define', headerSym, '1', file=self.outFile)
2920 self.newline()
2921 #
2922 # Headers
Dustin Gravesb210cee2016-03-31 09:50:42 -06002923 write('#include <string>', file=self.outFile)
2924 self.newline()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002925 write('#include "vulkan/vulkan.h"', file=self.outFile)
Dustin Graves58c2f662016-03-08 17:48:20 -07002926 write('#include "vk_layer_extension_utils.h"', file=self.outFile)
Mark Lobodzinski739391a2016-03-17 15:08:18 -06002927 write('#include "parameter_validation_utils.h"', file=self.outFile)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002928 #
2929 # Macros
2930 self.newline()
2931 write('#ifndef UNUSED_PARAMETER', file=self.outFile)
2932 write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
2933 write('#endif // UNUSED_PARAMETER', file=self.outFile)
Dustin Gravesb83fc2d2016-05-04 12:56:08 -06002934 #
2935 # Namespace
2936 self.newline()
2937 write('namespace parameter_validation {', file = self.outFile)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002938 def endFile(self):
2939 # C-specific
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002940 self.newline()
Dustin Gravesb83fc2d2016-05-04 12:56:08 -06002941 # Namespace
2942 write('} // namespace parameter_validation', file = self.outFile)
2943 # Finish C++ wrapper and multiple inclusion protection
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002944 if (self.genOpts.protectFile and self.genOpts.filename):
2945 self.newline()
2946 write('#endif', file=self.outFile)
2947 # Finish processing in superclass
2948 OutputGenerator.endFile(self)
2949 def beginFeature(self, interface, emit):
2950 # Start processing in superclass
2951 OutputGenerator.beginFeature(self, interface, emit)
2952 # C-specific
2953 # Accumulate includes, defines, types, enums, function pointer typedefs,
2954 # end function prototypes separately for this feature. They're only
2955 # printed in endFeature().
Dustin Graves6e4c2be2016-07-19 13:17:35 -06002956 self.headerVersion = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002957 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002958 self.structNames = []
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002959 self.stypes = []
2960 self.structTypes = dict()
Dustin Graves20fd66f2016-04-18 18:33:21 -06002961 self.handleTypes = set()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002962 self.commands = []
Dustin Graveseec48bf2016-03-02 18:23:29 -07002963 self.structMembers = []
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06002964 self.validatedStructs = dict()
Dustin Graves29148ff2016-03-23 19:44:00 -06002965 self.enumRanges = dict()
Dustin Gravesc900f572016-05-16 11:07:59 -06002966 self.flags = set()
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002967 self.flagBits = dict()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002968 def endFeature(self):
2969 # C-specific
2970 # Actually write the interface to the output file.
2971 if (self.emit):
2972 self.newline()
2973 # If type declarations are needed by other features based on
2974 # this one, it may be necessary to suppress the ExtraProtect,
2975 # or move it below the 'for section...' loop.
2976 if (self.featureExtraProtect != None):
2977 write('#ifdef', self.featureExtraProtect, file=self.outFile)
Dustin Graveseec48bf2016-03-02 18:23:29 -07002978 # Generate the struct member checking code from the captured data
Dustin Graveseec48bf2016-03-02 18:23:29 -07002979 self.processStructMemberData()
2980 # Generate the command parameter checking code from the captured data
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002981 self.processCmdData()
Dustin Graves6e4c2be2016-07-19 13:17:35 -06002982 # Write the declaration for the HeaderVersion
2983 if self.headerVersion:
2984 write('const uint32_t GeneratedHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
2985 self.newline()
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002986 # Write the declarations for the VkFlags values combining all flag bits
2987 for flag in sorted(self.flags):
Dustin Gravesc900f572016-05-16 11:07:59 -06002988 flagBits = flag.replace('Flags', 'FlagBits')
2989 if flagBits in self.flagBits:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002990 bits = self.flagBits[flagBits]
2991 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
2992 for bit in bits[1:]:
2993 decl += '|' + bit
2994 decl += ';'
2995 write(decl, file=self.outFile)
2996 self.newline()
2997 # Write the parameter validation code to the file
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002998 if (self.sections['command']):
2999 if (self.genOpts.protectProto):
3000 write(self.genOpts.protectProto,
3001 self.genOpts.protectProtoStr, file=self.outFile)
3002 write('\n'.join(self.sections['command']), end='', file=self.outFile)
3003 if (self.featureExtraProtect != None):
3004 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
3005 else:
3006 self.newline()
3007 # Finish processing in superclass
3008 OutputGenerator.endFeature(self)
3009 #
3010 # Append a definition to the specified section
3011 def appendSection(self, section, text):
3012 # self.sections[section].append('SECTION: ' + section + '\n')
3013 self.sections[section].append(text)
3014 #
3015 # Type generation
3016 def genType(self, typeinfo, name):
3017 OutputGenerator.genType(self, typeinfo, name)
3018 typeElem = typeinfo.elem
3019 # If the type is a struct type, traverse the imbedded <member> tags
3020 # generating a structure. Otherwise, emit the tag text.
3021 category = typeElem.get('category')
3022 if (category == 'struct' or category == 'union'):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003023 self.structNames.append(name)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003024 self.genStruct(typeinfo, name)
Dustin Graves20fd66f2016-04-18 18:33:21 -06003025 elif (category == 'handle'):
3026 self.handleTypes.add(name)
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003027 elif (category == 'bitmask'):
Dustin Gravesc900f572016-05-16 11:07:59 -06003028 self.flags.add(name)
Dustin Graves6e4c2be2016-07-19 13:17:35 -06003029 elif (category == 'define'):
3030 if name == 'VK_HEADER_VERSION':
3031 nameElem = typeElem.find('name')
3032 self.headerVersion = noneStr(nameElem.tail).strip()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003033 #
3034 # Struct parameter check generation.
3035 # This is a special case of the <type> tag where the contents are
3036 # interpreted as a set of <member> tags instead of freeform C
3037 # C type declarations. The <member> tags are just like <param>
3038 # tags - they are a declaration of a struct or union member.
3039 # Only simple member declarations are supported (no nested
3040 # structs etc.)
3041 def genStruct(self, typeinfo, typeName):
3042 OutputGenerator.genStruct(self, typeinfo, typeName)
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003043 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
Dustin Graveseec48bf2016-03-02 18:23:29 -07003044 members = typeinfo.elem.findall('.//member')
3045 #
3046 # Iterate over members once to get length parameters for arrays
3047 lens = set()
3048 for member in members:
3049 len = self.getLen(member)
3050 if len:
3051 lens.add(len)
3052 #
3053 # Generate member info
3054 membersInfo = []
3055 for member in members:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003056 # Get the member's type and name
Dustin Graveseec48bf2016-03-02 18:23:29 -07003057 info = self.getTypeNameTuple(member)
3058 type = info[0]
3059 name = info[1]
3060 stypeValue = ''
Dustin Graves29148ff2016-03-23 19:44:00 -06003061 cdecl = self.makeCParamDecl(member, 0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003062 # Process VkStructureType
3063 if type == 'VkStructureType':
3064 # Extract the required struct type value from the comments
3065 # embedded in the original text defining the 'typeinfo' element
3066 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003067 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003068 if result:
3069 value = result.group(0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003070 else:
Dustin Graves94f19142016-05-10 16:44:16 -06003071 value = self.genVkStructureType(typeName)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003072 # Store the required type value
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003073 self.structTypes[typeName] = self.StructType(name=name, value=value)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003074 #
3075 # Store pointer/array/string info
3076 # Check for parameter name in lens set
3077 iscount = False
3078 if name in lens:
3079 iscount = True
3080 # The pNext members are not tagged as optional, but are treated as
3081 # optional for parameter NULL checks. Static array members
3082 # are also treated as optional to skip NULL pointer validation, as
3083 # they won't be NULL.
3084 isstaticarray = self.paramIsStaticArray(member)
3085 isoptional = False
3086 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
3087 isoptional = True
3088 membersInfo.append(self.CommandParam(type=type, name=name,
3089 ispointer=self.paramIsPointer(member),
3090 isstaticarray=isstaticarray,
Dustin Graves29148ff2016-03-23 19:44:00 -06003091 isbool=True if type == 'VkBool32' else False,
3092 israngedenum=True if type in self.enumRanges else False,
3093 isconst=True if 'const' in cdecl else False,
Dustin Graveseec48bf2016-03-02 18:23:29 -07003094 isoptional=isoptional,
3095 iscount=iscount,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003096 noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
Dustin Graveseec48bf2016-03-02 18:23:29 -07003097 len=self.getLen(member),
Dustin Graves58c2f662016-03-08 17:48:20 -07003098 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003099 condition=conditions[name] if conditions and name in conditions else None,
Dustin Graves29148ff2016-03-23 19:44:00 -06003100 cdecl=cdecl))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003101 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003102 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003103 # Capture group (e.g. C "enum" type) info to be used for
3104 # param check code generation.
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003105 # These are concatenated together with other types.
3106 def genGroup(self, groupinfo, groupName):
3107 OutputGenerator.genGroup(self, groupinfo, groupName)
Dustin Graves29148ff2016-03-23 19:44:00 -06003108 groupElem = groupinfo.elem
3109 #
3110 # Store the sType values
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003111 if groupName == 'VkStructureType':
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003112 for elem in groupElem.findall('enum'):
Dustin Graves29148ff2016-03-23 19:44:00 -06003113 self.stypes.append(elem.get('name'))
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003114 elif 'FlagBits' in groupName:
3115 bits = []
3116 for elem in groupElem.findall('enum'):
3117 bits.append(elem.get('name'))
3118 if bits:
3119 self.flagBits[groupName] = bits
Dustin Graves29148ff2016-03-23 19:44:00 -06003120 else:
3121 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
3122 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
3123 expandPrefix = expandName
3124 expandSuffix = ''
3125 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
3126 if expandSuffixMatch:
3127 expandSuffix = '_' + expandSuffixMatch.group()
3128 # Strip off the suffix from the prefix
3129 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
3130 isEnum = ('FLAG_BITS' not in expandPrefix)
3131 if isEnum:
3132 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003133 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003134 # Capture command parameter info to be used for param
3135 # check code generation.
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003136 def genCmd(self, cmdinfo, name):
3137 OutputGenerator.genCmd(self, cmdinfo, name)
3138 if name not in self.blacklist:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003139 params = cmdinfo.elem.findall('param')
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003140 # Get list of array lengths
3141 lens = set()
3142 for param in params:
3143 len = self.getLen(param)
3144 if len:
3145 lens.add(len)
3146 # Get param info
3147 paramsInfo = []
3148 for param in params:
3149 paramInfo = self.getTypeNameTuple(param)
Dustin Graves29148ff2016-03-23 19:44:00 -06003150 cdecl = self.makeCParamDecl(param, 0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003151 # Check for parameter name in lens set
3152 iscount = False
3153 if paramInfo[1] in lens:
3154 iscount = True
3155 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
3156 ispointer=self.paramIsPointer(param),
3157 isstaticarray=self.paramIsStaticArray(param),
Dustin Graves29148ff2016-03-23 19:44:00 -06003158 isbool=True if paramInfo[0] == 'VkBool32' else False,
3159 israngedenum=True if paramInfo[0] in self.enumRanges else False,
3160 isconst=True if 'const' in cdecl else False,
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003161 isoptional=self.paramIsOptional(param),
3162 iscount=iscount,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003163 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003164 len=self.getLen(param),
Dustin Graves58c2f662016-03-08 17:48:20 -07003165 extstructs=None,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003166 condition=None,
Dustin Graves29148ff2016-03-23 19:44:00 -06003167 cdecl=cdecl))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003168 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
3169 #
3170 # Check if the parameter passed in is a pointer
3171 def paramIsPointer(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003172 ispointer = 0
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003173 paramtype = param.find('type')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003174 if (paramtype.tail is not None) and ('*' in paramtype.tail):
3175 ispointer = paramtype.tail.count('*')
Dustin Graves27a912a2016-03-07 17:52:14 -07003176 elif paramtype.text[:4] == 'PFN_':
3177 # Treat function pointer typedefs as a pointer to a single value
3178 ispointer = 1
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003179 return ispointer
3180 #
3181 # Check if the parameter passed in is a static array
3182 def paramIsStaticArray(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003183 isstaticarray = 0
3184 paramname = param.find('name')
3185 if (paramname.tail is not None) and ('[' in paramname.tail):
3186 isstaticarray = paramname.tail.count('[')
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003187 return isstaticarray
3188 #
3189 # Check if the parameter passed in is optional
3190 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
3191 def paramIsOptional(self, param):
3192 # See if the handle is optional
3193 isoptional = False
3194 # Simple, if it's optional, return true
3195 optString = param.attrib.get('optional')
3196 if optString:
3197 if optString == 'true':
3198 isoptional = True
3199 elif ',' in optString:
3200 opts = []
3201 for opt in optString.split(','):
3202 val = opt.strip()
3203 if val == 'true':
3204 opts.append(True)
3205 elif val == 'false':
3206 opts.append(False)
3207 else:
3208 print('Unrecognized len attribute value',val)
3209 isoptional = opts
3210 return isoptional
3211 #
Dustin Graves20fd66f2016-04-18 18:33:21 -06003212 # Check if the handle passed in is optional
3213 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
3214 def isHandleOptional(self, param, lenParam):
3215 # Simple, if it's optional, return true
3216 if param.isoptional:
3217 return True
3218 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
3219 if param.noautovalidity:
3220 return True
3221 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
3222 if lenParam and lenParam.isoptional:
3223 return True
3224 return False
3225 #
Dustin Graves94f19142016-05-10 16:44:16 -06003226 # Generate a VkStructureType based on a structure typename
3227 def genVkStructureType(self, typename):
3228 # Add underscore between lowercase then uppercase
3229 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
3230 # Change to uppercase
3231 value = value.upper()
3232 # Add STRUCTURE_TYPE_
3233 return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
3234 #
3235 # Get the cached VkStructureType value for the specified struct typename, or generate a VkStructureType
3236 # value assuming the struct is defined by a different feature
3237 def getStructType(self, typename):
3238 value = None
3239 if typename in self.structTypes:
3240 value = self.structTypes[typename].value
3241 else:
3242 value = self.genVkStructureType(typename)
Dustin Gravesc900f572016-05-16 11:07:59 -06003243 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 -06003244 return value
3245 #
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003246 # Retrieve the value of the len tag
3247 def getLen(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003248 result = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003249 len = param.attrib.get('len')
3250 if len and len != 'null-terminated':
Dustin Graveseec48bf2016-03-02 18:23:29 -07003251 # For string arrays, 'len' can look like 'count,null-terminated',
3252 # indicating that we have a null terminated array of strings. We
3253 # strip the null-terminated from the 'len' field and only return
3254 # the parameter specifying the string count
3255 if 'null-terminated' in len:
3256 result = len.split(',')[0]
3257 else:
3258 result = len
3259 return result
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003260 #
3261 # Retrieve the type and name for a parameter
3262 def getTypeNameTuple(self, param):
3263 type = ''
3264 name = ''
3265 for elem in param:
3266 if elem.tag == 'type':
3267 type = noneStr(elem.text)
3268 elif elem.tag == 'name':
3269 name = noneStr(elem.text)
3270 return (type, name)
3271 #
3272 # Find a named parameter in a parameter list
3273 def getParamByName(self, params, name):
3274 for param in params:
3275 if param.name == name:
3276 return param
3277 return None
Dustin Graveseec48bf2016-03-02 18:23:29 -07003278 #
Dustin Graves3ff520c2016-03-28 16:17:38 -06003279 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
3280 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
3281 def parseLateXMath(self, source):
3282 name = 'ERROR'
3283 decoratedName = 'ERROR'
3284 if 'mathit' in source:
3285 # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
3286 match = re.match(r'latexmath\s*\:\s*\[\s*\$\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\$\s*\]', source)
3287 if not match or match.group(1) != match.group(4):
3288 raise 'Unrecognized latexmath expression'
3289 name = match.group(2)
3290 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
3291 else:
3292 # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
3293 match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
3294 name = match.group(1)
3295 decoratedName = '{}/{}'.format(*match.group(1, 2))
3296 return name, decoratedName
3297 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003298 # Get the length paramater record for the specified parameter name
3299 def getLenParam(self, params, name):
3300 lenParam = None
3301 if name:
3302 if '->' in name:
3303 # The count is obtained by dereferencing a member of a struct parameter
Dustin Graves29148ff2016-03-23 19:44:00 -06003304 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003305 isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
3306 condition=None, cdecl=None)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003307 elif 'latexmath' in name:
Dustin Graves3ff520c2016-03-28 16:17:38 -06003308 lenName, decoratedName = self.parseLateXMath(name)
3309 lenParam = self.getParamByName(params, lenName)
3310 # TODO: Zero-check the result produced by the equation?
3311 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
3312 #param = self.getParamByName(params, lenName)
3313 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
3314 # isoptional=param.isoptional, type=param.type, len=param.len,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003315 # isstaticarray=param.isstaticarray, extstructs=param.extstructs,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003316 # noautovalidity=True, condition=None, cdecl=param.cdecl)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003317 else:
3318 lenParam = self.getParamByName(params, name)
3319 return lenParam
3320 #
Mark Lobodzinski739391a2016-03-17 15:08:18 -06003321 # Convert a vulkan.h command declaration into a parameter_validation.h definition
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003322 def getCmdDef(self, cmd):
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003323 #
3324 # Strip the trailing ';' and split into individual lines
3325 lines = cmd.cdecl[:-1].split('\n')
3326 # Replace Vulkan prototype
Dustin Graves080069b2016-04-05 13:48:15 -06003327 lines[0] = 'static bool parameter_validation_' + cmd.name + '('
Dustin Graves842621d2016-03-03 14:17:08 -07003328 # Replace the first argument with debug_report_data, when the first
3329 # argument is a handle (not vkCreateInstance)
3330 reportData = ' debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
3331 if cmd.name != 'vkCreateInstance':
3332 lines[1] = reportData
3333 else:
3334 lines.insert(1, reportData)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003335 return '\n'.join(lines)
3336 #
Dustin Graves35328c12016-02-29 13:35:07 -07003337 # Generate the code to check for a NULL dereference before calling the
3338 # validation function
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003339 def genCheckedLengthCall(self, name, exprs):
Dustin Graves35328c12016-02-29 13:35:07 -07003340 count = name.count('->')
3341 if count:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003342 checkedExpr = []
3343 localIndent = ''
Dustin Graves35328c12016-02-29 13:35:07 -07003344 elements = name.split('->')
3345 # Open the if expression blocks
3346 for i in range(0, count):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003347 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
Dustin Graves35328c12016-02-29 13:35:07 -07003348 localIndent = self.incIndent(localIndent)
3349 # Add the validation expression
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003350 for expr in exprs:
3351 checkedExpr.append(localIndent + expr)
Dustin Graves35328c12016-02-29 13:35:07 -07003352 # Close the if blocks
3353 for i in range(0, count):
3354 localIndent = self.decIndent(localIndent)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003355 checkedExpr.append(localIndent + '}\n')
3356 return [checkedExpr]
Dustin Graves35328c12016-02-29 13:35:07 -07003357 # No if statements were required
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003358 return exprs
3359 #
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003360 # Generate code to check for a specific condition before executing validation code
3361 def genConditionalCall(self, prefix, condition, exprs):
3362 checkedExpr = []
3363 localIndent = ''
3364 formattedCondition = condition.format(prefix)
3365 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
3366 checkedExpr.append(localIndent + '{\n')
3367 localIndent = self.incIndent(localIndent)
3368 for expr in exprs:
3369 checkedExpr.append(localIndent + expr)
3370 localIndent = self.decIndent(localIndent)
3371 checkedExpr.append(localIndent + '}\n')
3372 return [checkedExpr]
3373 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003374 # Generate the sType check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003375 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003376 checkExpr = []
3377 stype = self.structTypes[value.type]
3378 if lenValue:
3379 # This is an array with a pointer to a count value
3380 if lenValue.ispointer:
3381 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Graves32b3c692016-07-22 13:20:44 -06003382 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
3383 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003384 # This is an array with an integer count value
3385 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003386 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
3387 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003388 # This is an individual struct
3389 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003390 checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {});\n'.format(
3391 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003392 return checkExpr
3393 #
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003394 # Generate the handle check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003395 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Graves20fd66f2016-04-18 18:33:21 -06003396 checkExpr = []
3397 if lenValue:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003398 if lenValue.ispointer:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003399 # This is assumed to be an output array with a pointer to a count value
Dustin Graves20fd66f2016-04-18 18:33:21 -06003400 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
Dustin Graves20fd66f2016-04-18 18:33:21 -06003401 else:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003402 # This is an array with an integer count value
Dustin Graves32b3c692016-07-22 13:20:44 -06003403 checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3404 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Dustin Graves20fd66f2016-04-18 18:33:21 -06003405 else:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003406 # This is assumed to be an output handle pointer
Dustin Graves20fd66f2016-04-18 18:33:21 -06003407 raise('Unsupported parameter validation case: Output handles are not NULL checked')
3408 return checkExpr
3409 #
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003410 # Generate check string for an array of VkFlags values
Dustin Graves32b3c692016-07-22 13:20:44 -06003411 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003412 checkExpr = []
3413 flagBitsName = value.type.replace('Flags', 'FlagBits')
3414 if not flagBitsName in self.flagBits:
3415 raise('Unsupported parameter validation case: array of reserved VkFlags')
3416 else:
3417 allFlags = 'All' + flagBitsName
Dustin Graves32b3c692016-07-22 13:20:44 -06003418 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 -06003419 return checkExpr
3420 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003421 # Generate pNext check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003422 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003423 checkExpr = []
3424 # Generate an array of acceptable VkStructureType values for pNext
3425 extStructCount = 0
3426 extStructVar = 'NULL'
3427 extStructNames = 'NULL'
3428 if value.extstructs:
3429 structs = value.extstructs.split(',')
Dustin Graves94f19142016-05-10 16:44:16 -06003430 checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.getStructType(s) for s in structs]) + '};\n')
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003431 extStructCount = 'ARRAY_SIZE(allowedStructs)'
3432 extStructVar = 'allowedStructs'
3433 extStructNames = '"' + ', '.join(structs) + '"'
Dustin Graves32b3c692016-07-22 13:20:44 -06003434 checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion);\n'.format(
3435 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003436 return checkExpr
3437 #
3438 # Generate the pointer check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003439 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003440 checkExpr = []
3441 if lenValue:
3442 # This is an array with a pointer to a count value
3443 if lenValue.ispointer:
3444 # If count and array parameters are optional, there will be no validation
3445 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
3446 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Graves32b3c692016-07-22 13:20:44 -06003447 checkExpr.append('skipCall |= validate_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
3448 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003449 # This is an array with an integer count value
3450 else:
3451 # If count and array parameters are optional, there will be no validation
3452 if valueRequired == 'true' or lenValueRequired == 'true':
3453 # Arrays of strings receive special processing
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003454 validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
Dustin Graves32b3c692016-07-22 13:20:44 -06003455 checkExpr.append('skipCall |= {}(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3456 validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003457 if checkExpr:
3458 if lenValue and ('->' in lenValue.name):
3459 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
3460 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
3461 # This is an individual struct that is not allowed to be NULL
3462 elif not value.isoptional:
3463 # Function pointers need a reinterpret_cast to void*
3464 if value.type[:4] == 'PFN_':
Dustin Graves32b3c692016-07-22 13:20:44 -06003465 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 -06003466 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003467 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 -06003468 return checkExpr
Dustin Graveseec48bf2016-03-02 18:23:29 -07003469 #
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003470 # Process struct member validation code, performing name suibstitution if required
Dustin Graves32b3c692016-07-22 13:20:44 -06003471 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
3472 # Build format specifier list
3473 kwargs = {}
3474 if '{postProcPrefix}' in line:
3475 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3476 if type(memberDisplayNamePrefix) is tuple:
3477 kwargs['postProcPrefix'] = 'ParameterName('
3478 else:
3479 kwargs['postProcPrefix'] = postProcSpec['ppp']
3480 if '{postProcSuffix}' in line:
3481 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3482 if type(memberDisplayNamePrefix) is tuple:
3483 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3484 else:
3485 kwargs['postProcSuffix'] = postProcSpec['pps']
3486 if '{postProcInsert}' in line:
3487 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3488 if type(memberDisplayNamePrefix) is tuple:
3489 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3490 else:
3491 kwargs['postProcInsert'] = postProcSpec['ppi']
3492 if '{funcName}' in line:
3493 kwargs['funcName'] = funcName
3494 if '{valuePrefix}' in line:
3495 kwargs['valuePrefix'] = memberNamePrefix
3496 if '{displayNamePrefix}' in line:
3497 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
3498 if type(memberDisplayNamePrefix) is tuple:
3499 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
3500 else:
3501 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
3502
3503 if kwargs:
3504 # Need to escape the C++ curly braces
3505 if 'IndexVector' in line:
3506 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
3507 line = line.replace(' }),', ' }}),')
3508 return line.format(**kwargs)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003509 return line
3510 #
3511 # Process struct validation code for inclusion in function or parent struct validation code
Dustin Graves32b3c692016-07-22 13:20:44 -06003512 def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003513 for line in lines:
3514 if output:
3515 output[-1] += '\n'
3516 if type(line) is list:
3517 for sub in line:
Dustin Graves32b3c692016-07-22 13:20:44 -06003518 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003519 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003520 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003521 return output
3522 #
3523 # Process struct pointer/array validation code, perfoeming name substitution if required
Dustin Graves32b3c692016-07-22 13:20:44 -06003524 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003525 expr = []
3526 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
3527 expr.append('{')
3528 indent = self.incIndent(None)
3529 if lenValue:
3530 # Need to process all elements in the array
3531 indexName = lenValue.name.replace('Count', 'Index')
3532 expr[-1] += '\n'
3533 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
3534 expr.append(indent + '{')
3535 indent = self.incIndent(indent)
3536 # Prefix for value name to display in error message
3537 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
Dustin Graves32b3c692016-07-22 13:20:44 -06003538 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003539 else:
3540 memberNamePrefix = '{}{}->'.format(prefix, value.name)
3541 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
3542 #
3543 # Expand the struct validation lines
Dustin Graves32b3c692016-07-22 13:20:44 -06003544 expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003545 #
3546 if lenValue:
3547 # Close if and for scopes
3548 indent = self.decIndent(indent)
3549 expr.append(indent + '}\n')
3550 expr.append('}\n')
3551 return expr
3552 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003553 # Generate the parameter checking code
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003554 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
3555 lines = [] # Generated lines of code
3556 unused = [] # Unused variable names
Dustin Graveseec48bf2016-03-02 18:23:29 -07003557 for value in values:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003558 usedLines = []
Dustin Graves29148ff2016-03-23 19:44:00 -06003559 lenParam = None
3560 #
Dustin Graves32b3c692016-07-22 13:20:44 -06003561 # 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.
3562 postProcSpec = {}
3563 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
3564 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
3565 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
3566 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003567 # 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 -06003568 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003569 #
3570 # Check for NULL pointers, ignore the inout count parameters that
3571 # will be validated with their associated array
3572 if (value.ispointer or value.isstaticarray) and not value.iscount:
3573 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003574 # Parameters for function argument generation
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003575 req = 'true' # Paramerter cannot be NULL
3576 cpReq = 'true' # Count pointer cannot be NULL
3577 cvReq = 'true' # Count value cannot be 0
Dustin Graves29148ff2016-03-23 19:44:00 -06003578 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Dustin Graveseec48bf2016-03-02 18:23:29 -07003579 #
3580 # Generate required/optional parameter strings for the pointer and count values
3581 if value.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003582 req = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003583 if value.len:
3584 # The parameter is an array with an explicit count parameter
3585 lenParam = self.getLenParam(values, value.len)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003586 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003587 if lenParam.ispointer:
3588 # Count parameters that are pointers are inout
3589 if type(lenParam.isoptional) is list:
3590 if lenParam.isoptional[0]:
Dustin Graves080069b2016-04-05 13:48:15 -06003591 cpReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003592 if lenParam.isoptional[1]:
Dustin Graves080069b2016-04-05 13:48:15 -06003593 cvReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003594 else:
3595 if lenParam.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003596 cpReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003597 else:
3598 if lenParam.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003599 cvReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003600 #
Dustin Gravesc900f572016-05-16 11:07:59 -06003601 # The parameter will not be processes when tagged as 'noautovalidity'
3602 # For the pointer to struct case, the struct pointer will not be validated, but any
3603 # members not tagged as 'noatuvalidity' will be validated
3604 if value.noautovalidity:
3605 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3606 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003607 else:
Dustin Gravesc900f572016-05-16 11:07:59 -06003608 #
3609 # If this is a pointer to a struct with an sType field, verify the type
3610 if value.type in self.structTypes:
Dustin Graves32b3c692016-07-22 13:20:44 -06003611 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003612 # If this is an input handle array that is not allowed to contain NULL handles, verify that none of the handles are VK_NULL_HANDLE
3613 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
Dustin Graves32b3c692016-07-22 13:20:44 -06003614 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003615 elif value.type in self.flags and value.isconst:
Dustin Graves32b3c692016-07-22 13:20:44 -06003616 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003617 elif value.isbool and value.isconst:
Dustin Graves32b3c692016-07-22 13:20:44 -06003618 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 -06003619 elif value.israngedenum and value.isconst:
3620 enumRange = self.enumRanges[value.type]
Dustin Graves32b3c692016-07-22 13:20:44 -06003621 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 -06003622 elif value.name == 'pNext':
3623 # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
3624 if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
Dustin Graves32b3c692016-07-22 13:20:44 -06003625 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003626 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003627 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Graves629259b2016-05-30 16:14:27 -06003628 #
3629 # If this is a pointer to a struct (input), see if it contains members that need to be checked
3630 if value.type in self.validatedStructs and value.isconst:
Dustin Graves32b3c692016-07-22 13:20:44 -06003631 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003632 # Non-pointer types
Dustin Gravesc900f572016-05-16 11:07:59 -06003633 else:
3634 #
3635 # The parameter will not be processes when tagged as 'noautovalidity'
3636 # For the struct case, the struct type will not be validated, but any
3637 # members not tagged as 'noatuvalidity' will be validated
3638 if value.noautovalidity:
3639 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3640 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
3641 else:
3642 if value.type in self.structTypes:
3643 stype = self.structTypes[value.type]
Dustin Graves32b3c692016-07-22 13:20:44 -06003644 usedLines.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false);\n'.format(
3645 funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003646 elif value.type in self.handleTypes:
3647 if not self.isHandleOptional(value, None):
Dustin Graves32b3c692016-07-22 13:20:44 -06003648 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 -06003649 elif value.type in self.flags:
3650 flagBitsName = value.type.replace('Flags', 'FlagBits')
3651 if not flagBitsName in self.flagBits:
Dustin Graves32b3c692016-07-22 13:20:44 -06003652 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 -06003653 else:
3654 flagsRequired = 'false' if value.isoptional else 'true'
3655 allFlagsName = 'All' + flagBitsName
Dustin Graves32b3c692016-07-22 13:20:44 -06003656 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 -06003657 elif value.isbool:
Dustin Graves32b3c692016-07-22 13:20:44 -06003658 usedLines.append('skipCall |= validate_bool32(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003659 elif value.israngedenum:
3660 enumRange = self.enumRanges[value.type]
Dustin Graves32b3c692016-07-22 13:20:44 -06003661 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 -06003662 #
3663 # If this is a struct, see if it contains members that need to be checked
3664 if value.type in self.validatedStructs:
3665 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
3666 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Dustin Graves32b3c692016-07-22 13:20:44 -06003667 usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003668 #
3669 # Append the parameter check to the function body for the current command
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003670 if usedLines:
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003671 # Apply special conditional checks
3672 if value.condition:
3673 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003674 lines += usedLines
Dustin Graves842621d2016-03-03 14:17:08 -07003675 elif not value.iscount:
Dustin Graves29148ff2016-03-23 19:44:00 -06003676 # If no expression was generated for this value, it is unreferenced by the validation function, unless
3677 # it is an array count, which is indirectly referenced for array valiadation.
Dustin Graves842621d2016-03-03 14:17:08 -07003678 unused.append(value.name)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003679 return lines, unused
Dustin Graveseec48bf2016-03-02 18:23:29 -07003680 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003681 # Generate the struct member check code from the captured data
3682 def processStructMemberData(self):
3683 indent = self.incIndent(None)
3684 for struct in self.structMembers:
Dustin Graves29148ff2016-03-23 19:44:00 -06003685 #
3686 # 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 -06003687 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003688 if lines:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003689 self.validatedStructs[struct.name] = lines
Dustin Graveseec48bf2016-03-02 18:23:29 -07003690 #
3691 # Generate the command param check code from the captured data
3692 def processCmdData(self):
3693 indent = self.incIndent(None)
3694 for command in self.commands:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003695 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
3696 startIndex = 0 if command.name == 'vkCreateInstance' else 1
3697 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003698 if lines:
Dustin Graveseec48bf2016-03-02 18:23:29 -07003699 cmdDef = self.getCmdDef(command) + '\n'
3700 cmdDef += '{\n'
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003701 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
3702 # processed by parameter_validation (except for vkCreateInstance, which does not have a
3703 # handle as its first parameter)
3704 if unused:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003705 for name in unused:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003706 cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
Dustin Graves20fd66f2016-04-18 18:33:21 -06003707 if len(unused) > 0:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003708 cmdDef += '\n'
Dustin Graves080069b2016-04-05 13:48:15 -06003709 cmdDef += indent + 'bool skipCall = false;\n'
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003710 for line in lines:
3711 cmdDef += '\n'
3712 if type(line) is list:
3713 for sub in line:
3714 cmdDef += indent + sub
3715 else:
3716 cmdDef += indent + line
Dustin Graveseec48bf2016-03-02 18:23:29 -07003717 cmdDef += '\n'
3718 cmdDef += indent + 'return skipCall;\n'
3719 cmdDef += '}\n'
3720 self.appendSection('command', cmdDef)