blob: 740f66e9e29bbd7b287f0f257637bdbc40a26e83 [file] [log] [blame]
Mike Stroyana451fa82016-01-07 15:35:37 -07001#!/usr/bin/python3 -i
2import os,re,sys
3
4def write( *args, **kwargs ):
5 file = kwargs.pop('file',sys.stdout)
6 end = kwargs.pop( 'end','\n')
7 file.write( ' '.join([str(arg) for arg in args]) )
8 file.write( end )
9
10# noneStr - returns string argument, or "" if argument is None.
11# Used in converting lxml Elements into text.
12# str - string to convert
13def noneStr(str):
14 if (str):
15 return str
16 else:
17 return ""
18
19# enquote - returns string argument with surrounding quotes,
20# for serialization into Python code.
21def enquote(str):
22 if (str):
23 return "'" + str + "'"
24 else:
25 return None
26
27# Primary sort key for regSortFeatures.
28# Sorts by category of the feature name string:
29# Core API features (those defined with a <feature> tag)
30# ARB/KHR/OES (Khronos extensions)
31# other (EXT/vendor extensions)
32# This will need changing for Vulkan!
33def regSortCategoryKey(feature):
34 if (feature.elem.tag == 'feature'):
35 return 0
36 elif (feature.category == 'ARB' or
37 feature.category == 'KHR' or
38 feature.category == 'OES'):
39 return 1
40 else:
41 return 2
42
43# Secondary sort key for regSortFeatures.
44# Sorts by extension name.
45def regSortNameKey(feature):
46 return feature.name
47
48# Second sort key for regSortFeatures.
49# Sorts by feature version. <extension> elements all have version number "0"
50def regSortFeatureVersionKey(feature):
51 return float(feature.version)
52
53# Tertiary sort key for regSortFeatures.
54# Sorts by extension number. <feature> elements all have extension number 0.
55def regSortExtensionNumberKey(feature):
56 return int(feature.number)
57
58# regSortFeatures - default sort procedure for features.
59# Sorts by primary key of feature category ('feature' or 'extension')
60# then by version number (for features)
61# then by extension number (for extensions)
62def regSortFeatures(featureList):
63 featureList.sort(key = regSortExtensionNumberKey)
64 featureList.sort(key = regSortFeatureVersionKey)
65 featureList.sort(key = regSortCategoryKey)
66
67# GeneratorOptions - base class for options used during header production
68# These options are target language independent, and used by
69# Registry.apiGen() and by base OutputGenerator objects.
70#
71# Members
72# filename - name of file to generate, or None to write to stdout.
73# apiname - string matching <api> 'apiname' attribute, e.g. 'gl'.
74# profile - string specifying API profile , e.g. 'core', or None.
75# versions - regex matching API versions to process interfaces for.
76# Normally '.*' or '[0-9]\.[0-9]' to match all defined versions.
77# emitversions - regex matching API versions to actually emit
78# interfaces for (though all requested versions are considered
79# when deciding which interfaces to generate). For GL 4.3 glext.h,
80# this might be '1\.[2-5]|[2-4]\.[0-9]'.
81# defaultExtensions - If not None, a string which must in its
82# entirety match the pattern in the "supported" attribute of
83# the <extension>. Defaults to None. Usually the same as apiname.
84# addExtensions - regex matching names of additional extensions
85# to include. Defaults to None.
86# removeExtensions - regex matching names of extensions to
87# remove (after defaultExtensions and addExtensions). Defaults
88# to None.
89# sortProcedure - takes a list of FeatureInfo objects and sorts
90# them in place to a preferred order in the generated output.
91# Default is core API versions, ARB/KHR/OES extensions, all
92# other extensions, alphabetically within each group.
93# The regex patterns can be None or empty, in which case they match
94# nothing.
95class GeneratorOptions:
96 """Represents options during header production from an API registry"""
97 def __init__(self,
98 filename = None,
99 apiname = None,
100 profile = None,
101 versions = '.*',
102 emitversions = '.*',
103 defaultExtensions = None,
104 addExtensions = None,
105 removeExtensions = None,
106 sortProcedure = regSortFeatures):
107 self.filename = filename
108 self.apiname = apiname
109 self.profile = profile
110 self.versions = self.emptyRegex(versions)
111 self.emitversions = self.emptyRegex(emitversions)
112 self.defaultExtensions = defaultExtensions
113 self.addExtensions = self.emptyRegex(addExtensions)
114 self.removeExtensions = self.emptyRegex(removeExtensions)
115 self.sortProcedure = sortProcedure
116 #
117 # Substitute a regular expression which matches no version
118 # or extension names for None or the empty string.
119 def emptyRegex(self,pat):
120 if (pat == None or pat == ''):
121 return '_nomatch_^'
122 else:
123 return pat
124
125# CGeneratorOptions - subclass of GeneratorOptions.
126#
127# Adds options used by COutputGenerator objects during C language header
128# generation.
129#
130# Additional members
131# prefixText - list of strings to prefix generated header with
132# (usually a copyright statement + calling convention macros).
133# protectFile - True if multiple inclusion protection should be
134# generated (based on the filename) around the entire header.
135# protectFeature - True if #ifndef..#endif protection should be
136# generated around a feature interface in the header file.
137# genFuncPointers - True if function pointer typedefs should be
138# generated
139# protectProto - If conditional protection should be generated
140# around prototype declarations, set to either '#ifdef'
141# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
142# to require opt-out (#ifndef protectProtoStr). Otherwise
143# set to None.
144# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
145# declarations, if protectProto is set
146# apicall - string to use for the function declaration prefix,
147# such as APICALL on Windows.
148# apientry - string to use for the calling convention macro,
149# in typedefs, such as APIENTRY.
150# apientryp - string to use for the calling convention macro
151# in function pointer typedefs, such as APIENTRYP.
152# indentFuncProto - True if prototype declarations should put each
153# parameter on a separate line
154# indentFuncPointer - True if typedefed function pointers should put each
155# parameter on a separate line
156# alignFuncParam - if nonzero and parameters are being put on a
157# separate line, align parameter names at the specified column
158class CGeneratorOptions(GeneratorOptions):
159 """Represents options during C interface generation for headers"""
160 def __init__(self,
161 filename = None,
162 apiname = None,
163 profile = None,
164 versions = '.*',
165 emitversions = '.*',
166 defaultExtensions = None,
167 addExtensions = None,
168 removeExtensions = None,
169 sortProcedure = regSortFeatures,
170 prefixText = "",
171 genFuncPointers = True,
172 protectFile = True,
173 protectFeature = True,
174 protectProto = None,
175 protectProtoStr = None,
176 apicall = '',
177 apientry = '',
178 apientryp = '',
179 indentFuncProto = True,
180 indentFuncPointer = False,
181 alignFuncParam = 0):
182 GeneratorOptions.__init__(self, filename, apiname, profile,
183 versions, emitversions, defaultExtensions,
184 addExtensions, removeExtensions, sortProcedure)
185 self.prefixText = prefixText
186 self.genFuncPointers = genFuncPointers
187 self.protectFile = protectFile
188 self.protectFeature = protectFeature
189 self.protectProto = protectProto
190 self.protectProtoStr = protectProtoStr
191 self.apicall = apicall
192 self.apientry = apientry
193 self.apientryp = apientryp
194 self.indentFuncProto = indentFuncProto
195 self.indentFuncPointer = indentFuncPointer
196 self.alignFuncParam = alignFuncParam
197
198# DocGeneratorOptions - subclass of GeneratorOptions.
199#
200# Shares many members with CGeneratorOptions, since
201# both are writing C-style declarations:
202#
203# prefixText - list of strings to prefix generated header with
204# (usually a copyright statement + calling convention macros).
205# apicall - string to use for the function declaration prefix,
206# such as APICALL on Windows.
207# apientry - string to use for the calling convention macro,
208# in typedefs, such as APIENTRY.
209# apientryp - string to use for the calling convention macro
210# in function pointer typedefs, such as APIENTRYP.
211# genDirectory - directory into which to generate include files
212# indentFuncProto - True if prototype declarations should put each
213# parameter on a separate line
214# indentFuncPointer - True if typedefed function pointers should put each
215# parameter on a separate line
216# alignFuncParam - if nonzero and parameters are being put on a
217# separate line, align parameter names at the specified column
218#
219# Additional members:
220#
221class DocGeneratorOptions(GeneratorOptions):
222 """Represents options during C interface generation for Asciidoc"""
223 def __init__(self,
224 filename = None,
225 apiname = None,
226 profile = None,
227 versions = '.*',
228 emitversions = '.*',
229 defaultExtensions = None,
230 addExtensions = None,
231 removeExtensions = None,
232 sortProcedure = regSortFeatures,
233 prefixText = "",
234 apicall = '',
235 apientry = '',
236 apientryp = '',
237 genDirectory = 'gen',
238 indentFuncProto = True,
239 indentFuncPointer = False,
240 alignFuncParam = 0,
241 expandEnumerants = True):
242 GeneratorOptions.__init__(self, filename, apiname, profile,
243 versions, emitversions, defaultExtensions,
244 addExtensions, removeExtensions, sortProcedure)
245 self.prefixText = prefixText
246 self.apicall = apicall
247 self.apientry = apientry
248 self.apientryp = apientryp
249 self.genDirectory = genDirectory
250 self.indentFuncProto = indentFuncProto
251 self.indentFuncPointer = indentFuncPointer
252 self.alignFuncParam = alignFuncParam
253 self.expandEnumerants = expandEnumerants
254
Mike Stroyan8849f9a2015-11-02 15:30:20 -0700255# ThreadGeneratorOptions - subclass of GeneratorOptions.
256#
257# Adds options used by COutputGenerator objects during C language header
258# generation.
259#
260# Additional members
261# prefixText - list of strings to prefix generated header with
262# (usually a copyright statement + calling convention macros).
263# protectFile - True if multiple inclusion protection should be
264# generated (based on the filename) around the entire header.
265# protectFeature - True if #ifndef..#endif protection should be
266# generated around a feature interface in the header file.
267# genFuncPointers - True if function pointer typedefs should be
268# generated
269# protectProto - True if #ifdef..#endif protection should be
270# generated around prototype declarations
271# protectProtoStr - #ifdef symbol to use around prototype
272# declarations, if protected
273# apicall - string to use for the function declaration prefix,
274# such as APICALL on Windows.
275# apientry - string to use for the calling convention macro,
276# in typedefs, such as APIENTRY.
277# apientryp - string to use for the calling convention macro
278# in function pointer typedefs, such as APIENTRYP.
279# indentFuncProto - True if prototype declarations should put each
280# parameter on a separate line
281# indentFuncPointer - True if typedefed function pointers should put each
282# parameter on a separate line
283# alignFuncParam - if nonzero and parameters are being put on a
284# separate line, align parameter names at the specified column
285class ThreadGeneratorOptions(GeneratorOptions):
286 """Represents options during C interface generation for headers"""
287 def __init__(self,
288 filename = None,
289 apiname = None,
290 profile = None,
291 versions = '.*',
292 emitversions = '.*',
293 defaultExtensions = None,
294 addExtensions = None,
295 removeExtensions = None,
296 sortProcedure = regSortFeatures,
297 prefixText = "",
298 genFuncPointers = True,
299 protectFile = True,
300 protectFeature = True,
301 protectProto = True,
302 protectProtoStr = True,
303 apicall = '',
304 apientry = '',
305 apientryp = '',
306 indentFuncProto = True,
307 indentFuncPointer = False,
308 alignFuncParam = 0):
309 GeneratorOptions.__init__(self, filename, apiname, profile,
310 versions, emitversions, defaultExtensions,
311 addExtensions, removeExtensions, sortProcedure)
312 self.prefixText = prefixText
313 self.genFuncPointers = genFuncPointers
314 self.protectFile = protectFile
315 self.protectFeature = protectFeature
316 self.protectProto = protectProto
317 self.protectProtoStr = protectProtoStr
318 self.apicall = apicall
319 self.apientry = apientry
320 self.apientryp = apientryp
321 self.indentFuncProto = indentFuncProto
322 self.indentFuncPointer = indentFuncPointer
323 self.alignFuncParam = alignFuncParam
324
325
Mike Stroyana451fa82016-01-07 15:35:37 -0700326# OutputGenerator - base class for generating API interfaces.
327# Manages basic logic, logging, and output file control
328# Derived classes actually generate formatted output.
329#
330# ---- methods ----
331# OutputGenerator(errFile, warnFile, diagFile)
332# errFile, warnFile, diagFile - file handles to write errors,
333# warnings, diagnostics to. May be None to not write.
334# logMsg(level, *args) - log messages of different categories
335# level - 'error', 'warn', or 'diag'. 'error' will also
336# raise a UserWarning exception
337# *args - print()-style arguments
338# setExtMap(map) - specify a dictionary map from extension names to
339# numbers, used in creating values for extension enumerants.
340# beginFile(genOpts) - start a new interface file
341# genOpts - GeneratorOptions controlling what's generated and how
342# endFile() - finish an interface file, closing it when done
343# beginFeature(interface, emit) - write interface for a feature
344# and tag generated features as having been done.
345# interface - element for the <version> / <extension> to generate
346# emit - actually write to the header only when True
347# endFeature() - finish an interface.
348# genType(typeinfo,name) - generate interface for a type
349# typeinfo - TypeInfo for a type
350# genStruct(typeinfo,name) - generate interface for a C "struct" type.
351# typeinfo - TypeInfo for a type interpreted as a struct
352# genGroup(groupinfo,name) - generate interface for a group of enums (C "enum")
353# groupinfo - GroupInfo for a group
354# genEnum(enuminfo, name) - generate interface for an enum (constant)
355# enuminfo - EnumInfo for an enum
356# name - enum name
357# genCmd(cmdinfo) - generate interface for a command
358# cmdinfo - CmdInfo for a command
359# makeCDecls(cmd) - return C prototype and function pointer typedef for a
360# <command> Element, as a list of two strings
361# cmd - Element for the <command>
362# newline() - print a newline to the output file (utility function)
363#
364class OutputGenerator:
365 """Generate specified API interfaces in a specific style, such as a C header"""
366 def __init__(self,
367 errFile = sys.stderr,
368 warnFile = sys.stderr,
369 diagFile = sys.stdout):
370 self.outFile = None
371 self.errFile = errFile
372 self.warnFile = warnFile
373 self.diagFile = diagFile
374 # Internal state
375 self.featureName = None
376 self.genOpts = None
377 self.registry = None
378 # Used for extension enum value generation
379 self.extBase = 1000000000
380 self.extBlockSize = 1000
381 #
382 # logMsg - write a message of different categories to different
383 # destinations.
384 # level -
385 # 'diag' (diagnostic, voluminous)
386 # 'warn' (warning)
387 # 'error' (fatal error - raises exception after logging)
388 # *args - print()-style arguments to direct to corresponding log
389 def logMsg(self, level, *args):
390 """Log a message at the given level. Can be ignored or log to a file"""
391 if (level == 'error'):
392 strfile = io.StringIO()
393 write('ERROR:', *args, file=strfile)
394 if (self.errFile != None):
395 write(strfile.getvalue(), file=self.errFile)
396 raise UserWarning(strfile.getvalue())
397 elif (level == 'warn'):
398 if (self.warnFile != None):
399 write('WARNING:', *args, file=self.warnFile)
400 elif (level == 'diag'):
401 if (self.diagFile != None):
402 write('DIAG:', *args, file=self.diagFile)
403 else:
404 raise UserWarning(
405 '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
406 #
407 # enumToValue - parses and converts an <enum> tag into a value.
408 # Returns a list
409 # first element - integer representation of the value, or None
410 # if needsNum is False. The value must be a legal number
411 # if needsNum is True.
412 # second element - string representation of the value
413 # There are several possible representations of values.
414 # A 'value' attribute simply contains the value.
415 # A 'bitpos' attribute defines a value by specifying the bit
416 # position which is set in that value.
417 # A 'offset','extbase','extends' triplet specifies a value
418 # as an offset to a base value defined by the specified
419 # 'extbase' extension name, which is then cast to the
420 # typename specified by 'extends'. This requires probing
421 # the registry database, and imbeds knowledge of the
422 # Vulkan extension enum scheme in this function.
423 def enumToValue(self, elem, needsNum):
424 name = elem.get('name')
425 numVal = None
426 if ('value' in elem.keys()):
427 value = elem.get('value')
428 # print('About to translate value =', value, 'type =', type(value))
429 if (needsNum):
430 numVal = int(value, 0)
431 # If there's a non-integer, numeric 'type' attribute (e.g. 'u' or
432 # 'ull'), append it to the string value.
433 # t = enuminfo.elem.get('type')
434 # if (t != None and t != '' and t != 'i' and t != 's'):
435 # value += enuminfo.type
436 self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
437 return [numVal, value]
438 if ('bitpos' in elem.keys()):
439 value = elem.get('bitpos')
440 numVal = int(value, 0)
441 numVal = 1 << numVal
442 value = '0x%08x' % numVal
443 self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
444 return [numVal, value]
445 if ('offset' in elem.keys()):
446 # Obtain values in the mapping from the attributes
447 enumNegative = False
448 offset = int(elem.get('offset'),0)
449 extnumber = int(elem.get('extnumber'),0)
450 extends = elem.get('extends')
451 if ('dir' in elem.keys()):
452 enumNegative = True
453 self.logMsg('diag', 'Enum', name, 'offset =', offset,
454 'extnumber =', extnumber, 'extends =', extends,
455 'enumNegative =', enumNegative)
456 # Now determine the actual enumerant value, as defined
457 # in the "Layers and Extensions" appendix of the spec.
458 numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
459 if (enumNegative):
460 numVal = -numVal
461 value = '%d' % numVal
462 # More logic needed!
463 self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
464 return [numVal, value]
465 return [None, None]
466 #
467 def beginFile(self, genOpts):
468 self.genOpts = genOpts
469 #
470 # Open specified output file. Not done in constructor since a
471 # Generator can be used without writing to a file.
472 if (self.genOpts.filename != None):
473 self.outFile = open(self.genOpts.filename, 'w')
474 else:
475 self.outFile = sys.stdout
476 def endFile(self):
477 self.errFile and self.errFile.flush()
478 self.warnFile and self.warnFile.flush()
479 self.diagFile and self.diagFile.flush()
480 self.outFile.flush()
481 if (self.outFile != sys.stdout and self.outFile != sys.stderr):
482 self.outFile.close()
483 self.genOpts = None
484 #
485 def beginFeature(self, interface, emit):
486 self.emit = emit
487 self.featureName = interface.get('name')
488 # If there's an additional 'protect' attribute in the feature, save it
489 self.featureExtraProtect = interface.get('protect')
490 def endFeature(self):
491 # Derived classes responsible for emitting feature
492 self.featureName = None
493 self.featureExtraProtect = None
494 # Utility method to validate we're generating something only inside a
495 # <feature> tag
496 def validateFeature(self, featureType, featureName):
497 if (self.featureName == None):
498 raise UserWarning('Attempt to generate', featureType, name,
499 'when not in feature')
500 #
501 # Type generation
502 def genType(self, typeinfo, name):
503 self.validateFeature('type', name)
504 #
505 # Struct (e.g. C "struct" type) generation
506 def genStruct(self, typeinfo, name):
507 self.validateFeature('struct', name)
508 #
509 # Group (e.g. C "enum" type) generation
510 def genGroup(self, groupinfo, name):
511 self.validateFeature('group', name)
512 #
513 # Enumerant (really, constant) generation
514 def genEnum(self, enuminfo, name):
515 self.validateFeature('enum', name)
516 #
517 # Command generation
518 def genCmd(self, cmd, name):
519 self.validateFeature('command', name)
520 #
521 # Utility functions - turn a <proto> <name> into C-language prototype
522 # and typedef declarations for that name.
523 # name - contents of <name> tag
524 # tail - whatever text follows that tag in the Element
525 def makeProtoName(self, name, tail):
526 return self.genOpts.apientry + name + tail
527 def makeTypedefName(self, name, tail):
528 return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')'
529 #
530 # makeCParamDecl - return a string which is an indented, formatted
531 # declaration for a <param> or <member> block (e.g. function parameter
532 # or structure/union member).
533 # param - Element (<param> or <member>) to format
534 # aligncol - if non-zero, attempt to align the nested <name> element
535 # at this column
536 def makeCParamDecl(self, param, aligncol):
537 paramdecl = ' ' + noneStr(param.text)
538 for elem in param:
539 text = noneStr(elem.text)
540 tail = noneStr(elem.tail)
541 if (elem.tag == 'name' and aligncol > 0):
542 self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam)
543 # Align at specified column, if possible
544 paramdecl = paramdecl.rstrip()
545 oldLen = len(paramdecl)
546 paramdecl = paramdecl.ljust(aligncol)
547 newLen = len(paramdecl)
548 self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl)
549 paramdecl += text + tail
550 return paramdecl
551 #
552 # getCParamTypeLength - return the length of the type field is an indented, formatted
553 # declaration for a <param> or <member> block (e.g. function parameter
554 # or structure/union member).
555 # param - Element (<param> or <member>) to identify
556 def getCParamTypeLength(self, param):
557 paramdecl = ' ' + noneStr(param.text)
558 for elem in param:
559 text = noneStr(elem.text)
560 tail = noneStr(elem.tail)
561 if (elem.tag == 'name'):
562 # Align at specified column, if possible
563 newLen = len(paramdecl.rstrip())
564 self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen)
565 paramdecl += text + tail
566 return newLen
567 #
568 # makeCDecls - return C prototype and function pointer typedef for a
569 # command, as a two-element list of strings.
570 # cmd - Element containing a <command> tag
571 def makeCDecls(self, cmd):
572 """Generate C function pointer typedef for <command> Element"""
573 proto = cmd.find('proto')
574 params = cmd.findall('param')
575 # Begin accumulating prototype and typedef strings
576 pdecl = self.genOpts.apicall
577 tdecl = 'typedef '
578 #
579 # Insert the function return type/name.
580 # For prototypes, add APIENTRY macro before the name
581 # For typedefs, add (APIENTRY *<name>) around the name and
582 # use the PFN_cmdnameproc naming convention.
583 # Done by walking the tree for <proto> element by element.
584 # lxml.etree has elem.text followed by (elem[i], elem[i].tail)
585 # for each child element and any following text
586 # Leading text
587 pdecl += noneStr(proto.text)
588 tdecl += noneStr(proto.text)
589 # For each child element, if it's a <name> wrap in appropriate
590 # declaration. Otherwise append its contents and tail contents.
591 for elem in proto:
592 text = noneStr(elem.text)
593 tail = noneStr(elem.tail)
594 if (elem.tag == 'name'):
595 pdecl += self.makeProtoName(text, tail)
596 tdecl += self.makeTypedefName(text, tail)
597 else:
598 pdecl += text + tail
599 tdecl += text + tail
600 # Now add the parameter declaration list, which is identical
601 # for prototypes and typedefs. Concatenate all the text from
602 # a <param> node without the tags. No tree walking required
603 # since all tags are ignored.
604 # Uses: self.indentFuncProto
605 # self.indentFuncPointer
606 # self.alignFuncParam
607 # Might be able to doubly-nest the joins, e.g.
608 # ','.join(('_'.join([l[i] for i in range(0,len(l))])
609 n = len(params)
610 # Indented parameters
611 if n > 0:
612 indentdecl = '(\n'
613 for i in range(0,n):
614 paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam)
615 if (i < n - 1):
616 paramdecl += ',\n'
617 else:
618 paramdecl += ');'
619 indentdecl += paramdecl
620 else:
621 indentdecl = '(void);'
622 # Non-indented parameters
623 paramdecl = '('
624 if n > 0:
625 for i in range(0,n):
626 paramdecl += ''.join([t for t in params[i].itertext()])
627 if (i < n - 1):
628 paramdecl += ', '
629 else:
630 paramdecl += 'void'
631 paramdecl += ");";
632 return [ pdecl + indentdecl, tdecl + paramdecl ]
633 #
634 def newline(self):
635 write('', file=self.outFile)
636
637 def setRegistry(self, registry):
638 self.registry = registry
639 #
640
641# COutputGenerator - subclass of OutputGenerator.
642# Generates C-language API interfaces.
643#
644# ---- methods ----
645# COutputGenerator(errFile, warnFile, diagFile) - args as for
646# OutputGenerator. Defines additional internal state.
647# ---- methods overriding base class ----
648# beginFile(genOpts)
649# endFile()
650# beginFeature(interface, emit)
651# endFeature()
652# genType(typeinfo,name)
653# genStruct(typeinfo,name)
654# genGroup(groupinfo,name)
655# genEnum(enuminfo, name)
656# genCmd(cmdinfo)
657class COutputGenerator(OutputGenerator):
658 """Generate specified API interfaces in a specific style, such as a C header"""
659 # This is an ordered list of sections in the header file.
660 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
661 'group', 'bitmask', 'funcpointer', 'struct']
662 ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
663 def __init__(self,
664 errFile = sys.stderr,
665 warnFile = sys.stderr,
666 diagFile = sys.stdout):
667 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
668 # Internal state - accumulators for different inner block text
669 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
670 #
671 def beginFile(self, genOpts):
672 OutputGenerator.beginFile(self, genOpts)
673 # C-specific
674 #
675 # Multiple inclusion protection & C++ wrappers.
676 if (genOpts.protectFile and self.genOpts.filename):
677 headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
678 write('#ifndef', headerSym, file=self.outFile)
679 write('#define', headerSym, '1', file=self.outFile)
680 self.newline()
681 write('#ifdef __cplusplus', file=self.outFile)
682 write('extern "C" {', file=self.outFile)
683 write('#endif', file=self.outFile)
684 self.newline()
685 #
686 # User-supplied prefix text, if any (list of strings)
687 if (genOpts.prefixText):
688 for s in genOpts.prefixText:
689 write(s, file=self.outFile)
690 #
691 # Some boilerplate describing what was generated - this
692 # will probably be removed later since the extensions
693 # pattern may be very long.
694 # write('/* Generated C header for:', file=self.outFile)
695 # write(' * API:', genOpts.apiname, file=self.outFile)
696 # if (genOpts.profile):
697 # write(' * Profile:', genOpts.profile, file=self.outFile)
698 # write(' * Versions considered:', genOpts.versions, file=self.outFile)
699 # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile)
700 # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile)
701 # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile)
702 # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile)
703 # write(' */', file=self.outFile)
704 def endFile(self):
705 # C-specific
706 # Finish C++ wrapper and multiple inclusion protection
707 self.newline()
708 write('#ifdef __cplusplus', file=self.outFile)
709 write('}', file=self.outFile)
710 write('#endif', file=self.outFile)
711 if (self.genOpts.protectFile and self.genOpts.filename):
712 self.newline()
713 write('#endif', file=self.outFile)
714 # Finish processing in superclass
715 OutputGenerator.endFile(self)
716 def beginFeature(self, interface, emit):
717 # Start processing in superclass
718 OutputGenerator.beginFeature(self, interface, emit)
719 # C-specific
720 # Accumulate includes, defines, types, enums, function pointer typedefs,
721 # end function prototypes separately for this feature. They're only
722 # printed in endFeature().
723 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
724 def endFeature(self):
725 # C-specific
726 # Actually write the interface to the output file.
727 if (self.emit):
728 self.newline()
729 if (self.genOpts.protectFeature):
730 write('#ifndef', self.featureName, file=self.outFile)
731 # If type declarations are needed by other features based on
732 # this one, it may be necessary to suppress the ExtraProtect,
733 # or move it below the 'for section...' loop.
734 if (self.featureExtraProtect != None):
735 write('#ifdef', self.featureExtraProtect, file=self.outFile)
736 write('#define', self.featureName, '1', file=self.outFile)
737 for section in self.TYPE_SECTIONS:
738 contents = self.sections[section]
739 if contents:
740 write('\n'.join(contents), file=self.outFile)
741 self.newline()
742 if (self.genOpts.genFuncPointers and self.sections['commandPointer']):
743 write('\n'.join(self.sections['commandPointer']), file=self.outFile)
744 self.newline()
745 if (self.sections['command']):
746 if (self.genOpts.protectProto):
747 write(self.genOpts.protectProto,
748 self.genOpts.protectProtoStr, file=self.outFile)
749 write('\n'.join(self.sections['command']), end='', file=self.outFile)
750 if (self.genOpts.protectProto):
751 write('#endif', file=self.outFile)
752 else:
753 self.newline()
754 if (self.featureExtraProtect != None):
755 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
756 if (self.genOpts.protectFeature):
757 write('#endif /*', self.featureName, '*/', file=self.outFile)
758 # Finish processing in superclass
759 OutputGenerator.endFeature(self)
760 #
761 # Append a definition to the specified section
762 def appendSection(self, section, text):
763 # self.sections[section].append('SECTION: ' + section + '\n')
764 self.sections[section].append(text)
765 #
766 # Type generation
767 def genType(self, typeinfo, name):
768 OutputGenerator.genType(self, typeinfo, name)
769 typeElem = typeinfo.elem
770 # If the type is a struct type, traverse the imbedded <member> tags
771 # generating a structure. Otherwise, emit the tag text.
772 category = typeElem.get('category')
773 if (category == 'struct' or category == 'union'):
774 self.genStruct(typeinfo, name)
775 else:
776 # Replace <apientry /> tags with an APIENTRY-style string
777 # (from self.genOpts). Copy other text through unchanged.
778 # If the resulting text is an empty string, don't emit it.
779 s = noneStr(typeElem.text)
780 for elem in typeElem:
781 if (elem.tag == 'apientry'):
782 s += self.genOpts.apientry + noneStr(elem.tail)
783 else:
784 s += noneStr(elem.text) + noneStr(elem.tail)
785 if s:
786 # Add extra newline after multi-line entries.
787 if '\n' in s:
788 s += '\n'
789 self.appendSection(category, s)
790 #
791 # Struct (e.g. C "struct" type) generation.
792 # This is a special case of the <type> tag where the contents are
793 # interpreted as a set of <member> tags instead of freeform C
794 # C type declarations. The <member> tags are just like <param>
795 # tags - they are a declaration of a struct or union member.
796 # Only simple member declarations are supported (no nested
797 # structs etc.)
798 def genStruct(self, typeinfo, typeName):
799 OutputGenerator.genStruct(self, typeinfo, typeName)
800 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
801 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
802 targetLen = 0;
803 for member in typeinfo.elem.findall('.//member'):
804 targetLen = max(targetLen, self.getCParamTypeLength(member))
805 for member in typeinfo.elem.findall('.//member'):
806 body += self.makeCParamDecl(member, targetLen + 4)
807 body += ';\n'
808 body += '} ' + typeName + ';\n'
809 self.appendSection('struct', body)
810 #
811 # Group (e.g. C "enum" type) generation.
812 # These are concatenated together with other types.
813 def genGroup(self, groupinfo, groupName):
814 OutputGenerator.genGroup(self, groupinfo, groupName)
815 groupElem = groupinfo.elem
816 # See if this group needs min/max/num/padding at end
817 expand = 'expand' in groupElem.keys()
818 if (expand):
819 expandPrefix = groupElem.get('expand')
820 # Prefix
821 body = "\ntypedef enum " + groupName + " {\n"
822
823 # Loop over the nested 'enum' tags. Keep track of the minimum and
824 # maximum numeric values, if they can be determined; but only for
825 # core API enumerants, not extension enumerants. This is inferred
826 # by looking for 'extends' attributes.
827 minName = None
828 for elem in groupElem.findall('enum'):
829 # Convert the value to an integer and use that to track min/max.
830 # Values of form -(number) are accepted but nothing more complex.
831 # Should catch exceptions here for more complex constructs. Not yet.
832 (numVal,strVal) = self.enumToValue(elem, True)
833 name = elem.get('name')
834 body += " " + name + " = " + strVal + ",\n"
835 if (expand and elem.get('extends') is None):
836 if (minName == None):
837 minName = maxName = name
838 minValue = maxValue = numVal
839 elif (numVal < minValue):
840 minName = name
841 minValue = numVal
842 elif (numVal > maxValue):
843 maxName = name
844 maxValue = numVal
845 # Generate min/max value tokens and a range-padding enum. Need some
846 # additional padding to generate correct names...
847 if (expand):
848 body += " " + expandPrefix + "_BEGIN_RANGE = " + minName + ",\n"
849 body += " " + expandPrefix + "_END_RANGE = " + maxName + ",\n"
850 body += " " + expandPrefix + "_RANGE_SIZE = (" + maxName + " - " + minName + " + 1),\n"
851 body += " " + expandPrefix + "_MAX_ENUM = 0x7FFFFFFF\n"
852 # Postfix
853 body += "} " + groupName + ";"
854 if groupElem.get('type') == 'bitmask':
855 section = 'bitmask'
856 else:
857 section = 'group'
858 self.appendSection(section, body)
859 # Enumerant generation
860 # <enum> tags may specify their values in several ways, but are usually
861 # just integers.
862 def genEnum(self, enuminfo, name):
863 OutputGenerator.genEnum(self, enuminfo, name)
864 (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
865 body = '#define ' + name.ljust(33) + ' ' + strVal
866 self.appendSection('enum', body)
867 #
868 # Command generation
869 def genCmd(self, cmdinfo, name):
870 OutputGenerator.genCmd(self, cmdinfo, name)
871 #
872 decls = self.makeCDecls(cmdinfo.elem)
873 self.appendSection('command', decls[0] + '\n')
874 if (self.genOpts.genFuncPointers):
875 self.appendSection('commandPointer', decls[1])
876
877# DocOutputGenerator - subclass of OutputGenerator.
878# Generates AsciiDoc includes with C-language API interfaces, for reference
879# pages and the Vulkan specification. Similar to COutputGenerator, but
880# each interface is written into a different file as determined by the
881# options, only actual C types are emitted, and none of the boilerplate
882# preprocessor code is emitted.
883#
884# ---- methods ----
885# DocOutputGenerator(errFile, warnFile, diagFile) - args as for
886# OutputGenerator. Defines additional internal state.
887# ---- methods overriding base class ----
888# beginFile(genOpts)
889# endFile()
890# beginFeature(interface, emit)
891# endFeature()
892# genType(typeinfo,name)
893# genStruct(typeinfo,name)
894# genGroup(groupinfo,name)
895# genEnum(enuminfo, name)
896# genCmd(cmdinfo)
897class DocOutputGenerator(OutputGenerator):
898 """Generate specified API interfaces in a specific style, such as a C header"""
899 def __init__(self,
900 errFile = sys.stderr,
901 warnFile = sys.stderr,
902 diagFile = sys.stdout):
903 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
904 #
905 def beginFile(self, genOpts):
906 OutputGenerator.beginFile(self, genOpts)
907 def endFile(self):
908 OutputGenerator.endFile(self)
909 def beginFeature(self, interface, emit):
910 # Start processing in superclass
911 OutputGenerator.beginFeature(self, interface, emit)
912 def endFeature(self):
913 # Finish processing in superclass
914 OutputGenerator.endFeature(self)
915 #
916 # Generate an include file
917 #
918 # directory - subdirectory to put file in
919 # basename - base name of the file
920 # contents - contents of the file (Asciidoc boilerplate aside)
921 def writeInclude(self, directory, basename, contents):
922 # Create file
923 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
924 self.logMsg('diag', '# Generating include file:', filename)
925 fp = open(filename, 'w')
926 # Asciidoc anchor
927 write('[[{0},{0}]]'.format(basename), file=fp)
928 write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp)
929 write('------------------------------------------------------------------------------', file=fp)
930 write(contents, file=fp)
931 write('------------------------------------------------------------------------------', file=fp)
932 fp.close()
933 #
934 # Type generation
935 def genType(self, typeinfo, name):
936 OutputGenerator.genType(self, typeinfo, name)
937 typeElem = typeinfo.elem
938 # If the type is a struct type, traverse the imbedded <member> tags
939 # generating a structure. Otherwise, emit the tag text.
940 category = typeElem.get('category')
941 if (category == 'struct' or category == 'union'):
942 self.genStruct(typeinfo, name)
943 else:
944 # Replace <apientry /> tags with an APIENTRY-style string
945 # (from self.genOpts). Copy other text through unchanged.
946 # If the resulting text is an empty string, don't emit it.
947 s = noneStr(typeElem.text)
948 for elem in typeElem:
949 if (elem.tag == 'apientry'):
950 s += self.genOpts.apientry + noneStr(elem.tail)
951 else:
952 s += noneStr(elem.text) + noneStr(elem.tail)
953 if (len(s) > 0):
954 if (category == 'bitmask'):
955 self.writeInclude('flags', name, s + '\n')
956 elif (category == 'enum'):
957 self.writeInclude('enums', name, s + '\n')
958 elif (category == 'funcpointer'):
959 self.writeInclude('funcpointers', name, s+ '\n')
960 else:
961 self.logMsg('diag', '# NOT writing include file for type:',
962 name, 'category: ', category)
963 else:
964 self.logMsg('diag', '# NOT writing empty include file for type', name)
965 #
966 # Struct (e.g. C "struct" type) generation.
967 # This is a special case of the <type> tag where the contents are
968 # interpreted as a set of <member> tags instead of freeform C
969 # C type declarations. The <member> tags are just like <param>
970 # tags - they are a declaration of a struct or union member.
971 # Only simple member declarations are supported (no nested
972 # structs etc.)
973 def genStruct(self, typeinfo, typeName):
974 OutputGenerator.genStruct(self, typeinfo, typeName)
975 s = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
976 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
977 targetLen = 0;
978 for member in typeinfo.elem.findall('.//member'):
979 targetLen = max(targetLen, self.getCParamTypeLength(member))
980 for member in typeinfo.elem.findall('.//member'):
981 s += self.makeCParamDecl(member, targetLen + 4)
982 s += ';\n'
983 s += '} ' + typeName + ';'
984 self.writeInclude('structs', typeName, s)
985 #
986 # Group (e.g. C "enum" type) generation.
987 # These are concatenated together with other types.
988 def genGroup(self, groupinfo, groupName):
989 OutputGenerator.genGroup(self, groupinfo, groupName)
990 groupElem = groupinfo.elem
991 # See if this group needs min/max/num/padding at end
992 expand = self.genOpts.expandEnumerants and ('expand' in groupElem.keys())
993 if (expand):
994 expandPrefix = groupElem.get('expand')
995 # Prefix
996 s = "typedef enum " + groupName + " {\n"
997
998 # Loop over the nested 'enum' tags. Keep track of the minimum and
999 # maximum numeric values, if they can be determined.
1000 minName = None
1001 for elem in groupElem.findall('enum'):
1002 # Convert the value to an integer and use that to track min/max.
1003 # Values of form -(number) are accepted but nothing more complex.
1004 # Should catch exceptions here for more complex constructs. Not yet.
1005 (numVal,strVal) = self.enumToValue(elem, True)
1006 name = elem.get('name')
1007 s += " " + name + " = " + strVal + ",\n"
1008 if (expand and elem.get('extends') is None):
1009 if (minName == None):
1010 minName = maxName = name
1011 minValue = maxValue = numVal
1012 elif (numVal < minValue):
1013 minName = name
1014 minValue = numVal
1015 elif (numVal > maxValue):
1016 maxName = name
1017 maxValue = numVal
1018 # Generate min/max value tokens and a range-padding enum. Need some
1019 # additional padding to generate correct names...
1020 if (expand):
1021 s += "\n"
1022 s += " " + expandPrefix + "_BEGIN_RANGE = " + minName + ",\n"
1023 s += " " + expandPrefix + "_END_RANGE = " + maxName + ",\n"
1024 s += " " + expandPrefix + "_NUM = (" + maxName + " - " + minName + " + 1),\n"
1025 s += " " + expandPrefix + "_MAX_ENUM = 0x7FFFFFFF\n"
1026 # Postfix
1027 s += "} " + groupName + ";"
1028 self.writeInclude('enums', groupName, s)
1029 # Enumerant generation
1030 # <enum> tags may specify their values in several ways, but are usually
1031 # just integers.
1032 def genEnum(self, enuminfo, name):
1033 OutputGenerator.genEnum(self, enuminfo, name)
1034 (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
1035 s = '#define ' + name.ljust(33) + ' ' + strVal
1036 self.logMsg('diag', '# NOT writing compile-time constant', name)
1037 # self.writeInclude('consts', name, s)
1038 #
1039 # Command generation
1040 def genCmd(self, cmdinfo, name):
1041 OutputGenerator.genCmd(self, cmdinfo, name)
1042 #
1043 decls = self.makeCDecls(cmdinfo.elem)
1044 self.writeInclude('protos', name, decls[0])
1045
1046# PyOutputGenerator - subclass of OutputGenerator.
1047# Generates Python data structures describing API names.
1048# Similar to DocOutputGenerator, but writes a single
1049# file.
1050#
1051# ---- methods ----
1052# PyOutputGenerator(errFile, warnFile, diagFile) - args as for
1053# OutputGenerator. Defines additional internal state.
1054# ---- methods overriding base class ----
1055# beginFile(genOpts)
1056# endFile()
1057# genType(typeinfo,name)
1058# genStruct(typeinfo,name)
1059# genGroup(groupinfo,name)
1060# genEnum(enuminfo, name)
1061# genCmd(cmdinfo)
1062class PyOutputGenerator(OutputGenerator):
1063 """Generate specified API interfaces in a specific style, such as a C header"""
1064 def __init__(self,
1065 errFile = sys.stderr,
1066 warnFile = sys.stderr,
1067 diagFile = sys.stdout):
1068 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1069 #
1070 def beginFile(self, genOpts):
1071 OutputGenerator.beginFile(self, genOpts)
1072 for dict in [ 'flags', 'enums', 'structs', 'consts', 'enums',
1073 'consts', 'protos', 'funcpointers' ]:
1074 write(dict, '= {}', file=self.outFile)
1075 def endFile(self):
1076 OutputGenerator.endFile(self)
1077 #
1078 # Add a name from the interface
1079 #
1080 # dict - type of name (see beginFile above)
1081 # name - name to add
1082 # value - A serializable Python value for the name
1083 def addName(self, dict, name, value=None):
1084 write(dict + "['" + name + "'] = ", value, file=self.outFile)
1085 #
1086 # Type generation
1087 # For 'struct' or 'union' types, defer to genStruct() to
1088 # add to the dictionary.
1089 # For 'bitmask' types, add the type name to the 'flags' dictionary,
1090 # with the value being the corresponding 'enums' name defining
1091 # the acceptable flag bits.
1092 # For 'enum' types, add the type name to the 'enums' dictionary,
1093 # with the value being '@STOPHERE@' (because this case seems
1094 # never to happen).
1095 # For 'funcpointer' types, add the type name to the 'funcpointers'
1096 # dictionary.
1097 # For 'handle' and 'define' types, add the handle or #define name
1098 # to the 'struct' dictionary, because that's how the spec sources
1099 # tag these types even though they aren't structs.
1100 def genType(self, typeinfo, name):
1101 OutputGenerator.genType(self, typeinfo, name)
1102 typeElem = typeinfo.elem
1103 # If the type is a struct type, traverse the imbedded <member> tags
1104 # generating a structure. Otherwise, emit the tag text.
1105 category = typeElem.get('category')
1106 if (category == 'struct' or category == 'union'):
1107 self.genStruct(typeinfo, name)
1108 else:
1109 # Extract the type name
1110 # (from self.genOpts). Copy other text through unchanged.
1111 # If the resulting text is an empty string, don't emit it.
1112 count = len(noneStr(typeElem.text))
1113 for elem in typeElem:
1114 count += len(noneStr(elem.text)) + len(noneStr(elem.tail))
1115 if (count > 0):
1116 if (category == 'bitmask'):
1117 requiredEnum = typeElem.get('requires')
1118 self.addName('flags', name, enquote(requiredEnum))
1119 elif (category == 'enum'):
1120 # This case never seems to come up!
1121 # @enums C 'enum' name Dictionary of enumerant names
1122 self.addName('enums', name, enquote('@STOPHERE@'))
1123 elif (category == 'funcpointer'):
1124 self.addName('funcpointers', name, None)
1125 elif (category == 'handle' or category == 'define'):
1126 self.addName('structs', name, None)
1127 else:
1128 write('# Unprocessed type:', name, 'category:', category, file=self.outFile)
1129 else:
1130 write('# Unprocessed type:', name, file=self.outFile)
1131 #
1132 # Struct (e.g. C "struct" type) generation.
1133 #
1134 # Add the struct name to the 'structs' dictionary, with the
1135 # value being an ordered list of the struct member names.
1136 def genStruct(self, typeinfo, typeName):
1137 OutputGenerator.genStruct(self, typeinfo, typeName)
1138
1139 members = [member.text for member in typeinfo.elem.findall('.//member/name')]
1140 self.addName('structs', typeName, members)
1141 #
1142 # Group (e.g. C "enum" type) generation.
1143 # These are concatenated together with other types.
1144 #
1145 # Add the enum type name to the 'enums' dictionary, with
1146 # the value being an ordered list of the enumerant names.
1147 # Add each enumerant name to the 'consts' dictionary, with
1148 # the value being the enum type the enumerant is part of.
1149 def genGroup(self, groupinfo, groupName):
1150 OutputGenerator.genGroup(self, groupinfo, groupName)
1151 groupElem = groupinfo.elem
1152
1153 # @enums C 'enum' name Dictionary of enumerant names
1154 # @consts C enumerant/const name Name of corresponding 'enums' key
1155
1156 # Loop over the nested 'enum' tags. Keep track of the minimum and
1157 # maximum numeric values, if they can be determined.
1158 enumerants = [elem.get('name') for elem in groupElem.findall('enum')]
1159 for name in enumerants:
1160 self.addName('consts', name, enquote(groupName))
1161 self.addName('enums', groupName, enumerants)
1162 # Enumerant generation (compile-time constants)
1163 #
1164 # Add the constant name to the 'consts' dictionary, with the
1165 # value being None to indicate that the constant isn't
1166 # an enumeration value.
1167 def genEnum(self, enuminfo, name):
1168 OutputGenerator.genEnum(self, enuminfo, name)
1169
1170 # @consts C enumerant/const name Name of corresponding 'enums' key
1171
1172 self.addName('consts', name, None)
1173 #
1174 # Command generation
1175 #
1176 # Add the command name to the 'protos' dictionary, with the
1177 # value being an ordered list of the parameter names.
1178 def genCmd(self, cmdinfo, name):
1179 OutputGenerator.genCmd(self, cmdinfo, name)
1180
1181 params = [param.text for param in cmdinfo.elem.findall('param/name')]
1182 self.addName('protos', name, params)
1183
1184# ValidityOutputGenerator - subclass of OutputGenerator.
1185# Generates AsciiDoc includes of valid usage information, for reference
1186# pages and the Vulkan specification. Similar to DocOutputGenerator.
1187#
1188# ---- methods ----
1189# ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
1190# OutputGenerator. Defines additional internal state.
1191# ---- methods overriding base class ----
1192# beginFile(genOpts)
1193# endFile()
1194# beginFeature(interface, emit)
1195# endFeature()
1196# genCmd(cmdinfo)
1197class ValidityOutputGenerator(OutputGenerator):
1198 """Generate specified API interfaces in a specific style, such as a C header"""
1199 def __init__(self,
1200 errFile = sys.stderr,
1201 warnFile = sys.stderr,
1202 diagFile = sys.stdout):
1203 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1204
1205 def beginFile(self, genOpts):
1206 OutputGenerator.beginFile(self, genOpts)
1207 def endFile(self):
1208 OutputGenerator.endFile(self)
1209 def beginFeature(self, interface, emit):
1210 # Start processing in superclass
1211 OutputGenerator.beginFeature(self, interface, emit)
1212 def endFeature(self):
1213 # Finish processing in superclass
1214 OutputGenerator.endFeature(self)
1215
1216 def makeParameterName(self, name):
1217 return 'pname:' + name
1218
1219 def makeStructName(self, name):
1220 return 'sname:' + name
1221
1222 def makeBaseTypeName(self, name):
1223 return 'basetype:' + name
1224
1225 def makeEnumerationName(self, name):
1226 return 'elink:' + name
1227
1228 def makeEnumerantName(self, name):
1229 return 'ename:' + name
1230
1231 def makeFLink(self, name):
1232 return 'flink:' + name
1233
1234 #
1235 # Generate an include file
1236 #
1237 # directory - subdirectory to put file in
1238 # basename - base name of the file
1239 # contents - contents of the file (Asciidoc boilerplate aside)
1240 def writeInclude(self, directory, basename, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes):
1241 # Create file
1242 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1243 self.logMsg('diag', '# Generating include file:', filename)
1244 fp = open(filename, 'w')
1245 # Asciidoc anchor
1246
1247 # Valid Usage
1248 if validity is not None:
1249 write('.Valid Usage', file=fp)
1250 write('*' * 80, file=fp)
1251 write(validity, file=fp, end='')
1252 write('*' * 80, file=fp)
1253 write('', file=fp)
1254
1255 # Host Synchronization
1256 if threadsafety is not None:
1257 write('.Host Synchronization', file=fp)
1258 write('*' * 80, file=fp)
1259 write(threadsafety, file=fp, end='')
1260 write('*' * 80, file=fp)
1261 write('', file=fp)
1262
1263 # Command Properties - contained within a block, to avoid table numbering
1264 if commandpropertiesentry is not None:
1265 write('.Command Properties', file=fp)
1266 write('*' * 80, file=fp)
1267 write('[options="header", width="100%"]', file=fp)
1268 write('|=====================', file=fp)
1269 write('|Command Buffer Levels|Render Pass Scope|Supported Queue Types', file=fp)
1270 write(commandpropertiesentry, file=fp)
1271 write('|=====================', file=fp)
1272 write('*' * 80, file=fp)
1273 write('', file=fp)
1274
1275 # Success Codes - contained within a block, to avoid table numbering
1276 if successcodes is not None or errorcodes is not None:
1277 write('.Return Codes', file=fp)
1278 write('*' * 80, file=fp)
1279 if successcodes is not None:
1280 write('<<fundamentals-successcodes,Success>>::', file=fp)
1281 write(successcodes, file=fp)
1282 if errorcodes is not None:
1283 write('<<fundamentals-errorcodes,Failure>>::', file=fp)
1284 write(errorcodes, file=fp)
1285 write('*' * 80, file=fp)
1286 write('', file=fp)
1287
1288 fp.close()
1289
1290 #
1291 # Check if the parameter passed in is a pointer
1292 def paramIsPointer(self, param):
1293 ispointer = False
1294 paramtype = param.find('type')
1295 if paramtype.tail is not None and '*' in paramtype.tail:
1296 ispointer = True
1297
1298 return ispointer
1299
1300 #
1301 # Check if the parameter passed in is a static array
1302 def paramIsStaticArray(self, param):
1303 if param.find('name').tail is not None:
1304 if param.find('name').tail[0] == '[':
1305 return True
1306
1307 #
1308 # Get the length of a parameter that's been identified as a static array
1309 def staticArrayLength(self, param):
1310 paramname = param.find('name')
1311 paramenumsize = param.find('enum')
1312
1313 if paramenumsize is not None:
1314 return paramenumsize.text
1315 else:
1316 return paramname.tail[1:-1]
1317
1318 #
1319 # Check if the parameter passed in is a pointer to an array
1320 def paramIsArray(self, param):
1321 return param.attrib.get('len') is not None
1322
1323 #
1324 # Get the parent of a handle object
1325 def getHandleParent(self, typename):
1326 types = self.registry.findall("types/type")
1327 for elem in types:
1328 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1329 return elem.attrib.get('parent')
1330
1331 #
1332 # Check if a parent object is dispatchable or not
1333 def isHandleTypeDispatchable(self, handlename):
1334 handle = self.registry.find("types/type/[name='" + handlename + "'][@category='handle']")
1335 if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
1336 return True
1337 else:
1338 return False
1339
1340 def isHandleOptional(self, param, params):
1341
1342 # See if the handle is optional
1343 isOptional = False
1344
1345 # Simple, if it's optional, return true
1346 if param.attrib.get('optional') is not None:
1347 return True
1348
1349 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
1350 if param.attrib.get('noautovalidity') is not None:
1351 return True
1352
1353 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
1354 if self.paramIsArray(param):
1355 lengths = param.attrib.get('len').split(',')
1356 for length in lengths:
1357 if (length) != 'null-terminated' and (length) != '1':
1358 for otherparam in params:
1359 if otherparam.find('name').text == length:
1360 if otherparam.attrib.get('optional') is not None:
1361 return True
1362
1363 return False
1364 #
1365 # Get the category of a type
1366 def getTypeCategory(self, typename):
1367 types = self.registry.findall("types/type")
1368 for elem in types:
1369 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1370 return elem.attrib.get('category')
1371
1372 #
1373 # Make a chunk of text for the end of a parameter if it is an array
1374 def makeAsciiDocPreChunk(self, param, params):
1375 paramname = param.find('name')
1376 paramtype = param.find('type')
1377
1378 # General pre-amble. Check optionality and add stuff.
1379 asciidoc = '* '
1380
1381 if self.paramIsStaticArray(param):
1382 asciidoc += 'Any given element of '
1383
1384 elif self.paramIsArray(param):
1385 lengths = param.attrib.get('len').split(',')
1386
1387 # 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
1388 optionallengths = []
1389 for length in lengths:
1390 if (length) != 'null-terminated' and (length) != '1':
1391 for otherparam in params:
1392 if otherparam.find('name').text == length:
1393 if otherparam.attrib.get('optional') is not None:
1394 if self.paramIsPointer(otherparam):
1395 optionallengths.append('the value referenced by ' + self.makeParameterName(length))
1396 else:
1397 optionallengths.append(self.makeParameterName(length))
1398
1399 # Document that these arrays may be ignored if any of the length values are 0
1400 if len(optionallengths) != 0 or param.attrib.get('optional') is not None:
1401 asciidoc += 'If '
1402
1403
1404 if len(optionallengths) != 0:
1405 if len(optionallengths) == 1:
1406
1407 asciidoc += optionallengths[0]
1408 asciidoc += ' is '
1409
1410 else:
1411 asciidoc += ' or '.join(optionallengths)
1412 asciidoc += ' are '
1413
1414 asciidoc += 'not `0`, '
1415
1416 if len(optionallengths) != 0 and param.attrib.get('optional') is not None:
1417 asciidoc += 'and '
1418
1419 if param.attrib.get('optional') is not None:
1420 asciidoc += self.makeParameterName(paramname.text)
1421 asciidoc += ' is not `NULL`, '
1422
1423 elif param.attrib.get('optional') is not None:
1424 # Don't generate this stub for bitflags
1425 if self.getTypeCategory(paramtype.text) != 'bitmask':
1426 if param.attrib.get('optional').split(',')[0] == 'true':
1427 asciidoc += 'If '
1428 asciidoc += self.makeParameterName(paramname.text)
1429 asciidoc += ' is not '
1430 if self.paramIsArray(param) or self.paramIsPointer(param) or self.isHandleTypeDispatchable(paramtype.text):
1431 asciidoc += '`NULL`'
1432 elif self.getTypeCategory(paramtype.text) == 'handle':
1433 asciidoc += 'sname:VK_NULL_HANDLE'
1434 else:
1435 asciidoc += '`0`'
1436
1437 asciidoc += ', '
1438
1439 return asciidoc
1440
1441 #
1442 # Make the generic asciidoc line chunk portion used for all parameters.
1443 # May return an empty string if nothing to validate.
1444 def createValidationLineForParameterIntroChunk(self, param, params, typetext):
1445 asciidoc = ''
1446 paramname = param.find('name')
1447 paramtype = param.find('type')
1448
1449 asciidoc += self.makeAsciiDocPreChunk(param, params)
1450
1451 asciidoc += self.makeParameterName(paramname.text)
1452 asciidoc += ' must: be '
1453
1454 if self.paramIsArray(param):
1455 # Arrays. These are hard to get right, apparently
1456
1457 lengths = param.attrib.get('len').split(',')
1458
1459 if (lengths[0]) == 'null-terminated':
1460 asciidoc += 'a null-terminated '
1461 elif (lengths[0]) == '1':
1462 asciidoc += 'a pointer to '
1463 else:
1464 asciidoc += 'a pointer to an array of '
1465
1466 # Handle equations, which are currently denoted with latex
1467 if 'latexmath:' in lengths[0]:
1468 asciidoc += lengths[0]
1469 else:
1470 asciidoc += self.makeParameterName(lengths[0])
1471 asciidoc += ' '
1472
1473 for length in lengths[1:]:
1474 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.
1475 asciidoc += 'null-terminated '
1476 elif (length) == '1':
1477 asciidoc += 'pointers to '
1478 else:
1479 asciidoc += 'pointers to arrays of '
1480 # Handle equations, which are currently denoted with latex
1481 if 'latex:' in length:
1482 asciidoc += length
1483 else:
1484 asciidoc += self.makeParameterName(length)
1485 asciidoc += ' '
1486
1487 # Void pointers don't actually point at anything - remove the word "to"
1488 if paramtype.text == 'void':
1489 if lengths[-1] == '1':
1490 if len(lengths) > 1:
1491 asciidoc = asciidoc[:-5] # Take care of the extra s added by the post array chunk function. #HACK#
1492 else:
1493 asciidoc = asciidoc[:-4]
1494 else:
1495 # An array of void values is a byte array.
1496 asciidoc += 'byte'
1497
1498 elif paramtype.text == 'char':
1499 # A null terminated array of chars is a string
1500 if lengths[-1] == 'null-terminated':
1501 asciidoc += 'string'
1502 else:
1503 # Else it's just a bunch of chars
1504 asciidoc += 'char value'
1505 elif param.text is not None:
1506 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1507 if 'const' in param.text:
1508 typecategory = self.getTypeCategory(paramtype.text)
1509 if (typecategory != 'struct' and typecategory != 'union' and typecategory != 'basetype' and typecategory is not None) or not self.isStructAlwaysValid(paramtype.text):
1510 asciidoc += 'valid '
1511
1512 asciidoc += typetext
1513
1514 # pluralize
1515 if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'):
1516 asciidoc += 's'
1517
1518 elif self.paramIsPointer(param):
1519 # Handle pointers - which are really special case arrays (i.e. they don't have a length)
1520 pointercount = paramtype.tail.count('*')
1521
1522 # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that.
1523 for i in range(0, pointercount):
1524 asciidoc += 'a pointer to '
1525
1526 if paramtype.text == 'void':
1527 # If there's only one pointer, it's optional, and it doesn't point at anything in particular - we don't need any language.
1528 if pointercount == 1 and param.attrib.get('optional') is not None:
1529 return '' # early return
1530 else:
1531 # Pointer to nothing in particular - delete the " to " portion
1532 asciidoc = asciidoc[:-4]
1533 else:
1534 # Add an article for English semantic win
1535 asciidoc += 'a '
1536
1537 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1538 if param.text is not None and paramtype.text != 'void':
1539 if 'const' in param.text:
1540 asciidoc += 'valid '
1541
1542 asciidoc += typetext
1543
1544 else:
1545 # Non-pointer, non-optional things must be valid
1546 asciidoc += 'a valid '
1547 asciidoc += typetext
1548
1549 if asciidoc != '':
1550 asciidoc += '\n'
1551
1552 # Add additional line for non-optional bitmasks
1553 if self.getTypeCategory(paramtype.text) == 'bitmask':
1554 if param.attrib.get('optional') is None:
1555 asciidoc += '* '
1556 if self.paramIsArray(param):
1557 asciidoc += 'Each element of '
1558 asciidoc += 'pname:'
1559 asciidoc += paramname.text
1560 asciidoc += ' mustnot: be `0`'
1561 asciidoc += '\n'
1562
1563 return asciidoc
1564
1565 def makeAsciiDocLineForParameter(self, param, params, typetext):
1566 if param.attrib.get('noautovalidity') is not None:
1567 return ''
1568 asciidoc = self.createValidationLineForParameterIntroChunk(param, params, typetext)
1569
1570 return asciidoc
1571
1572 # Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance)
1573 def isStructAlwaysValid(self, structname):
1574
1575 struct = self.registry.find("types/type[@name='" + structname + "']")
1576
1577 params = struct.findall('member')
1578 validity = struct.find('validity')
1579
1580 if validity is not None:
1581 return False
1582
1583 for param in params:
1584 paramname = param.find('name')
1585 paramtype = param.find('type')
1586 typecategory = self.getTypeCategory(paramtype.text)
1587
1588 if paramname.text == 'pNext':
1589 return False
1590
1591 if paramname.text == 'sType':
1592 return False
1593
1594 if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param):
1595 if self.makeAsciiDocLineForParameter(param, params, '') != '':
1596 return False
1597 elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask' or param.attrib.get('returnedonly') == 'true':
1598 return False
1599 elif typecategory == 'struct' or typecategory == 'union':
1600 if self.isStructAlwaysValid(paramtype.text) is False:
1601 return False
1602
1603 return True
1604
1605 #
1606 # Make an entire asciidoc line for a given parameter
1607 def createValidationLineForParameter(self, param, params, typecategory):
1608 asciidoc = ''
1609 paramname = param.find('name')
1610 paramtype = param.find('type')
1611
1612 if paramtype.text == 'void' or paramtype.text == 'char':
1613 # Chars and void are special cases - needs care inside the generator functions
1614 # A null-terminated char array is a string, else it's chars.
1615 # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular
1616 asciidoc += self.makeAsciiDocLineForParameter(param, params, '')
1617 elif typecategory == 'bitmask':
1618 bitsname = paramtype.text.replace('Flags', 'FlagBits')
1619 if self.registry.find("enums[@name='" + bitsname + "']") is None:
1620 asciidoc += '* '
1621 asciidoc += self.makeParameterName(paramname.text)
1622 asciidoc += ' must: be `0`'
1623 asciidoc += '\n'
1624 else:
1625 if self.paramIsArray(param):
1626 asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value')
1627 else:
1628 asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values')
1629 elif typecategory == 'handle':
1630 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' handle')
1631 elif typecategory == 'enum':
1632 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeEnumerationName(paramtype.text) + ' value')
1633 elif typecategory == 'struct':
1634 if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1635 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' structure')
1636 elif typecategory == 'union':
1637 if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1638 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' union')
1639 elif self.paramIsArray(param) or self.paramIsPointer(param):
1640 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeBaseTypeName(paramtype.text) + ' value')
1641
1642 return asciidoc
1643
1644 #
1645 # Make an asciidoc validity entry for a handle's parent object
1646 def makeAsciiDocHandleParent(self, param, params):
1647 asciidoc = ''
1648 paramname = param.find('name')
1649 paramtype = param.find('type')
1650
1651 # Deal with handle parents
1652 handleparent = self.getHandleParent(paramtype.text)
1653 if handleparent is not None:
1654 parentreference = None
1655 for otherparam in params:
1656 if otherparam.find('type').text == handleparent:
1657 parentreference = otherparam.find('name').text
1658 if parentreference is not None:
1659 asciidoc += '* '
1660
1661 if self.isHandleOptional(param, params):
1662 if self.paramIsArray(param):
1663 asciidoc += 'Each element of '
1664 asciidoc += self.makeParameterName(paramname.text)
1665 asciidoc += ' that is a valid handle'
1666 else:
1667 asciidoc += 'If '
1668 asciidoc += self.makeParameterName(paramname.text)
1669 asciidoc += ' is a valid handle, it'
1670 else:
1671 if self.paramIsArray(param):
1672 asciidoc += 'Each element of '
1673 asciidoc += self.makeParameterName(paramname.text)
1674 asciidoc += ' must: have been created, allocated or retrieved from '
1675 asciidoc += self.makeParameterName(parentreference)
1676
1677 asciidoc += '\n'
1678 return asciidoc
1679
1680 #
1681 # Generate an asciidoc validity line for the sType value of a struct
1682 def makeStructureType(self, blockname, param):
1683 asciidoc = '* '
1684 paramname = param.find('name')
1685 paramtype = param.find('type')
1686
1687 asciidoc += self.makeParameterName(paramname.text)
1688 asciidoc += ' must: be '
1689
1690 structuretype = ''
1691 for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname):
1692 if elem[0] == 'Vk':
1693 structuretype += 'VK_STRUCTURE_TYPE_'
1694 else:
1695 structuretype += elem[0].upper()
1696 structuretype += '_'
1697
1698 asciidoc += self.makeEnumerantName(structuretype[:-1])
1699 asciidoc += '\n'
1700
1701 return asciidoc
1702
1703 #
1704 # Generate an asciidoc validity line for the pNext value of a struct
1705 def makeStructureExtensionPointer(self, param):
1706 asciidoc = '* '
1707 paramname = param.find('name')
1708 paramtype = param.find('type')
1709
1710 asciidoc += self.makeParameterName(paramname.text)
1711
1712 validextensionstructs = param.attrib.get('validextensionstructs')
1713 if validextensionstructs is None:
1714 asciidoc += ' must: be `NULL`'
1715 else:
1716 extensionstructs = validextensionstructs.split(',')
1717 asciidoc += ' must: point to one of ' + extensionstructs[:-1].join(', ') + ' or ' + extensionstructs[-1] + 'if the extension that introduced them is enabled '
1718
1719 asciidoc += '\n'
1720
1721 return asciidoc
1722
1723 #
1724 # Generate all the valid usage information for a given struct or command
1725 def makeValidUsageStatements(self, cmd, blockname, params, usages):
1726 # Start the asciidoc block for this
1727 asciidoc = ''
1728
1729 handles = []
1730 anyparentedhandlesoptional = False
1731 parentdictionary = {}
1732 arraylengths = set()
1733 for param in params:
1734 paramname = param.find('name')
1735 paramtype = param.find('type')
1736
1737 # Get the type's category
1738 typecategory = self.getTypeCategory(paramtype.text)
1739
1740 # Generate language to independently validate a parameter
1741 if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
1742 asciidoc += self.makeStructureType(blockname, param)
1743 elif paramtype.text == 'void' and paramname.text == 'pNext':
1744 asciidoc += self.makeStructureExtensionPointer(param)
1745 else:
1746 asciidoc += self.createValidationLineForParameter(param, params, typecategory)
1747
1748 # Ensure that any parenting is properly validated, and list that a handle was found
1749 if typecategory == 'handle':
1750 # Don't detect a parent for return values!
1751 if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
1752 parent = self.getHandleParent(paramtype.text)
1753 if parent is not None:
1754 handles.append(param)
1755
1756 # If any param is optional, it affects the output
1757 if self.isHandleOptional(param, params):
1758 anyparentedhandlesoptional = True
1759
1760 # Find the first dispatchable parent
1761 ancestor = parent
1762 while ancestor is not None and not self.isHandleTypeDispatchable(ancestor):
1763 ancestor = self.getHandleParent(ancestor)
1764
1765 # If one was found, add this parameter to the parent dictionary
1766 if ancestor is not None:
1767 if ancestor not in parentdictionary:
1768 parentdictionary[ancestor] = []
1769
1770 if self.paramIsArray(param):
1771 parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text))
1772 else:
1773 parentdictionary[ancestor].append(self.makeParameterName(paramname.text))
1774
1775 # Get the array length for this parameter
1776 arraylength = param.attrib.get('len')
1777 if arraylength is not None:
1778 for onelength in arraylength.split(','):
1779 arraylengths.add(onelength)
1780
1781 # For any vkQueue* functions, there might be queue type data
1782 if 'vkQueue' in blockname:
1783 # The queue type must be valid
1784 queuetypes = cmd.attrib.get('queues')
1785 if queuetypes is not None:
1786 queuebits = []
1787 for queuetype in re.findall(r'([^,]+)', queuetypes):
1788 queuebits.append(queuetype.replace('_',' '))
1789
1790 asciidoc += '* '
1791 asciidoc += 'The pname:queue must: support '
1792 if len(queuebits) == 1:
1793 asciidoc += queuebits[0]
1794 else:
1795 asciidoc += (', ').join(queuebits[:-1])
1796 asciidoc += ' or '
1797 asciidoc += queuebits[-1]
1798 asciidoc += ' operations'
1799 asciidoc += '\n'
1800
1801 if 'vkCmd' in blockname:
1802 # The commandBuffer parameter must be being recorded
1803 asciidoc += '* '
1804 asciidoc += 'pname:commandBuffer must: be in the recording state'
1805 asciidoc += '\n'
1806
1807 # The queue type must be valid
1808 queuetypes = cmd.attrib.get('queues')
1809 queuebits = []
1810 for queuetype in re.findall(r'([^,]+)', queuetypes):
1811 queuebits.append(queuetype.replace('_',' '))
1812
1813 asciidoc += '* '
1814 asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
1815 if len(queuebits) == 1:
1816 asciidoc += queuebits[0]
1817 else:
1818 asciidoc += (', ').join(queuebits[:-1])
1819 asciidoc += ' or '
1820 asciidoc += queuebits[-1]
1821 asciidoc += ' operations'
1822 asciidoc += '\n'
1823
1824 # Must be called inside/outside a renderpass appropriately
1825 renderpass = cmd.attrib.get('renderpass')
1826
1827 if renderpass != 'both':
1828 asciidoc += '* This command must: only be called '
1829 asciidoc += renderpass
1830 asciidoc += ' of a render pass instance'
1831 asciidoc += '\n'
1832
1833 # Must be in the right level command buffer
1834 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
1835
1836 if cmdbufferlevel != 'primary,secondary':
1837 asciidoc += '* pname:commandBuffer must: be a '
1838 asciidoc += cmdbufferlevel
1839 asciidoc += ' sname:VkCommandBuffer'
1840 asciidoc += '\n'
1841
1842 # Any non-optional arraylengths should specify they must be greater than 0
1843 for param in params:
1844 paramname = param.find('name')
1845
1846 for arraylength in arraylengths:
1847 if paramname.text == arraylength and param.attrib.get('optional') is None:
1848 # Get all the array dependencies
1849 arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
1850
1851 # Get all the optional array dependencies, including those not generating validity for some reason
1852 optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
1853 optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']"))
1854
1855 asciidoc += '* '
1856
1857 # Allow lengths to be arbitrary if all their dependents are optional
1858 if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
1859 asciidoc += 'If '
1860 if len(optionalarrays) > 1:
1861 asciidoc += 'any of '
1862
1863 for array in optionalarrays[:-1]:
1864 asciidoc += self.makeParameterName(optionalarrays.find('name').text)
1865 asciidoc += ', '
1866
1867 if len(optionalarrays) > 1:
1868 asciidoc += 'and '
1869 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
1870 asciidoc += ' are '
1871 else:
1872 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
1873 asciidoc += ' is '
1874
1875 asciidoc += 'not `NULL`, '
1876
1877 if self.paramIsPointer(param):
1878 asciidoc += 'the value referenced by '
1879 else:
1880 asciidoc += 'the value of '
1881
1882 elif self.paramIsPointer(param):
1883 asciidoc += 'The value referenced by '
1884 else:
1885 asciidoc += 'The value of '
1886
1887 asciidoc += self.makeParameterName(arraylength)
1888 asciidoc += ' must: be greater than `0`'
1889 asciidoc += '\n'
1890
1891 # Find the parents of all objects referenced in this command
1892 for param in handles:
1893 asciidoc += self.makeAsciiDocHandleParent(param, params)
1894
1895 # Find the common ancestors of objects
1896 noancestorscount = 0
1897 while noancestorscount < len(parentdictionary):
1898 noancestorscount = 0
1899 oldparentdictionary = parentdictionary.copy()
1900 for parent in oldparentdictionary.items():
1901 ancestor = self.getHandleParent(parent[0])
1902
1903 while ancestor is not None and ancestor not in parentdictionary:
1904 ancestor = self.getHandleParent(ancestor)
1905
1906 if ancestor is not None:
1907 parentdictionary[ancestor] += parentdictionary.pop(parent[0])
1908 else:
1909 # No ancestors possible - so count it up
1910 noancestorscount += 1
1911
1912 # Add validation language about common ancestors
1913 for parent in parentdictionary.items():
1914 if len(parent[1]) > 1:
1915 parentlanguage = '* '
1916
1917 parentlanguage += 'Each of '
1918 parentlanguage += ", ".join(parent[1][:-1])
1919 parentlanguage += ' and '
1920 parentlanguage += parent[1][-1]
1921 if anyparentedhandlesoptional is True:
1922 parentlanguage += ' that are valid handles'
1923 parentlanguage += ' must: have been created, allocated or retrieved from the same '
1924 parentlanguage += self.makeStructName(parent[0])
1925 parentlanguage += '\n'
1926
1927 # Capitalize and add to the main language
1928 asciidoc += parentlanguage
1929
1930 # Add in any plain-text validation language that's in the xml
1931 for usage in usages:
1932 asciidoc += '* '
1933 asciidoc += usage.text
1934 asciidoc += '\n'
1935
1936 # In case there's nothing to report, return None
1937 if asciidoc == '':
1938 return None
1939 # Delimit the asciidoc block
1940 return asciidoc
1941
1942 def makeThreadSafetyBlock(self, cmd, paramtext):
1943 """Generate C function pointer typedef for <command> Element"""
1944 paramdecl = ''
1945
1946 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
1947 if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name'):
1948 paramdecl += '* '
1949 paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from'
1950 paramdecl += '\n'
1951
1952 # Find and add any parameters that are thread unsafe
1953 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
1954 if (explicitexternsyncparams is not None):
1955 for param in explicitexternsyncparams:
1956 externsyncattribs = param.attrib.get('externsync')
1957 paramname = param.find('name')
1958 for externsyncattrib in externsyncattribs.split(','):
1959 paramdecl += '* '
1960 paramdecl += 'Host access to '
1961 if externsyncattrib == 'true':
1962 if self.paramIsArray(param):
1963 paramdecl += 'each member of ' + self.makeParameterName(paramname.text)
1964 elif self.paramIsPointer(param):
1965 paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text)
1966 else:
1967 paramdecl += self.makeParameterName(paramname.text)
1968 else:
1969 paramdecl += 'pname:'
1970 paramdecl += externsyncattrib
1971 paramdecl += ' must: be externally synchronized\n'
1972
1973 # Find and add any "implicit" parameters that are thread unsafe
1974 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
1975 if (implicitexternsyncparams is not None):
1976 for elem in implicitexternsyncparams:
1977 paramdecl += '* '
1978 paramdecl += 'Host access to '
1979 paramdecl += elem.text
1980 paramdecl += ' must: be externally synchronized\n'
1981
1982 if (paramdecl == ''):
1983 return None
1984 else:
1985 return paramdecl
1986
1987 def makeCommandPropertiesTableEntry(self, cmd, name):
1988
1989 if 'vkCmd' in name:
1990 # Must be called inside/outside a renderpass appropriately
1991 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
1992 cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
1993
1994 renderpass = cmd.attrib.get('renderpass')
1995 renderpass = renderpass.capitalize()
1996
1997 queues = cmd.attrib.get('queues')
1998 queues = (' + \n').join(queues.upper().split(','))
1999
2000 return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues
2001 elif 'vkQueue' in name:
2002 # Must be called inside/outside a renderpass appropriately
2003
2004 queues = cmd.attrib.get('queues')
2005 if queues is None:
2006 queues = 'Any'
2007 else:
2008 queues = (' + \n').join(queues.upper().split(','))
2009
2010 return '|-|-|' + queues
2011
2012 return None
2013
2014 def makeSuccessCodes(self, cmd, name):
2015
2016 successcodes = cmd.attrib.get('successcodes')
2017 if successcodes is not None:
2018
2019 successcodeentry = ''
2020 successcodes = successcodes.split(',')
2021 return '* ' + '\n* '.join(successcodes)
2022
2023 return None
2024
2025 def makeErrorCodes(self, cmd, name):
2026
2027 errorcodes = cmd.attrib.get('errorcodes')
2028 if errorcodes is not None:
2029
2030 errorcodeentry = ''
2031 errorcodes = errorcodes.split(',')
2032 return '* ' + '\n* '.join(errorcodes)
2033
2034 return None
2035
2036 #
2037 # Command generation
2038 def genCmd(self, cmdinfo, name):
2039 OutputGenerator.genCmd(self, cmdinfo, name)
2040 #
2041 # Get all thh parameters
2042 params = cmdinfo.elem.findall('param')
2043 usages = cmdinfo.elem.findall('validity/usage')
2044
2045 validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages)
2046 threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
2047 commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name)
2048 successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
2049 errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
2050
2051 self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
2052
2053 #
2054 # Struct Generation
2055 def genStruct(self, typeinfo, typename):
2056 OutputGenerator.genStruct(self, typeinfo, typename)
2057
2058 # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
2059 if typeinfo.elem.attrib.get('returnedonly') is None:
2060 params = typeinfo.elem.findall('member')
2061 usages = typeinfo.elem.findall('validity/usage')
2062
2063 validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
2064 threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
2065
2066 self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None)
2067 else:
2068 # Still generate files for return only structs, in case this state changes later
2069 self.writeInclude('validity/structs', typename, None, None, None, None, None)
2070
2071 #
2072 # Type Generation
2073 def genType(self, typeinfo, typename):
2074 OutputGenerator.genType(self, typeinfo, typename)
2075
2076 category = typeinfo.elem.get('category')
2077 if (category == 'struct' or category == 'union'):
2078 self.genStruct(typeinfo, typename)
2079
2080# HostSynchronizationOutputGenerator - subclass of OutputGenerator.
2081# Generates AsciiDoc includes of the externsync parameter table for the
2082# fundamentals chapter of the Vulkan specification. Similar to
2083# DocOutputGenerator.
2084#
2085# ---- methods ----
2086# HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for
2087# OutputGenerator. Defines additional internal state.
2088# ---- methods overriding base class ----
2089# genCmd(cmdinfo)
2090class HostSynchronizationOutputGenerator(OutputGenerator):
2091 # Generate Host Synchronized Parameters in a table at the top of the spec
2092 def __init__(self,
2093 errFile = sys.stderr,
2094 warnFile = sys.stderr,
2095 diagFile = sys.stdout):
2096 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2097
2098 threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''}
2099
2100 def makeParameterName(self, name):
2101 return 'pname:' + name
2102
2103 def makeFLink(self, name):
2104 return 'flink:' + name
2105
2106 #
2107 # Generate an include file
2108 #
2109 # directory - subdirectory to put file in
2110 # basename - base name of the file
2111 # contents - contents of the file (Asciidoc boilerplate aside)
2112 def writeInclude(self):
2113
2114 if self.threadsafety['parameters'] is not None:
2115 # Create file
2116 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt'
2117 self.logMsg('diag', '# Generating include file:', filename)
2118 fp = open(filename, 'w')
2119
2120 # Host Synchronization
2121 write('.Externally Synchronized Parameters', file=fp)
2122 write('*' * 80, file=fp)
2123 write(self.threadsafety['parameters'], file=fp, end='')
2124 write('*' * 80, file=fp)
2125 write('', file=fp)
2126
2127 if self.threadsafety['parameterlists'] is not None:
2128 # Create file
2129 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt'
2130 self.logMsg('diag', '# Generating include file:', filename)
2131 fp = open(filename, 'w')
2132
2133 # Host Synchronization
2134 write('.Externally Synchronized Parameter Lists', file=fp)
2135 write('*' * 80, file=fp)
2136 write(self.threadsafety['parameterlists'], file=fp, end='')
2137 write('*' * 80, file=fp)
2138 write('', file=fp)
2139
2140 if self.threadsafety['implicit'] is not None:
2141 # Create file
2142 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt'
2143 self.logMsg('diag', '# Generating include file:', filename)
2144 fp = open(filename, 'w')
2145
2146 # Host Synchronization
2147 write('.Implicit Externally Synchronized Parameters', file=fp)
2148 write('*' * 80, file=fp)
2149 write(self.threadsafety['implicit'], file=fp, end='')
2150 write('*' * 80, file=fp)
2151 write('', file=fp)
2152
2153 fp.close()
2154
2155 #
2156 # Check if the parameter passed in is a pointer to an array
2157 def paramIsArray(self, param):
2158 return param.attrib.get('len') is not None
2159
2160 # Check if the parameter passed in is a pointer
2161 def paramIsPointer(self, param):
2162 ispointer = False
2163 paramtype = param.find('type')
2164 if paramtype.tail is not None and '*' in paramtype.tail:
2165 ispointer = True
2166
2167 return ispointer
2168
2169 # Turn the "name[].member[]" notation into plain English.
2170 def makeThreadDereferenceHumanReadable(self, dereference):
2171 matches = re.findall(r"[\w]+[^\w]*",dereference)
2172 stringval = ''
2173 for match in reversed(matches):
2174 if '->' in match or '.' in match:
2175 stringval += 'member of '
2176 if '[]' in match:
2177 stringval += 'each element of '
2178
2179 stringval += 'the '
2180 stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0])
2181 stringval += ' '
2182
2183 stringval += 'parameter'
2184
2185 return stringval[0].upper() + stringval[1:]
2186
2187 def makeThreadSafetyBlocks(self, cmd, paramtext):
2188 protoname = cmd.find('proto/name').text
2189
2190 # Find and add any parameters that are thread unsafe
2191 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2192 if (explicitexternsyncparams is not None):
2193 for param in explicitexternsyncparams:
2194 externsyncattribs = param.attrib.get('externsync')
2195 paramname = param.find('name')
2196 for externsyncattrib in externsyncattribs.split(','):
2197
2198 tempstring = '* '
2199 if externsyncattrib == 'true':
2200 if self.paramIsArray(param):
2201 tempstring += 'Each element of the '
2202 elif self.paramIsPointer(param):
2203 tempstring += 'The object referenced by the '
2204 else:
2205 tempstring += 'The '
2206
2207 tempstring += self.makeParameterName(paramname.text)
2208 tempstring += ' parameter'
2209
2210 else:
2211 tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib)
2212
2213 tempstring += ' in '
2214 tempstring += self.makeFLink(protoname)
2215 tempstring += '\n'
2216
2217
2218 if ' element of ' in tempstring:
2219 self.threadsafety['parameterlists'] += tempstring
2220 else:
2221 self.threadsafety['parameters'] += tempstring
2222
2223
2224 # Find and add any "implicit" parameters that are thread unsafe
2225 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2226 if (implicitexternsyncparams is not None):
2227 for elem in implicitexternsyncparams:
2228 self.threadsafety['implicit'] += '* '
2229 self.threadsafety['implicit'] += elem.text[0].upper()
2230 self.threadsafety['implicit'] += elem.text[1:]
2231 self.threadsafety['implicit'] += ' in '
2232 self.threadsafety['implicit'] += self.makeFLink(protoname)
2233 self.threadsafety['implicit'] += '\n'
2234
2235
2236 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2237 if protoname is not None and 'vkCmd' in protoname:
2238 self.threadsafety['implicit'] += '* '
2239 self.threadsafety['implicit'] += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in '
2240 self.threadsafety['implicit'] += self.makeFLink(protoname)
2241
2242 self.threadsafety['implicit'] += '\n'
2243
2244 #
2245 # Command generation
2246 def genCmd(self, cmdinfo, name):
2247 OutputGenerator.genCmd(self, cmdinfo, name)
2248 #
2249 # Get all thh parameters
2250 params = cmdinfo.elem.findall('param')
2251 usages = cmdinfo.elem.findall('validity/usage')
2252
2253 self.makeThreadSafetyBlocks(cmdinfo.elem, 'param')
2254
2255 self.writeInclude()
Mike Stroyan8849f9a2015-11-02 15:30:20 -07002256
2257# ThreadOutputGenerator - subclass of OutputGenerator.
2258# Generates Thread checking framework
2259#
2260# ---- methods ----
2261# ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
2262# OutputGenerator. Defines additional internal state.
2263# ---- methods overriding base class ----
2264# beginFile(genOpts)
2265# endFile()
2266# beginFeature(interface, emit)
2267# endFeature()
2268# genType(typeinfo,name)
2269# genStruct(typeinfo,name)
2270# genGroup(groupinfo,name)
2271# genEnum(enuminfo, name)
2272# genCmd(cmdinfo)
2273class ThreadOutputGenerator(OutputGenerator):
2274 """Generate specified API interfaces in a specific style, such as a C header"""
2275 # This is an ordered list of sections in the header file.
2276 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
2277 'group', 'bitmask', 'funcpointer', 'struct']
2278 ALL_SECTIONS = TYPE_SECTIONS + ['command']
2279 def __init__(self,
2280 errFile = sys.stderr,
2281 warnFile = sys.stderr,
2282 diagFile = sys.stdout):
2283 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2284 # Internal state - accumulators for different inner block text
2285 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2286 self.intercepts = []
2287
2288 # Check if the parameter passed in is a pointer to an array
2289 def paramIsArray(self, param):
2290 return param.attrib.get('len') is not None
2291
2292 # Check if the parameter passed in is a pointer
2293 def paramIsPointer(self, param):
2294 ispointer = False
2295 for elem in param:
2296 #write('paramIsPointer '+elem.text, file=sys.stderr)
2297 #write('elem.tag '+elem.tag, file=sys.stderr)
2298 #if (elem.tail is None):
2299 # write('elem.tail is None', file=sys.stderr)
2300 #else:
2301 # write('elem.tail '+elem.tail, file=sys.stderr)
2302 if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
2303 ispointer = True
2304 # write('is pointer', file=sys.stderr)
2305 return ispointer
2306 def makeThreadUseBlock(self, cmd, functionprefix):
2307 """Generate C function pointer typedef for <command> Element"""
2308 paramdecl = ''
2309 thread_check_dispatchable_objects = [
2310 "VkCommandBuffer",
2311 "VkDevice",
2312 "VkInstance",
2313 "VkQueue",
2314 ]
2315 thread_check_nondispatchable_objects = [
2316 "VkBuffer",
2317 "VkBufferView",
2318 "VkCommandPool",
2319 "VkDescriptorPool",
2320 "VkDescriptorSetLayout",
2321 "VkDeviceMemory",
2322 "VkEvent",
2323 "VkFence",
2324 "VkFramebuffer",
2325 "VkImage",
2326 "VkImageView",
2327 "VkPipeline",
2328 "VkPipelineCache",
2329 "VkPipelineLayout",
2330 "VkQueryPool",
2331 "VkRenderPass",
2332 "VkSampler",
2333 "VkSemaphore",
2334 "VkShaderModule",
2335 ]
2336
2337 # Find and add any parameters that are thread unsafe
2338 params = cmd.findall('param')
2339 for param in params:
2340 paramname = param.find('name')
2341 if False: # self.paramIsPointer(param):
2342 paramdecl += ' // not watching use of pointer ' + paramname.text + '\n'
2343 else:
2344 externsync = param.attrib.get('externsync')
2345 if externsync == 'true':
2346 if self.paramIsArray(param):
2347 paramdecl += ' for (int index=0;index<' + param.attrib.get('len') + ';index++) {\n'
2348 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + '[index]);\n'
2349 paramdecl += ' }\n'
2350 else:
2351 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + ');\n'
2352 elif (param.attrib.get('externsync')):
2353 if self.paramIsArray(param):
2354 # Externsync can list pointers to arrays of members to synchronize
2355 paramdecl += ' for (int index=0;index<' + param.attrib.get('len') + ';index++) {\n'
2356 for member in externsync.split(","):
2357 # Replace first empty [] in member name with index
2358 element = member.replace('[]','[index]',1)
2359 if '[]' in element:
2360 # Replace any second empty [] in element name with
2361 # inner array index based on mapping array names like
2362 # "pSomeThings[]" to "someThingCount" array size.
2363 # This could be more robust by mapping a param member
2364 # name to a struct type and "len" attribute.
2365 limit = element[0:element.find('s[]')] + 'Count'
2366 dotp = limit.rfind('.p')
2367 limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:]
2368 paramdecl += ' for(int index2=0;index2<'+limit+';index2++)'
2369 element = element.replace('[]','[index2]')
2370 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n'
2371 paramdecl += ' }\n'
2372 else:
2373 # externsync can list members to synchronize
2374 for member in externsync.split(","):
2375 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
2376 else:
2377 paramtype = param.find('type')
2378 if paramtype is not None:
2379 paramtype = paramtype.text
2380 else:
2381 paramtype = 'None'
2382 if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
2383 if self.paramIsArray(param) and ('pPipelines' != paramname.text):
2384 paramdecl += ' for (int index=0;index<' + param.attrib.get('len') + ';index++) {\n'
2385 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
2386 paramdecl += ' }\n'
2387 elif not self.paramIsPointer(param):
2388 # Pointer params are often being created.
2389 # They are not being read from.
2390 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
2391 explicitexternsyncparams = cmd.findall("param[@externsync]")
2392 if (explicitexternsyncparams is not None):
2393 for param in explicitexternsyncparams:
2394 externsyncattrib = param.attrib.get('externsync')
2395 paramname = param.find('name')
2396 paramdecl += '// Host access to '
2397 if externsyncattrib == 'true':
2398 if self.paramIsArray(param):
2399 paramdecl += 'each member of ' + paramname.text
2400 elif self.paramIsPointer(param):
2401 paramdecl += 'the object referenced by ' + paramname.text
2402 else:
2403 paramdecl += paramname.text
2404 else:
2405 paramdecl += externsyncattrib
2406 paramdecl += ' must be externally synchronized\n'
2407
2408 # Find and add any "implicit" parameters that are thread unsafe
2409 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2410 if (implicitexternsyncparams is not None):
2411 for elem in implicitexternsyncparams:
2412 paramdecl += ' // '
2413 paramdecl += elem.text
2414 paramdecl += ' must be externally synchronized between host accesses\n'
2415
2416 if (paramdecl == ''):
2417 return None
2418 else:
2419 return paramdecl
2420 def beginFile(self, genOpts):
2421 OutputGenerator.beginFile(self, genOpts)
2422 # C-specific
2423 #
2424 # Multiple inclusion protection & C++ wrappers.
2425 if (genOpts.protectFile and self.genOpts.filename):
2426 headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
2427 write('#ifndef', headerSym, file=self.outFile)
2428 write('#define', headerSym, '1', file=self.outFile)
2429 self.newline()
2430 write('#ifdef __cplusplus', file=self.outFile)
2431 write('extern "C" {', file=self.outFile)
2432 write('#endif', file=self.outFile)
2433 self.newline()
2434 #
2435 # User-supplied prefix text, if any (list of strings)
2436 if (genOpts.prefixText):
2437 for s in genOpts.prefixText:
2438 write(s, file=self.outFile)
2439 def endFile(self):
2440 # C-specific
2441 # Finish C++ wrapper and multiple inclusion protection
2442 self.newline()
2443 # record intercepted procedures
2444 write('// intercepts', file=self.outFile)
2445 write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
2446 write('\n'.join(self.intercepts), file=self.outFile)
2447 write('};\n', file=self.outFile)
2448 self.newline()
2449 write('#ifdef __cplusplus', file=self.outFile)
2450 write('}', file=self.outFile)
2451 write('#endif', file=self.outFile)
2452 if (self.genOpts.protectFile and self.genOpts.filename):
2453 self.newline()
2454 write('#endif', file=self.outFile)
2455 # Finish processing in superclass
2456 OutputGenerator.endFile(self)
2457 def beginFeature(self, interface, emit):
2458 #write('// starting beginFeature', file=self.outFile)
2459 # Start processing in superclass
2460 OutputGenerator.beginFeature(self, interface, emit)
2461 # C-specific
2462 # Accumulate includes, defines, types, enums, function pointer typedefs,
2463 # end function prototypes separately for this feature. They're only
2464 # printed in endFeature().
2465 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2466 #write('// ending beginFeature', file=self.outFile)
2467 def endFeature(self):
2468 # C-specific
2469 # Actually write the interface to the output file.
2470 #write('// starting endFeature', file=self.outFile)
2471 if (self.emit):
2472 self.newline()
2473 if (self.genOpts.protectFeature):
2474 write('#ifndef', self.featureName, file=self.outFile)
2475 # If type declarations are needed by other features based on
2476 # this one, it may be necessary to suppress the ExtraProtect,
2477 # or move it below the 'for section...' loop.
2478 #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
2479 if (self.featureExtraProtect != None):
2480 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2481 #write('#define', self.featureName, '1', file=self.outFile)
2482 for section in self.TYPE_SECTIONS:
2483 #write('// endFeature writing section'+section, file=self.outFile)
2484 contents = self.sections[section]
2485 if contents:
2486 write('\n'.join(contents), file=self.outFile)
2487 self.newline()
2488 #write('// endFeature looking at self.sections[command]', file=self.outFile)
2489 if (self.sections['command']):
2490 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2491 self.newline()
2492 if (self.featureExtraProtect != None):
2493 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2494 if (self.genOpts.protectFeature):
2495 write('#endif /*', self.featureName, '*/', file=self.outFile)
2496 # Finish processing in superclass
2497 OutputGenerator.endFeature(self)
2498 #write('// ending endFeature', file=self.outFile)
2499 #
2500 # Append a definition to the specified section
2501 def appendSection(self, section, text):
2502 # self.sections[section].append('SECTION: ' + section + '\n')
2503 self.sections[section].append(text)
2504 #
2505 # Type generation
2506 def genType(self, typeinfo, name):
2507 pass
2508 #
2509 # Struct (e.g. C "struct" type) generation.
2510 # This is a special case of the <type> tag where the contents are
2511 # interpreted as a set of <member> tags instead of freeform C
2512 # C type declarations. The <member> tags are just like <param>
2513 # tags - they are a declaration of a struct or union member.
2514 # Only simple member declarations are supported (no nested
2515 # structs etc.)
2516 def genStruct(self, typeinfo, typeName):
2517 OutputGenerator.genStruct(self, typeinfo, typeName)
2518 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
2519 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
2520 for member in typeinfo.elem.findall('.//member'):
2521 body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
2522 body += ';\n'
2523 body += '} ' + typeName + ';\n'
2524 self.appendSection('struct', body)
2525 #
2526 # Group (e.g. C "enum" type) generation.
2527 # These are concatenated together with other types.
2528 def genGroup(self, groupinfo, groupName):
2529 pass
2530 # Enumerant generation
2531 # <enum> tags may specify their values in several ways, but are usually
2532 # just integers.
2533 def genEnum(self, enuminfo, name):
2534 pass
2535 #
2536 # Command generation
2537 def genCmd(self, cmdinfo, name):
2538 special_functions = [
2539 'vkGetDeviceProcAddr',
2540 'vkGetInstanceProcAddr',
2541 'vkCreateDevice',
2542 'vkDestroyDevice',
2543 'vkCreateInstance',
2544 'vkDestroyInstance',
2545 'vkEnumerateInstanceLayerProperties',
2546 'vkEnumerateInstanceExtensionProperties',
2547 'vkAllocateCommandBuffers',
2548 'vkFreeCommandBuffers',
2549 'vkCreateDebugReportCallbackEXT',
2550 'vkDestroyDebugReportCallbackEXT',
2551 ]
2552 if name in special_functions:
2553 self.intercepts += [ ' "%s", (PFN_vkVoidFunction) %s,' % (name,name) ]
2554 return
2555 if "KHR" in name:
2556 self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
2557 return
2558 # Determine first if this function needs to be intercepted
2559 startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
2560 if startthreadsafety is None:
2561 return
2562 finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
2563 # record that the function will be intercepted
2564 if (self.featureExtraProtect != None):
2565 self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
2566 self.intercepts += [ ' "%s", (PFN_vkVoidFunction) %s,' % (name,name) ]
2567 if (self.featureExtraProtect != None):
2568 self.intercepts += [ '#endif' ]
2569
2570 OutputGenerator.genCmd(self, cmdinfo, name)
2571 #
2572 decls = self.makeCDecls(cmdinfo.elem)
2573 self.appendSection('command', '')
2574 self.appendSection('command', decls[0][:-1])
2575 self.appendSection('command', '{')
2576 # setup common to call wrappers
2577 # first parameter is always dispatchable
2578 dispatchable_type = cmdinfo.elem.find('param/type').text
2579 dispatchable_name = cmdinfo.elem.find('param/name').text
2580 self.appendSection('command', ' dispatch_key key = get_dispatch_key('+dispatchable_name+');')
2581 self.appendSection('command', ' layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
2582 if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
2583 self.appendSection('command', ' VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
2584 else:
2585 self.appendSection('command', ' VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
2586 # Declare result variable, if any.
2587 resulttype = cmdinfo.elem.find('proto/type')
2588 if (resulttype != None and resulttype.text == 'void'):
2589 resulttype = None
2590 if (resulttype != None):
2591 self.appendSection('command', ' ' + resulttype.text + ' result;')
2592 assignresult = 'result = '
2593 else:
2594 assignresult = ''
2595
2596 self.appendSection('command', str(startthreadsafety))
2597 params = cmdinfo.elem.findall('param/name')
2598 paramstext = ','.join([str(param.text) for param in params])
2599 API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
2600 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');')
2601 self.appendSection('command', str(finishthreadsafety))
2602 # Return result variable, if any.
2603 if (resulttype != None):
2604 self.appendSection('command', ' return result;')
2605 self.appendSection('command', '}')