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