comparison fuhtark_test/Vulkan-Headers-1.4.334/registry/generator.py @ 1501:f40d9d814c08 default tip main

did: correct vulkan-api generator
author sam <sam@basx.dev>
date Wed, 26 Nov 2025 23:34:29 +0700
parents
children
comparison
equal deleted inserted replaced
1500:91c8c3b7cbf0 1501:f40d9d814c08
1 #!/usr/bin/env python3 -i
2 #
3 # Copyright 2013-2025 The Khronos Group Inc.
4 #
5 # SPDX-License-Identifier: Apache-2.0
6 """Base class for source/header/doc generators, as well as some utility functions."""
7
8 from __future__ import unicode_literals
9
10 import io
11 import os
12 import pdb
13 import re
14 import shutil
15 import sys
16 import tempfile
17 try:
18 from pathlib import Path
19 except ImportError:
20 from pathlib2 import Path # type: ignore
21
22 from spec_tools.util import getElemName, getElemType
23
24
25 def write(*args, **kwargs):
26 file = kwargs.pop('file', sys.stdout)
27 end = kwargs.pop('end', '\n')
28 file.write(' '.join(str(arg) for arg in args))
29 file.write(end)
30
31
32 def noneStr(s):
33 """Return string argument, or "" if argument is None.
34
35 Used in converting etree Elements into text.
36 s - string to convert"""
37 if s:
38 return s
39 return ""
40
41
42 def enquote(s):
43 """Return string argument with surrounding quotes,
44 for serialization into Python code."""
45 if s:
46 if isinstance(s, str):
47 return f"'{s}'"
48 else:
49 return s
50 return None
51
52
53 def regSortCategoryKey(feature):
54 """Sort key for regSortFeatures.
55 Sorts by category of the feature name string:
56
57 - Core API features (those defined with a `<feature>` tag)
58 - (sort VKSC after VK - this is Vulkan-specific)
59 - ARB/KHR/OES (Khronos extensions)
60 - other (EXT/vendor extensions)"""
61
62 if feature.elem.tag == 'feature':
63 if feature.name.startswith('VKSC'):
64 return 0.5
65 else:
66 return 0
67
68 if feature.category.upper() in ('ARB', 'KHR', 'OES'):
69 return 1
70
71 return 2
72
73
74 def regSortOrderKey(feature):
75 """Sort key for regSortFeatures - key is the sortorder attribute."""
76
77 return feature.sortorder
78
79
80 def regSortNameKey(feature):
81 """Sort key for regSortFeatures - key is the extension name."""
82
83 return feature.name
84
85
86 def regSortFeatureVersionKey(feature):
87 """Sort key for regSortFeatures - key is the feature version.
88 `<extension>` elements all have version number 0."""
89
90 return float(feature.versionNumber)
91
92
93 def regSortExtensionNumberKey(feature):
94 """Sort key for regSortFeatures - key is the extension number.
95 `<feature>` elements all have extension number 0."""
96
97 return int(feature.number)
98
99
100 def regSortFeatures(featureList):
101 """Default sort procedure for features.
102
103 - Sorts by explicit sort order (default 0) relative to other features
104 - then by feature category ('feature' or 'extension'),
105 - then by version number (for features)
106 - then by extension number (for extensions)"""
107 featureList.sort(key=regSortExtensionNumberKey)
108 featureList.sort(key=regSortFeatureVersionKey)
109 featureList.sort(key=regSortCategoryKey)
110 featureList.sort(key=regSortOrderKey)
111
112
113 class MissingGeneratorOptionsError(RuntimeError):
114 """Error raised when a Generator tries to do something that requires GeneratorOptions but it is None."""
115
116 def __init__(self, msg=None):
117 full_msg = 'Missing generator options object self.genOpts'
118 if msg:
119 full_msg += f": {msg}"
120 super().__init__(full_msg)
121
122
123 class MissingRegistryError(RuntimeError):
124 """Error raised when a Generator tries to do something that requires a Registry object but it is None."""
125
126 def __init__(self, msg=None):
127 full_msg = 'Missing Registry object self.registry'
128 if msg:
129 full_msg += f": {msg}"
130 super().__init__(full_msg)
131
132
133 class MissingGeneratorOptionsConventionsError(RuntimeError):
134 """Error raised when a Generator tries to do something that requires a Conventions object but it is None."""
135
136 def __init__(self, msg=None):
137 full_msg = 'Missing Conventions object self.genOpts.conventions'
138 if msg:
139 full_msg += f": {msg}"
140 super().__init__(full_msg)
141
142
143 class GeneratorOptions:
144 """Base class for options used during header/documentation production.
145
146 These options are target language independent, and used by
147 Registry.apiGen() and by base OutputGenerator objects."""
148
149 def __init__(self,
150 conventions=None,
151 filename=None,
152 directory='.',
153 genpath=None,
154 apiname=None,
155 mergeApiNames=None,
156 mergeInternalApis=True,
157 profile=None,
158 versions='.*',
159 emitversions='.*',
160 defaultExtensions=None,
161 addExtensions=None,
162 removeExtensions=None,
163 emitExtensions=None,
164 emitSpirv=None,
165 emitFormats=None,
166 reparentEnums=True,
167 sortProcedure=regSortFeatures,
168 requireCommandAliases=False,
169 requireDepends=True,
170 ):
171 """Constructor.
172
173 Arguments:
174
175 - conventions - may be mandatory for some generators:
176 an object that implements ConventionsBase
177 - filename - basename of file to generate, or None to write to stdout.
178 - directory - directory in which to generate filename
179 - genpath - path to previously generated files, such as apimap.py
180 - apiname - string matching `<api>` 'apiname' attribute, e.g. 'gl'.
181 - mergeApiNames - If not None, a comma separated list of API names
182 to merge into the API specified by 'apiname'
183 - mergeInternalApis - whether to merge internal APIs into public APIs
184 - profile - string specifying API profile , e.g. 'core', or None.
185 - versions - regex matching API versions to process interfaces for.
186 Normally `'.*'` or `'[0-9][.][0-9]'` to match all defined versions.
187 - emitversions - regex matching API versions to actually emit
188 interfaces for (though all requested versions are considered
189 when deciding which interfaces to generate). For GL 4.3 glext.h,
190 this might be `'1[.][2-5]|[2-4][.][0-9]'`.
191 - defaultExtensions - If not None, a string which must in its
192 entirety match the pattern in the "supported" attribute of
193 the `<extension>`. Defaults to None. Usually the same as apiname.
194 - addExtensions - regex matching names of additional extensions
195 to include. Defaults to None.
196 - removeExtensions - regex matching names of extensions to
197 remove (after defaultExtensions and addExtensions). Defaults
198 to None.
199 - emitExtensions - regex matching names of extensions to actually emit
200 interfaces for (though all requested versions are considered when
201 deciding which interfaces to generate). Defaults to None.
202 - emitSpirv - regex matching names of extensions and capabilities
203 to actually emit interfaces for.
204 - emitFormats - regex matching names of formats to actually emit
205 interfaces for.
206 - reparentEnums - move <enum> elements which extend an enumerated
207 type from <feature> or <extension> elements to the target <enums>
208 element. This is required for almost all purposes, but the
209 InterfaceGenerator relies on the list of interfaces in the <feature>
210 or <extension> being complete. Defaults to True.
211 - sortProcedure - takes a list of FeatureInfo objects and sorts
212 them in place to a preferred order in the generated output.
213 - requireCommandAliases - if True, treat command aliases
214 as required dependencies.
215 - requireDepends - whether to follow API dependencies when emitting
216 APIs.
217
218 Default is
219 - core API versions
220 - Khronos (ARB/KHR/OES) extensions
221 - All other extensions
222 - By core API version number or extension number in each group.
223
224 The regex patterns can be None or empty, in which case they match
225 nothing."""
226 self.conventions = conventions
227 """may be mandatory for some generators:
228 an object that implements ConventionsBase"""
229
230 self.filename = filename
231 "basename of file to generate, or None to write to stdout."
232
233 self.genpath = genpath
234 """path to previously generated files, such as apimap.py"""
235
236 self.directory = directory
237 "directory in which to generate filename"
238
239 self.apiname = apiname
240 "string matching `<api>` 'apiname' attribute, e.g. 'gl'."
241
242 self.mergeApiNames = mergeApiNames
243 "comma separated list of API names to merge into the API specified by 'apiname'"
244
245 self.mergeInternalApis = mergeInternalApis
246 "whether to merge internal APIs into public APIs"
247
248 self.profile = profile
249 "string specifying API profile , e.g. 'core', or None."
250
251 self.versions = self.emptyRegex(versions)
252 """regex matching API versions to process interfaces for.
253 Normally `'.*'` or `'[0-9][.][0-9]'` to match all defined versions."""
254
255 self.emitversions = self.emptyRegex(emitversions)
256 """regex matching API versions to actually emit
257 interfaces for (though all requested versions are considered
258 when deciding which interfaces to generate). For GL 4.3 glext.h,
259 this might be `'1[.][2-5]|[2-4][.][0-9]'`."""
260
261 self.defaultExtensions = defaultExtensions
262 """If not None, a string which must in its
263 entirety match the pattern in the "supported" attribute of
264 the `<extension>`. Defaults to None. Usually the same as apiname."""
265
266 self.addExtensions = self.emptyRegex(addExtensions)
267 """regex matching names of additional extensions
268 to include. Defaults to None."""
269
270 self.removeExtensions = self.emptyRegex(removeExtensions)
271 """regex matching names of extensions to
272 remove (after defaultExtensions and addExtensions). Defaults
273 to None."""
274
275 self.emitExtensions = self.emptyRegex(emitExtensions)
276 """regex matching names of extensions to actually emit
277 interfaces for (though all requested versions are considered when
278 deciding which interfaces to generate)."""
279
280 self.emitSpirv = self.emptyRegex(emitSpirv)
281 """regex matching names of extensions and capabilities
282 to actually emit interfaces for."""
283
284 self.emitFormats = self.emptyRegex(emitFormats)
285 """regex matching names of formats
286 to actually emit interfaces for."""
287
288 self.reparentEnums = reparentEnums
289 """boolean specifying whether to remove <enum> elements from
290 <feature> or <extension> when extending an <enums> type."""
291
292 self.sortProcedure = sortProcedure
293 """takes a list of FeatureInfo objects and sorts
294 them in place to a preferred order in the generated output.
295 Default is core API versions, ARB/KHR/OES extensions, all
296 other extensions, alphabetically within each group."""
297
298 self.codeGenerator = False
299 """True if this generator makes compilable code"""
300
301 self.registry = None
302 """Populated later with the registry object."""
303
304 self.requireCommandAliases = requireCommandAliases
305 """True if alias= attributes of <command> tags are transitively
306 required."""
307
308 self.requireDepends = requireDepends
309 """True if dependencies of API tags are transitively required."""
310
311 def emptyRegex(self, pat):
312 """Substitute a regular expression which matches no version
313 or extension names for None or the empty string."""
314 if not pat:
315 return '_nomatch_^'
316
317 return pat
318
319
320 class OutputGenerator:
321 """Generate specified API interfaces in a specific style, such as a C header.
322
323 Base class for generating API interfaces.
324 Manages basic logic, logging, and output file control.
325 Derived classes actually generate formatted output.
326 """
327
328 # categoryToPath - map XML 'category' to include file directory name
329 categoryToPath = {
330 'bitmask': 'flags',
331 'enum': 'enums',
332 'funcpointer': 'funcpointers',
333 'handle': 'handles',
334 'define': 'defines',
335 'basetype': 'basetypes',
336 }
337
338 def breakName(self, name, msg):
339 """Break into debugger if this is a special name"""
340
341 # List of string names to break on
342 bad = (
343 )
344
345 if name in bad and True:
346 print(f'breakName {name}: {msg}')
347 pdb.set_trace()
348
349 def __init__(self, errFile=sys.stderr, warnFile=sys.stderr, diagFile=sys.stdout):
350 """Constructor
351
352 - errFile, warnFile, diagFile - file handles to write errors,
353 warnings, diagnostics to. May be None to not write."""
354 self.outFile = None
355 self.errFile = errFile
356 self.warnFile = warnFile
357 self.diagFile = diagFile
358 # Internal state
359 self.featureName = None
360 """The current feature name being generated."""
361
362 self.genOpts = None
363 """The GeneratorOptions subclass instance."""
364
365 self.registry = None
366 """The specification registry object."""
367
368 self.featureDictionary = {}
369 """The dictionary of dictionaries of API features."""
370
371 # Used for extension enum value generation
372 self.extBase = 1000000000
373 self.extBlockSize = 1000
374 self.madeDirs = {}
375
376 # API dictionary, which may be loaded by the beginFile method of
377 # derived generators.
378 self.apidict = None
379
380 # File suffix for generated files, set in beginFile below.
381 self.file_suffix = ''
382
383 def logMsg(self, level, *args):
384 """Write a message of different categories to different
385 destinations.
386
387 - `level`
388 - 'diag' (diagnostic, voluminous)
389 - 'warn' (warning)
390 - 'error' (fatal error - raises exception after logging)
391
392 - `*args` - print()-style arguments to direct to corresponding log"""
393 if level == 'error':
394 strfile = io.StringIO()
395 write('ERROR:', *args, file=strfile)
396 if self.errFile is not None:
397 write(strfile.getvalue(), file=self.errFile)
398 raise UserWarning(strfile.getvalue())
399 elif level == 'warn':
400 if self.warnFile is not None:
401 write('WARNING:', *args, file=self.warnFile)
402 elif level == 'diag':
403 if self.diagFile is not None:
404 write('DIAG:', *args, file=self.diagFile)
405 else:
406 raise UserWarning(
407 f"*** FATAL ERROR in Generator.logMsg: unknown level:{level}")
408
409 def enumToValue(self, elem, needsNum, bitwidth = 32,
410 forceSuffix = False, parent_for_alias_dereference=None):
411 """Parse and convert an `<enum>` tag into a value.
412
413 - elem - <enum> Element
414 - needsNum - generate a numeric representation of the element value
415 - bitwidth - size of the numeric representation in bits (32 or 64)
416 - forceSuffix - if True, always use a 'U' / 'ULL' suffix on integers
417 - parent_for_alias_dereference - if not None, an Element containing
418 the parent of elem, used to look for elements this is an alias of
419
420 Returns a list:
421
422 - first element - integer representation of the value, or None
423 if needsNum is False. The value must be a legal number
424 if needsNum is True.
425 - second element - string representation of the value
426
427 There are several possible representations of values.
428
429 - A 'value' attribute simply contains the value.
430 - A 'bitpos' attribute defines a value by specifying the bit
431 position which is set in that value.
432 - An 'offset','extbase','extends' triplet specifies a value
433 as an offset to a base value defined by the specified
434 'extbase' extension name, which is then cast to the
435 typename specified by 'extends'. This requires probing
436 the registry database, and imbeds knowledge of the
437 API extension enum scheme in this function.
438 - An 'alias' attribute contains the name of another enum
439 which this is an alias of. The other enum must be
440 declared first when emitting this enum."""
441 if self.genOpts is None:
442 raise MissingGeneratorOptionsError()
443 if self.genOpts.conventions is None:
444 raise MissingGeneratorOptionsConventionsError()
445
446 name = elem.get('name')
447 numVal = None
448 if 'value' in elem.keys():
449 value = elem.get('value')
450 # print('About to translate value =', value, 'type =', type(value))
451 if needsNum:
452 numVal = int(value, 0)
453 # If there is a non-integer, numeric 'type' attribute (e.g. 'u' or
454 # 'ull'), append it to the string value.
455 # t = enuminfo.elem.get('type')
456 # if t is not None and t != '' and t != 'i' and t != 's':
457 # value += enuminfo.type
458 if forceSuffix:
459 if bitwidth == 64:
460 value = f"{value}ULL"
461 else:
462 value = f"{value}U"
463 self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
464 return [numVal, value]
465 if 'bitpos' in elem.keys():
466 value = elem.get('bitpos')
467 bitpos = int(value, 0)
468 numVal = 1 << bitpos
469 value = f'0x{numVal:08x}'
470 if bitwidth == 64 or bitpos >= 32:
471 value = f"{value}ULL"
472 elif forceSuffix:
473 value = f"{value}U"
474 self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
475 return [numVal, value]
476 if 'offset' in elem.keys():
477 # Obtain values in the mapping from the attributes
478 enumNegative = False
479 offset = int(elem.get('offset'), 0)
480 extnumber = int(elem.get('extnumber'), 0)
481 extends = elem.get('extends')
482 if 'dir' in elem.keys():
483 enumNegative = True
484 self.logMsg('diag', 'Enum', name, 'offset =', offset,
485 'extnumber =', extnumber, 'extends =', extends,
486 'enumNegative =', enumNegative)
487 # Now determine the actual enumerant value, as defined
488 # in the "Layers and Extensions" appendix of the spec.
489 numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
490 if enumNegative:
491 numVal *= -1
492 value = '%d' % numVal
493 # More logic needed!
494 self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
495 return [numVal, value]
496 if 'alias' in elem.keys():
497 alias_of = elem.get('alias')
498 if parent_for_alias_dereference is None:
499 return (None, alias_of)
500 siblings = parent_for_alias_dereference.findall('enum')
501 for sib in siblings:
502 sib_name = sib.get('name')
503 if sib_name == alias_of:
504 return self.enumToValue(sib, needsNum)
505 raise RuntimeError("Could not find the aliased enum value")
506 return [None, None]
507
508 def checkDuplicateEnums(self, enums):
509 """Check enumerated values for duplicates.
510
511 - enums - list of `<enum>` Elements
512
513 returns the list with duplicates stripped"""
514 # Dictionaries indexed by name and numeric value.
515 # Entries are [ Element, numVal, strVal ] matching name or value
516
517 nameMap = {}
518 valueMap = {}
519
520 stripped = []
521 for elem in enums:
522 name = elem.get('name')
523 (numVal, strVal) = self.enumToValue(elem, True)
524
525 if name in nameMap:
526 # Duplicate name found; check values
527 (name2, numVal2, strVal2) = nameMap[name]
528
529 # Duplicate enum values for the same name are benign. This
530 # happens when defining the same enum conditionally in
531 # several extension blocks.
532 if (strVal2 == strVal or (numVal is not None
533 and numVal == numVal2)):
534 True
535 # self.logMsg('info', 'checkDuplicateEnums: Duplicate enum (' + name +
536 # ') found with the same value:' + strVal)
537 else:
538 self.logMsg('warn', 'checkDuplicateEnums: Duplicate enum (' + name
539 + ') found with different values:' + strVal
540 + ' and ' + strVal2)
541
542 # Do not add the duplicate to the returned list
543 continue
544 elif numVal in valueMap:
545 # Duplicate value found (such as an alias); report it, but
546 # still add this enum to the list.
547 (name2, numVal2, strVal2) = valueMap[numVal]
548
549 msg = 'Two enums found with the same value: {} = {} = {}'.format(
550 name, name2.get('name'), strVal)
551 self.logMsg('error', msg)
552
553 # Track this enum to detect followon duplicates
554 nameMap[name] = [elem, numVal, strVal]
555 if numVal is not None:
556 valueMap[numVal] = [elem, numVal, strVal]
557
558 # Add this enum to the list
559 stripped.append(elem)
560
561 # Return the list
562 return stripped
563
564 def misracstyle(self):
565 return False;
566
567 def misracppstyle(self):
568 return False;
569
570 def deprecationComment(self, elem, indent = 0):
571 """If an API element is marked deprecated, return a brief comment
572 describing why.
573 Otherwise, return an empty string.
574
575 - elem - Element of the API.
576 API name is determined depending on the element tag.
577 - indent - number of spaces to indent the comment"""
578
579 reason = elem.get('deprecated')
580
581 # This is almost always the path taken.
582 if reason == None:
583 return ''
584
585 # There is actually a deprecated attribute.
586 padding = indent * ' '
587
588 # Determine the API name.
589 if elem.tag == 'member' or elem.tag == 'param':
590 name = elem.find('.//name').text
591 else:
592 name = elem.get('name')
593
594 if reason == 'aliased':
595 return f'{padding}// {name} is a legacy alias\n'
596 elif reason == 'ignored':
597 return f'{padding}// {name} is legacy and should not be used\n'
598 elif reason == 'true':
599 return f'{padding}// {name} is legacy, but no reason was given in the API XML\n'
600 else:
601 # This can be caught by schema validation
602 self.logMsg('error', f"{name} has an unknown legacy attribute value '{reason}'")
603 exit(1)
604
605 def buildEnumCDecl(self, expand, groupinfo, groupName):
606 """Generate the C declaration for an enum"""
607 if self.genOpts is None:
608 raise MissingGeneratorOptionsError()
609 if self.genOpts.conventions is None:
610 raise MissingGeneratorOptionsConventionsError()
611
612 groupElem = groupinfo.elem
613
614 # Determine the required bit width for the enum group.
615 # 32 is the default, which generates C enum types for the values.
616 bitwidth = 32
617
618 # If the constFlagBits preference is set, 64 is the default for bitmasks
619 if self.genOpts.conventions.constFlagBits and groupElem.get('type') == 'bitmask':
620 bitwidth = 64
621
622 # Check for an explicitly defined bitwidth, which will override any defaults.
623 if groupElem.get('bitwidth'):
624 try:
625 bitwidth = int(groupElem.get('bitwidth'))
626 except ValueError as ve:
627 self.logMsg('error', 'Invalid value for bitwidth attribute (', groupElem.get('bitwidth'), ') for ', groupName, ' - must be an integer value\n')
628 exit(1)
629
630 usebitmask = False
631 usedefine = False
632
633 # Bitmask flags can be generated as either "static const uint{32,64}_t" values,
634 # or as 32-bit C enums. 64-bit types must use uint64_t values.
635 if groupElem.get('type') == 'bitmask':
636 if bitwidth > 32 or self.misracppstyle():
637 usebitmask = True
638 if self.misracstyle():
639 usedefine = True
640
641 if usedefine or usebitmask:
642 # Validate the bitwidth and generate values appropriately
643 if bitwidth > 64:
644 self.logMsg('error', 'Invalid value for bitwidth attribute (', groupElem.get('bitwidth'), ') for bitmask type ', groupName, ' - must be less than or equal to 64\n')
645 exit(1)
646 else:
647 return self.buildEnumCDecl_BitmaskOrDefine(groupinfo, groupName, bitwidth, usedefine)
648 else:
649 # Validate the bitwidth and generate values appropriately
650 if bitwidth > 32:
651 self.logMsg('error', 'Invalid value for bitwidth attribute (', groupElem.get('bitwidth'), ') for enum type ', groupName, ' - must be less than or equal to 32\n')
652 exit(1)
653 else:
654 return self.buildEnumCDecl_Enum(expand, groupinfo, groupName)
655
656 def buildEnumCDecl_BitmaskOrDefine(self, groupinfo, groupName, bitwidth, usedefine):
657 """Generate the C declaration for an "enum" that is actually a
658 set of flag bits"""
659 groupElem = groupinfo.elem
660 flagTypeName = groupElem.get('name')
661
662 # Prefix
663 body = f"// Flag bits for {flagTypeName}\n"
664
665 if bitwidth == 64:
666 body += f"typedef VkFlags64 {flagTypeName};\n";
667 else:
668 body += f"typedef VkFlags {flagTypeName};\n";
669
670 # Maximum allowable value for a flag (unsigned 64-bit integer)
671 maxValidValue = 2**(64) - 1
672 minValidValue = 0
673
674 # Get a list of nested 'enum' tags.
675 enums = groupElem.findall('enum')
676
677 # Check for and report duplicates, and return a list with them
678 # removed.
679 enums = self.checkDuplicateEnums(enums)
680
681 # Accumulate non-numeric enumerant values separately and append
682 # them following the numeric values, to allow for aliases.
683 # NOTE: this does not do a topological sort yet, so aliases of
684 # aliases can still get in the wrong order.
685 aliasText = ''
686
687 # Loop over the nested 'enum' tags.
688 for elem in enums:
689 # Convert the value to an integer and use that to track min/max.
690 # Values of form -(number) are accepted but nothing more complex.
691 # Should catch exceptions here for more complex constructs. Not yet.
692 (numVal, strVal) = self.enumToValue(elem, True, bitwidth, True)
693 name = elem.get('name')
694
695 # Range check for the enum value
696 if numVal is not None and (numVal > maxValidValue or numVal < minValidValue):
697 self.logMsg('error', 'Allowable range for flag types in C is [', minValidValue, ',', maxValidValue, '], but', name, 'flag has a value outside of this (', strVal, ')\n')
698 exit(1)
699
700 decl = self.genRequirements(name, mustBeFound = False)
701
702 if self.isEnumRequired(elem):
703 protect = elem.get('protect')
704 if protect is not None:
705 body += f'#ifdef {protect}\n'
706
707 body += self.deprecationComment(elem, indent = 0)
708
709 if usedefine:
710 decl += f"#define {name} {strVal}\n"
711 elif self.misracppstyle():
712 decl += f"static constexpr {flagTypeName} {name} {{{strVal}}};\n"
713 else:
714 # Some C compilers only allow initializing a 'static const' variable with a literal value.
715 # So initializing an alias from another 'static const' value would fail to compile.
716 # Work around this by chasing the aliases to get the actual value.
717 while numVal is None:
718 alias = self.registry.tree.find(f"enums/enum[@name='{strVal}']")
719 if alias is not None:
720 (numVal, strVal) = self.enumToValue(alias, True, bitwidth, True)
721 else:
722 self.logMsg('error', f'No such alias {strVal} for enum {name}')
723 decl += f"static const {flagTypeName} {name} = {strVal};\n"
724
725 if numVal is not None:
726 body += decl
727 else:
728 aliasText += decl
729
730 if protect is not None:
731 body += '#endif\n'
732
733 # Now append the non-numeric enumerant values
734 body += aliasText
735
736 # Postfix
737
738 return ("bitmask", body)
739
740 def buildEnumCDecl_Enum(self, expand, groupinfo, groupName):
741 """Generate the C declaration for an enumerated type"""
742 groupElem = groupinfo.elem
743
744 # Break the group name into prefix and suffix portions for range
745 # enum generation
746 expandName = re.sub(r'([0-9]+|[a-z_])([A-Z0-9])', r'\1_\2', groupName).upper()
747 expandPrefix = expandName
748 expandSuffix = ''
749 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$', groupName)
750 if expandSuffixMatch:
751 expandSuffix = f"_{expandSuffixMatch.group()}"
752 # Strip off the suffix from the prefix
753 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
754
755 # Prefix
756 body = ["typedef enum %s {" % groupName]
757
758 # @@ Should use the type="bitmask" attribute instead
759 isEnum = ('FLAG_BITS' not in expandPrefix)
760
761 # Allowable range for a C enum - which is that of a signed 32-bit integer
762 maxValidValue = 2**(32 - 1) - 1
763 minValidValue = (maxValidValue * -1) - 1
764
765 # Get a list of nested 'enum' tags.
766 enums = groupElem.findall('enum')
767
768 # Check for and report duplicates, and return a list with them
769 # removed.
770 enums = self.checkDuplicateEnums(enums)
771
772 # Loop over the nested 'enum' tags. Keep track of the minimum and
773 # maximum numeric values, if they can be determined; but only for
774 # core API enumerants, not extension enumerants. This is inferred
775 # by looking for 'extends' attributes.
776 minName = None
777
778 # Accumulate non-numeric enumerant values separately and append
779 # them following the numeric values, to allow for aliases.
780 # NOTE: this does not do a topological sort yet, so aliases of
781 # aliases can still get in the wrong order.
782 aliasText = []
783
784 maxName = None
785 minValue = None
786 maxValue = None
787 for elem in enums:
788 # Convert the value to an integer and use that to track min/max.
789 # Values of form -(number) are accepted but nothing more complex.
790 # Should catch exceptions here for more complex constructs. Not yet.
791 (numVal, strVal) = self.enumToValue(elem, True)
792 name = elem.get('name')
793
794 # Extension enumerants are only included if they are required
795 if self.isEnumRequired(elem):
796 decl = ''
797
798 protect = elem.get('protect')
799 if protect is not None:
800 decl += f'#ifdef {protect}\n'
801
802
803 decl += self.genRequirements(name, mustBeFound = False, indent = 2)
804 decl += self.deprecationComment(elem, indent = 2)
805 decl += f' {name} = {strVal},'
806
807 if protect is not None:
808 decl += '\n#endif'
809
810 if numVal is not None:
811 body.append(decl)
812 else:
813 aliasText.append(decl)
814
815 # Range check for the enum value
816 if numVal is not None and (numVal > maxValidValue or numVal < minValidValue):
817 self.logMsg('error', 'Allowable range for C enum types is [', minValidValue, ',', maxValidValue, '], but', name, 'has a value outside of this (', strVal, ')\n')
818 exit(1)
819
820 # Do not track min/max for non-numbers (numVal is None)
821 if isEnum and numVal is not None and elem.get('extends') is None:
822 if minName is None:
823 minName = maxName = name
824 minValue = maxValue = numVal
825 elif minValue is None or numVal < minValue:
826 minName = name
827 minValue = numVal
828 elif maxValue is None or numVal > maxValue:
829 maxName = name
830 maxValue = numVal
831
832 # Now append the non-numeric enumerant values
833 body.extend(aliasText)
834
835 # Generate min/max value tokens - legacy use case.
836 if isEnum and expand:
837 body.extend((f' {expandPrefix}_BEGIN_RANGE{expandSuffix} = {minName},',
838 f' {expandPrefix}_END_RANGE{expandSuffix} = {maxName},',
839 f' {expandPrefix}_RANGE_SIZE{expandSuffix} = ({maxName} - {minName} + 1),'))
840
841 # Generate a range-padding value to ensure the enum is 32 bits, but
842 # only in code generators, so it does not appear in documentation
843 if (self.genOpts.codeGenerator or
844 self.conventions.generate_max_enum_in_docs):
845 body.append(f' {expandPrefix}_MAX_ENUM{expandSuffix} = 0x7FFFFFFF')
846
847 # Postfix
848 body.append("} %s;" % groupName)
849
850 # Determine appropriate section for this declaration
851 if groupElem.get('type') == 'bitmask':
852 section = 'bitmask'
853 else:
854 section = 'group'
855
856 return (section, '\n'.join(body))
857
858 def buildConstantCDecl(self, enuminfo, name, alias):
859 """Generate the C declaration for a constant (a single <enum>
860 value).
861
862 <enum> tags may specify their values in several ways, but are
863 usually just integers or floating-point numbers."""
864
865 (_, strVal) = self.enumToValue(enuminfo.elem, False)
866
867 if self.misracppstyle() and enuminfo.elem.get('type') and not alias:
868 # Generate e.g.: static constexpr uint32_t x = ~static_cast<uint32_t>(1U);
869 # This appeases MISRA "underlying type" rules.
870 typeStr = enuminfo.elem.get('type');
871 invert = '~' in strVal
872 number = strVal.strip("()~UL")
873 if typeStr != "float":
874 number += 'U'
875 strVal = "~" if invert else ""
876 strVal += f"static_cast<{typeStr}>({number})"
877 body = f"static constexpr {typeStr.ljust(9)}{name.ljust(33)} {{{strVal}}};"
878 elif enuminfo.elem.get('type') and not alias:
879 # Generate e.g.: #define x (~0ULL)
880 typeStr = enuminfo.elem.get('type');
881 invert = '~' in strVal
882 paren = '(' in strVal
883 number = strVal.strip("()~UL")
884 if typeStr != "float":
885 if typeStr == "uint64_t":
886 number += 'ULL'
887 else:
888 number += 'U'
889 strVal = "~" if invert else ""
890 strVal += number
891 if paren:
892 strVal = f"({strVal})";
893 body = f"#define {name.ljust(33)} {strVal}";
894 else:
895 body = f"#define {name.ljust(33)} {strVal}"
896
897 return body
898
899 def makeDir(self, path):
900 """Create a directory, if not already done.
901
902 Generally called from derived generators creating hierarchies."""
903 self.logMsg('diag', 'OutputGenerator::makeDir(', path, ')')
904 if path not in self.madeDirs:
905 # This can get race conditions with multiple writers, see
906 # https://stackoverflow.com/questions/273192/
907 if not os.path.exists(path):
908 os.makedirs(path)
909 self.madeDirs[path] = None
910
911 def beginFile(self, genOpts):
912 """Start a new interface file
913
914 - genOpts - GeneratorOptions controlling what is generated and how"""
915
916 self.genOpts = genOpts
917 if self.genOpts is None:
918 raise MissingGeneratorOptionsError()
919 if self.genOpts.conventions is None:
920 raise MissingGeneratorOptionsConventionsError()
921 self.should_insert_may_alias_macro = \
922 self.genOpts.conventions.should_insert_may_alias_macro(self.genOpts)
923 self.file_suffix = self.genOpts.conventions.file_suffix
924
925 # Try to import the API dictionary, apimap.py, if it exists. Nothing
926 # in apimap.py cannot be extracted directly from the XML, and in the
927 # future we should do that.
928 if self.genOpts.genpath is not None:
929 try:
930 sys.path.insert(0, self.genOpts.genpath)
931 import apimap
932 self.apidict = apimap
933 except ImportError:
934 self.apidict = None
935
936 self.conventions = genOpts.conventions
937
938 # Open a temporary file for accumulating output.
939 if self.genOpts.filename is not None:
940 self.outFile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', newline='\n', delete=False)
941 else:
942 self.outFile = sys.stdout
943
944 def endFile(self):
945 if self.errFile:
946 self.errFile.flush()
947 if self.warnFile:
948 self.warnFile.flush()
949 if self.diagFile:
950 self.diagFile.flush()
951 if self.outFile:
952 self.outFile.flush()
953 if self.outFile != sys.stdout and self.outFile != sys.stderr:
954 self.outFile.close()
955
956 if self.genOpts is None:
957 raise MissingGeneratorOptionsError()
958
959 # On successfully generating output, move the temporary file to the
960 # target file.
961 if self.genOpts.filename is not None:
962 directory = Path(self.genOpts.directory)
963 if sys.platform == 'win32':
964 if not Path.exists(directory):
965 os.makedirs(directory)
966 shutil.copy(self.outFile.name, directory / self.genOpts.filename)
967 os.remove(self.outFile.name)
968 self.genOpts = None
969
970 def beginFeature(self, interface, emit):
971 """Write interface for a feature and tag generated features as having been done.
972
973 - interface - element for the `<version>` / `<extension>` to generate
974 - emit - actually write to the header only when True"""
975 self.emit = emit
976 self.featureName = interface.get('name')
977 # If there is an additional 'protect' attribute in the feature, save it
978 self.featureExtraProtect = interface.get('protect')
979
980 def endFeature(self):
981 """Finish an interface file, closing it when done.
982
983 Derived classes responsible for emitting feature"""
984 self.featureName = None
985 self.featureExtraProtect = None
986
987 def genRequirements(self, name, mustBeFound = True, indent = 0):
988 """Generate text showing what core versions and extensions introduce
989 an API. This exists in the base Generator class because it is used by
990 the shared enumerant-generating interfaces (buildEnumCDecl, etc.).
991 Here it returns an empty string for most generators, but can be
992 overridden by e.g. DocGenerator.
993
994 - name - name of the API
995 - mustBeFound - If True, when requirements for 'name' cannot be
996 determined, a warning comment is generated.
997 """
998
999 return ''
1000
1001 def validateFeature(self, featureType, featureName):
1002 """Validate we are generating something only inside a `<feature>` tag"""
1003 if self.featureName is None:
1004 raise UserWarning('Attempt to generate', featureType,
1005 featureName, 'when not in feature')
1006
1007 def genType(self, typeinfo, name, alias):
1008 """Generate interface for a type
1009
1010 - typeinfo - TypeInfo for a type
1011
1012 Extend to generate as desired in your derived class."""
1013 self.validateFeature('type', name)
1014
1015 def genStruct(self, typeinfo, typeName, alias):
1016 """Generate interface for a C "struct" type.
1017
1018 - typeinfo - TypeInfo for a type interpreted as a struct
1019
1020 Extend to generate as desired in your derived class."""
1021 self.validateFeature('struct', typeName)
1022
1023 # The mixed-mode <member> tags may contain no-op <comment> tags.
1024 # It is convenient to remove them here where all output generators
1025 # will benefit.
1026 for member in typeinfo.elem.findall('.//member'):
1027 for comment in member.findall('comment'):
1028 member.remove(comment)
1029
1030 def genGroup(self, groupinfo, groupName, alias):
1031 """Generate interface for a group of enums (C "enum")
1032
1033 - groupinfo - GroupInfo for a group.
1034
1035 Extend to generate as desired in your derived class."""
1036
1037 self.validateFeature('group', groupName)
1038
1039 def genEnum(self, enuminfo, typeName, alias):
1040 """Generate interface for an enum (constant).
1041
1042 - enuminfo - EnumInfo for an enum
1043 - name - enum name
1044
1045 Extend to generate as desired in your derived class."""
1046 self.validateFeature('enum', typeName)
1047
1048 def genCmd(self, cmd, cmdinfo, alias):
1049 """Generate interface for a command.
1050
1051 - cmdinfo - CmdInfo for a command
1052
1053 Extend to generate as desired in your derived class."""
1054 self.validateFeature('command', cmdinfo)
1055
1056 def genSpirv(self, spirv, spirvinfo, alias):
1057 """Generate interface for a spirv element.
1058
1059 - spirvinfo - SpirvInfo for a command
1060
1061 Extend to generate as desired in your derived class."""
1062 return
1063
1064 def genFormat(self, format, formatinfo, alias):
1065 """Generate interface for a format element.
1066
1067 - formatinfo - FormatInfo
1068
1069 Extend to generate as desired in your derived class."""
1070 return
1071
1072 def genSyncStage(self, stageinfo):
1073 """Generate interface for a sync stage element.
1074
1075 - stageinfo - SyncStageInfo
1076
1077 Extend to generate as desired in your derived class."""
1078 return
1079
1080 def genSyncAccess(self, accessinfo):
1081 """Generate interface for a sync stage element.
1082
1083 - accessinfo - AccessInfo
1084
1085 Extend to generate as desired in your derived class."""
1086 return
1087
1088 def genSyncPipeline(self, pipelineinfo):
1089 """Generate interface for a sync stage element.
1090
1091 - pipelineinfo - SyncPipelineInfo
1092
1093 Extend to generate as desired in your derived class."""
1094 return
1095
1096 def makeProtoName(self, name, tail):
1097 """Turn a `<proto>` `<name>` into C-language prototype
1098 and typedef declarations for that name.
1099
1100 - name - contents of `<name>` tag
1101 - tail - whatever text follows that tag in the Element"""
1102 if self.genOpts is None:
1103 raise MissingGeneratorOptionsError()
1104 return self.genOpts.apientry + name + tail
1105
1106 def makeTypedefName(self, name, tail):
1107 """Make the function-pointer typedef name for a command."""
1108 if self.genOpts is None:
1109 raise MissingGeneratorOptionsError()
1110 return f"({self.genOpts.apientryp}PFN_{name}{tail})"
1111
1112 def makeCParamDecl(self, param, aligncol):
1113 """Return a string which is an indented, formatted
1114 declaration for a `<param>` or `<member>` block (e.g. function parameter
1115 or structure/union member).
1116
1117 - param - Element (`<param>` or `<member>`) to format
1118 - aligncol - if non-zero, attempt to align the nested `<name>` element
1119 at this column"""
1120 if self.genOpts is None:
1121 raise MissingGeneratorOptionsError()
1122 if self.genOpts.conventions is None:
1123 raise MissingGeneratorOptionsConventionsError()
1124 indent = ' '
1125 paramdecl = indent
1126 prefix = noneStr(param.text)
1127
1128 for elem in param:
1129 text = noneStr(elem.text)
1130 tail = noneStr(elem.tail)
1131
1132 if self.should_insert_may_alias_macro and self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail):
1133 # OpenXR-specific macro insertion - but not in apiinc for the spec
1134 tail = self.genOpts.conventions.make_voidpointer_alias(tail)
1135 if elem.tag == 'name' and aligncol > 0:
1136 self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam)
1137 # Align at specified column, if possible
1138 paramdecl = paramdecl.rstrip()
1139 oldLen = len(paramdecl)
1140 # This works around a problem where very long type names -
1141 # longer than the alignment column - would run into the tail
1142 # text.
1143 paramdecl = f"{paramdecl.ljust(aligncol - 1)} "
1144 newLen = len(paramdecl)
1145 self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl)
1146
1147 if (self.misracppstyle() and prefix.find('const ') != -1):
1148 # Change pointer type order from e.g. "const void *" to "void const *".
1149 # If the string starts with 'const', reorder it to be after the first type.
1150 paramdecl += f"{prefix.replace('const ', '') + text} const{tail}"
1151 else:
1152 paramdecl += prefix + text + tail
1153
1154 # Clear prefix for subsequent iterations
1155 prefix = ''
1156
1157 paramdecl = paramdecl + prefix
1158
1159 if aligncol == 0:
1160 # Squeeze out multiple spaces other than the indentation
1161 paramdecl = indent + ' '.join(paramdecl.split())
1162 return paramdecl
1163
1164 def getCParamTypeLength(self, param):
1165 """Return the length of the type field is an indented, formatted
1166 declaration for a `<param>` or `<member>` block (e.g. function parameter
1167 or structure/union member).
1168
1169 - param - Element (`<param>` or `<member>`) to identify"""
1170 if self.genOpts is None:
1171 raise MissingGeneratorOptionsError()
1172 if self.genOpts.conventions is None:
1173 raise MissingGeneratorOptionsConventionsError()
1174
1175 # Allow for missing <name> tag
1176 newLen = 0
1177 paramdecl = f" {noneStr(param.text)}"
1178 for elem in param:
1179 text = noneStr(elem.text)
1180 tail = noneStr(elem.tail)
1181
1182 if self.should_insert_may_alias_macro and self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail):
1183 # OpenXR-specific macro insertion
1184 tail = self.genOpts.conventions.make_voidpointer_alias(tail)
1185 if elem.tag == 'name':
1186 # Align at specified column, if possible
1187 newLen = len(paramdecl.rstrip())
1188 self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen)
1189 paramdecl += text + tail
1190
1191 return newLen
1192
1193 def getMaxCParamTypeLength(self, info):
1194 """Return the length of the longest type field for a member/parameter.
1195
1196 - info - TypeInfo or CommandInfo.
1197 """
1198 lengths = (self.getCParamTypeLength(member)
1199 for member in info.getMembers())
1200 return max(lengths)
1201
1202 def getHandleParent(self, typename):
1203 """Get the parent of a handle object."""
1204 if self.registry is None:
1205 raise MissingRegistryError()
1206
1207 info = self.registry.typedict.get(typename)
1208 if info is None:
1209 return None
1210
1211 elem = info.elem
1212 if elem is not None:
1213 return elem.get('parent')
1214
1215 return None
1216
1217 def iterateHandleAncestors(self, typename):
1218 """Iterate through the ancestors of a handle type."""
1219 current = self.getHandleParent(typename)
1220 while current is not None:
1221 yield current
1222 current = self.getHandleParent(current)
1223
1224 def getHandleAncestors(self, typename):
1225 """Get the ancestors of a handle object."""
1226 return list(self.iterateHandleAncestors(typename))
1227
1228 def getTypeCategory(self, typename):
1229 """Get the category of a type."""
1230 if self.registry is None:
1231 raise MissingRegistryError()
1232
1233 info = self.registry.typedict.get(typename)
1234 if info is None:
1235 return None
1236
1237 elem = info.elem
1238 if elem is not None:
1239 return elem.get('category')
1240 return None
1241
1242 def isStructAlwaysValid(self, structname):
1243 """Try to do check if a structure is always considered valid (i.e. there is no rules to its acceptance)."""
1244 # A conventions object is required for this call.
1245 if not self.conventions:
1246 raise RuntimeError("To use isStructAlwaysValid, be sure your options include a Conventions object.")
1247 if self.registry is None:
1248 raise MissingRegistryError()
1249
1250 if self.conventions.type_always_valid(structname):
1251 return True
1252
1253 category = self.getTypeCategory(structname)
1254 if self.conventions.category_requires_validation(category):
1255 return False
1256
1257 info = self.registry.typedict.get(structname)
1258 if info is None:
1259 self.logMsg('error', f'isStructAlwaysValid({structname}) - structure not found in typedict')
1260
1261 members = info.getMembers()
1262
1263 for member in members:
1264 member_name = getElemName(member)
1265 if member_name in (self.conventions.structtype_member_name,
1266 self.conventions.nextpointer_member_name):
1267 return False
1268
1269 if member.get('noautovalidity'):
1270 return False
1271
1272 member_type = getElemType(member)
1273
1274 if member_type in ('void', 'char') or self.paramIsArray(member) or self.paramIsPointer(member):
1275 return False
1276
1277 if self.conventions.type_always_valid(member_type):
1278 continue
1279
1280 member_category = self.getTypeCategory(member_type)
1281
1282 if self.conventions.category_requires_validation(member_category):
1283 return False
1284
1285 if member_category in ('struct', 'union'):
1286 if self.isStructAlwaysValid(member_type) is False:
1287 return False
1288
1289 return True
1290
1291 def paramIsArray(self, param):
1292 """Check if the parameter passed in is a pointer to an array.
1293
1294 param the XML information for the param
1295 """
1296 return param.get('len') is not None
1297
1298 def paramIsPointer(self, param):
1299 """Check if the parameter passed in is a pointer.
1300
1301 param the XML information for the param
1302 """
1303 tail = param.find('type').tail
1304 return tail is not None and '*' in tail
1305
1306 def isEnumRequired(self, elem):
1307 """Return True if this `<enum>` element is
1308 required, False otherwise
1309
1310 - elem - `<enum>` element to test"""
1311 required = elem.get('required') is not None
1312 self.logMsg('diag', 'isEnumRequired:', elem.get('name'),
1313 '->', required)
1314 return required
1315
1316 # @@@ This code is overridden by equivalent code now run in
1317 # @@@ Registry.generateFeature
1318
1319 required = False
1320
1321 extname = elem.get('extname')
1322 if extname is not None:
1323 # 'supported' attribute was injected when the <enum> element was
1324 # moved into the <enums> group in Registry.parseTree()
1325 if self.genOpts.defaultExtensions == elem.get('supported'):
1326 required = True
1327 elif re.match(self.genOpts.addExtensions, extname) is not None:
1328 required = True
1329 elif elem.get('version') is not None:
1330 required = re.match(self.genOpts.emitversions, elem.get('version')) is not None
1331 else:
1332 required = True
1333
1334 return required
1335
1336 def makeCDecls(self, cmd):
1337 """Return C prototype and function pointer typedef for a
1338 `<command>` Element, as a two-element list of strings.
1339
1340 - cmd - Element containing a `<command>` tag"""
1341 if self.genOpts is None:
1342 raise MissingGeneratorOptionsError()
1343 proto = cmd.find('proto')
1344 params = cmd.findall('param')
1345 # Begin accumulating prototype and typedef strings
1346 pdecl = self.genOpts.apicall
1347 tdecl = 'typedef '
1348
1349 # Insert the function return type/name.
1350 # For prototypes, add APIENTRY macro before the name
1351 # For typedefs, add (APIENTRY *<name>) around the name and
1352 # use the PFN_cmdnameproc naming convention.
1353 # Done by walking the tree for <proto> element by element.
1354 # etree has elem.text followed by (elem[i], elem[i].tail)
1355 # for each child element and any following text
1356 # Leading text
1357 pdecl += noneStr(proto.text)
1358 tdecl += noneStr(proto.text)
1359 # For each child element, if it is a <name> wrap in appropriate
1360 # declaration. Otherwise append its contents and tail contents.
1361 for elem in proto:
1362 text = noneStr(elem.text)
1363 tail = noneStr(elem.tail)
1364 if elem.tag == 'name':
1365 pdecl += self.makeProtoName(text, tail)
1366 tdecl += self.makeTypedefName(text, tail)
1367 else:
1368 pdecl += text + tail
1369 tdecl += text + tail
1370
1371 if self.genOpts.alignFuncParam == 0:
1372 # Squeeze out multiple spaces - there is no indentation
1373 pdecl = ' '.join(pdecl.split())
1374 tdecl = ' '.join(tdecl.split())
1375
1376 # Now add the parameter declaration list, which is identical
1377 # for prototypes and typedefs. Concatenate all the text from
1378 # a <param> node without the tags. No tree walking required
1379 # since all tags are ignored.
1380 # Uses: self.indentFuncProto
1381 # self.indentFuncPointer
1382 # self.alignFuncParam
1383 n = len(params)
1384 # Indented parameters
1385 if n > 0:
1386 indentdecl = '(\n'
1387 indentdecl += ',\n'.join(self.makeCParamDecl(p, self.genOpts.alignFuncParam)
1388 for p in params)
1389 indentdecl += ');'
1390 else:
1391 indentdecl = '(void);'
1392 # Non-indented parameters
1393 paramdecl = '('
1394 if n > 0:
1395 paramnames = []
1396 if self.misracppstyle():
1397 for p in params:
1398 param = ''
1399 firstIter = True;
1400 for t in p.itertext():
1401 if (firstIter):
1402 prefix = t
1403 firstIter = False
1404 else:
1405 # Change pointer type order from e.g. "const void *" to "void const *".
1406 # If the string starts with 'const', reorder it to be after the first type.
1407 if (prefix.find('const ') != -1):
1408 param += f"{prefix.replace('const ', '') + t} const "
1409 else:
1410 param += prefix + t
1411 # Clear prefix for subsequent iterations
1412 prefix = ''
1413 paramnames.append(param);
1414 else:
1415 paramnames = (''.join(t for t in p.itertext())
1416 for p in params)
1417 paramdecl += ', '.join(paramnames)
1418 else:
1419 paramdecl += 'void'
1420 paramdecl += ");"
1421
1422 return [pdecl + indentdecl, tdecl + paramdecl]
1423
1424 def newline(self):
1425 """Print a newline to the output file (utility function)"""
1426 write('', file=self.outFile)
1427
1428 def setRegistry(self, registry):
1429 self.registry = registry