xref: /aosp_15_r20/external/angle/third_party/vulkan-headers/src/registry/reg.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
7"""Types and classes for manipulating an API registry."""
8
9import copy
10import re
11import sys
12import xml.etree.ElementTree as etree
13from collections import defaultdict, deque, namedtuple
14
15from generator import GeneratorOptions, OutputGenerator, noneStr, write
16from apiconventions import APIConventions
17
18def apiNameMatch(str, supported):
19    """Return whether a required api name matches a pattern specified for an
20    XML <feature> 'api' attribute or <extension> 'supported' attribute.
21
22    - str - API name such as 'vulkan' or 'openxr'. May be None, in which
23        case it never matches (this should not happen).
24    - supported - comma-separated list of XML API names. May be None, in
25        which case str always matches (this is the usual case)."""
26
27    if str is not None:
28        return supported is None or str in supported.split(',')
29
30    # Fallthrough case - either str is None or the test failed
31    return False
32
33def matchAPIProfile(api, profile, elem):
34    """Return whether an API and profile
35    being generated matches an element's profile
36
37    - api - string naming the API to match
38    - profile - string naming the profile to match
39    - elem - Element which (may) have 'api' and 'profile'
40      attributes to match to.
41
42    If a tag is not present in the Element, the corresponding API
43      or profile always matches.
44
45    Otherwise, the tag must exactly match the API or profile.
46
47    Thus, if 'profile' = core:
48
49    - `<remove>`  with no attribute will match
50    - `<remove profile="core">` will match
51    - `<remove profile="compatibility">` will not match
52
53    Possible match conditions:
54
55    ```
56      Requested   Element
57      Profile     Profile
58      ---------   --------
59      None        None        Always matches
60      'string'    None        Always matches
61      None        'string'    Does not match. Cannot generate multiple APIs
62                              or profiles, so if an API/profile constraint
63                              is present, it must be asked for explicitly.
64      'string'    'string'    Strings must match
65    ```
66
67    ** In the future, we will allow regexes for the attributes,
68    not just strings, so that `api="^(gl|gles2)"` will match. Even
69    this is not really quite enough, we might prefer something
70    like `"gl(core)|gles1(common-lite)"`."""
71    # Match 'api', if present
72    elem_api = elem.get('api')
73    if elem_api:
74        if api is None:
75            raise UserWarning("No API requested, but 'api' attribute is present with value '"
76                              + elem_api + "'")
77        elif api != elem_api:
78            # Requested API does not match attribute
79            return False
80    elem_profile = elem.get('profile')
81    if elem_profile:
82        if profile is None:
83            raise UserWarning("No profile requested, but 'profile' attribute is present with value '"
84                              + elem_profile + "'")
85        elif profile != elem_profile:
86            # Requested profile does not match attribute
87            return False
88    return True
89
90
91def mergeAPIs(tree, fromApiNames, toApiName):
92    """Merge multiple APIs using the precedence order specified in apiNames.
93    Also deletes <remove> elements.
94
95        tree - Element at the root of the hierarchy to merge.
96        apiNames - list of strings of API names."""
97
98    stack = deque()
99    stack.append(tree)
100
101    while len(stack) > 0:
102        parent = stack.pop()
103
104        for child in parent.findall('*'):
105            if child.tag == 'remove':
106                # Remove <remove> elements
107                parent.remove(child)
108            else:
109                stack.append(child)
110
111            supportedList = child.get('supported')
112            if supportedList:
113                supportedList = supportedList.split(',')
114                for apiName in [toApiName] + fromApiNames:
115                    if apiName in supportedList:
116                        child.set('supported', toApiName)
117
118            if child.get('api'):
119                definitionName = None
120                definitionVariants = []
121
122                # Keep only one definition with the same name if there are multiple definitions
123                if child.tag in ['type']:
124                    if child.get('name') is not None:
125                        definitionName = child.get('name')
126                        definitionVariants = parent.findall(f"{child.tag}[@name='{definitionName}']")
127                    else:
128                        definitionName = child.find('name').text
129                        definitionVariants = parent.findall(f"{child.tag}/name[.='{definitionName}']/..")
130                elif child.tag in ['member', 'param']:
131                    definitionName = child.find('name').text
132                    definitionVariants = parent.findall(f"{child.tag}/name[.='{definitionName}']/..")
133                elif child.tag in ['enum', 'feature']:
134                    definitionName = child.get('name')
135                    definitionVariants = parent.findall(f"{child.tag}[@name='{definitionName}']")
136                elif child.tag in ['require']:
137                    definitionName = child.get('feature')
138                    definitionVariants = parent.findall(f"{child.tag}[@feature='{definitionName}']")
139                elif child.tag in ['command']:
140                    definitionName = child.find('proto/name').text
141                    definitionVariants = parent.findall(f"{child.tag}/proto/name[.='{definitionName}']/../..")
142
143                if definitionName:
144                    bestMatchApi = None
145                    requires = None
146                    for apiName in [toApiName] + fromApiNames:
147                        for variant in definitionVariants:
148                            # Keep any requires attributes from the target API
149                            if variant.get('requires') and variant.get('api') == apiName:
150                                requires = variant.get('requires')
151                            # Find the best matching definition
152                            if apiName in variant.get('api').split(',') and bestMatchApi is None:
153                                bestMatchApi = variant.get('api')
154
155                    if bestMatchApi:
156                        for variant in definitionVariants:
157                            if variant.get('api') != bestMatchApi:
158                                # Only keep best matching definition
159                                parent.remove(variant)
160                            else:
161                                # Add requires attribute from the target API if it is not overridden
162                                if requires is not None and variant.get('requires') is None:
163                                    variant.set('requires', requires)
164                                variant.set('api', toApiName)
165
166
167def stripNonmatchingAPIs(tree, apiName, actuallyDelete = True):
168    """Remove tree Elements with 'api' attributes matching apiName.
169
170        tree - Element at the root of the hierarchy to strip. Only its
171            children can actually be removed, not the tree itself.
172        apiName - string which much match a command-separated component of
173            the 'api' attribute.
174        actuallyDelete - only delete matching elements if True."""
175
176    stack = deque()
177    stack.append(tree)
178
179    while len(stack) > 0:
180        parent = stack.pop()
181
182        for child in parent.findall('*'):
183            api = child.get('api')
184
185            if apiNameMatch(apiName, api):
186                # Add child to the queue
187                stack.append(child)
188            elif not apiNameMatch(apiName, api):
189                # Child does not match requested api. Remove it.
190                if actuallyDelete:
191                    parent.remove(child)
192
193
194class BaseInfo:
195    """Base class for information about a registry feature
196    (type/group/enum/command/API/extension).
197
198    Represents the state of a registry feature, used during API generation.
199    """
200
201    def __init__(self, elem):
202        self.required = False
203        """should this feature be defined during header generation
204        (has it been removed by a profile or version)?"""
205
206        self.declared = False
207        "has this feature been defined already?"
208
209        self.elem = elem
210        "etree Element for this feature"
211
212    def resetState(self):
213        """Reset required/declared to initial values. Used
214        prior to generating a new API interface."""
215        self.required = False
216        self.declared = False
217
218    def compareKeys(self, info, key, required = False):
219        """Return True if self.elem and info.elem have the same attribute
220           value for key.
221           If 'required' is not True, also returns True if neither element
222           has an attribute value for key."""
223
224        if required and key not in self.elem.keys():
225            return False
226        return self.elem.get(key) == info.elem.get(key)
227
228    def compareElem(self, info, infoName):
229        """Return True if self.elem and info.elem have the same definition.
230        info - the other object
231        infoName - 'type' / 'group' / 'enum' / 'command' / 'feature' /
232                   'extension'"""
233
234        if infoName == 'enum':
235            if self.compareKeys(info, 'extends'):
236                # Either both extend the same type, or no type
237                if (self.compareKeys(info, 'value', required = True) or
238                    self.compareKeys(info, 'bitpos', required = True)):
239                    # If both specify the same value or bit position,
240                    # they are equal
241                    return True
242                elif (self.compareKeys(info, 'extnumber') and
243                      self.compareKeys(info, 'offset') and
244                      self.compareKeys(info, 'dir')):
245                    # If both specify the same relative offset, they are equal
246                    return True
247                elif (self.compareKeys(info, 'alias')):
248                    # If both are aliases of the same value
249                    return True
250                else:
251                    return False
252            else:
253                # The same enum cannot extend two different types
254                return False
255        else:
256            # Non-<enum>s should never be redefined
257            return False
258
259
260class TypeInfo(BaseInfo):
261    """Registry information about a type. No additional state
262      beyond BaseInfo is required."""
263
264    def __init__(self, elem):
265        BaseInfo.__init__(self, elem)
266        self.additionalValidity = []
267        self.removedValidity = []
268
269    def getMembers(self):
270        """Get a collection of all member elements for this type, if any."""
271        return self.elem.findall('member')
272
273    def resetState(self):
274        BaseInfo.resetState(self)
275        self.additionalValidity = []
276        self.removedValidity = []
277
278
279class GroupInfo(BaseInfo):
280    """Registry information about a group of related enums
281    in an <enums> block, generally corresponding to a C "enum" type."""
282
283    def __init__(self, elem):
284        BaseInfo.__init__(self, elem)
285
286
287class EnumInfo(BaseInfo):
288    """Registry information about an enum"""
289
290    def __init__(self, elem):
291        BaseInfo.__init__(self, elem)
292        self.type = elem.get('type')
293        """numeric type of the value of the <enum> tag
294        ( '' for GLint, 'u' for GLuint, 'ull' for GLuint64 )"""
295        if self.type is None:
296            self.type = ''
297
298
299class CmdInfo(BaseInfo):
300    """Registry information about a command"""
301
302    def __init__(self, elem):
303        BaseInfo.__init__(self, elem)
304        self.additionalValidity = []
305        self.removedValidity = []
306
307    def getParams(self):
308        """Get a collection of all param elements for this command, if any."""
309        return self.elem.findall('param')
310
311    def resetState(self):
312        BaseInfo.resetState(self)
313        self.additionalValidity = []
314        self.removedValidity = []
315
316
317class FeatureInfo(BaseInfo):
318    """Registry information about an API <feature>
319    or <extension>."""
320
321    def __init__(self, elem):
322        BaseInfo.__init__(self, elem)
323        self.name = elem.get('name')
324        "feature name string (e.g. 'VK_KHR_surface')"
325
326        self.emit = False
327        "has this feature been defined already?"
328
329        self.sortorder = int(elem.get('sortorder', 0))
330        """explicit numeric sort key within feature and extension groups.
331        Defaults to 0."""
332
333        # Determine element category (vendor). Only works
334        # for <extension> elements.
335        if elem.tag == 'feature':
336            # Element category (vendor) is meaningless for <feature>
337            self.category = 'VERSION'
338            """category, e.g. VERSION or khr/vendor tag"""
339
340            self.version = elem.get('name')
341            """feature name string"""
342
343            self.versionNumber = elem.get('number')
344            """versionNumber - API version number, taken from the 'number'
345               attribute of <feature>. Extensions do not have API version
346               numbers and are assigned number 0."""
347
348            self.number = 0
349            self.supported = None
350        else:
351            # Extract vendor portion of <APIprefix>_<vendor>_<name>
352            self.category = self.name.split('_', 2)[1]
353            self.version = "0"
354            self.versionNumber = "0"
355
356            self.number = int(elem.get('number','0'))
357            """extension number, used for ordering and for assigning
358            enumerant offsets. <feature> features do not have extension
359            numbers and are assigned number 0, as are extensions without
360            numbers, so sorting works."""
361
362            self.supported = elem.get('supported', 'disabled')
363
364class SpirvInfo(BaseInfo):
365    """Registry information about an API <spirvextensions>
366    or <spirvcapability>."""
367
368    def __init__(self, elem):
369        BaseInfo.__init__(self, elem)
370
371class FormatInfo(BaseInfo):
372    """Registry information about an API <format>."""
373
374    def __init__(self, elem, condition):
375        BaseInfo.__init__(self, elem)
376        # Need to save the condition here when it is known
377        self.condition = condition
378
379class SyncStageInfo(BaseInfo):
380    """Registry information about <syncstage>."""
381
382    def __init__(self, elem, condition):
383        BaseInfo.__init__(self, elem)
384        # Need to save the condition here when it is known
385        self.condition = condition
386
387class SyncAccessInfo(BaseInfo):
388    """Registry information about <syncaccess>."""
389
390    def __init__(self, elem, condition):
391        BaseInfo.__init__(self, elem)
392        # Need to save the condition here when it is known
393        self.condition = condition
394
395class SyncPipelineInfo(BaseInfo):
396    """Registry information about <syncpipeline>."""
397
398    def __init__(self, elem):
399        BaseInfo.__init__(self, elem)
400
401class Registry:
402    """Object representing an API registry, loaded from an XML file."""
403
404    def __init__(self, gen=None, genOpts=None):
405        if gen is None:
406            # If not specified, give a default object so messaging will work
407            self.gen = OutputGenerator()
408        else:
409            self.gen = gen
410        "Output generator used to write headers / messages"
411
412        if genOpts is None:
413            # If no generator is provided, we may still need the XML API name
414            # (for example, in genRef.py).
415            self.genOpts = GeneratorOptions(apiname = APIConventions().xml_api_name)
416        else:
417            self.genOpts = genOpts
418        "Options controlling features to write and how to format them"
419
420        self.gen.registry = self
421        self.gen.genOpts = self.genOpts
422        self.gen.genOpts.registry = self
423
424        self.tree = None
425        "ElementTree containing the root `<registry>`"
426
427        self.typedict = {}
428        "dictionary of TypeInfo objects keyed by type name"
429
430        self.groupdict = {}
431        "dictionary of GroupInfo objects keyed by group name"
432
433        self.enumdict = {}
434        "dictionary of EnumInfo objects keyed by enum name"
435
436        self.cmddict = {}
437        "dictionary of CmdInfo objects keyed by command name"
438
439        self.aliasdict = {}
440        "dictionary of type and command names mapped to their alias, such as VkFooKHR -> VkFoo"
441
442        self.enumvaluedict = {}
443        "dictionary of enum values mapped to their type, such as VK_FOO_VALUE -> VkFoo"
444
445        self.apidict = {}
446        "dictionary of FeatureInfo objects for `<feature>` elements keyed by API name"
447
448        self.extensions = []
449        "list of `<extension>` Elements"
450
451        self.extdict = {}
452        "dictionary of FeatureInfo objects for `<extension>` elements keyed by extension name"
453
454        self.spirvextdict = {}
455        "dictionary of FeatureInfo objects for `<spirvextension>` elements keyed by spirv extension name"
456
457        self.spirvcapdict = {}
458        "dictionary of FeatureInfo objects for `<spirvcapability>` elements keyed by spirv capability name"
459
460        self.formatsdict = {}
461        "dictionary of FeatureInfo objects for `<format>` elements keyed by VkFormat name"
462
463        self.syncstagedict = {}
464        "dictionary of Sync*Info objects for `<syncstage>` elements keyed by VkPipelineStageFlagBits2 name"
465
466        self.syncaccessdict = {}
467        "dictionary of Sync*Info objects for `<syncaccess>` elements keyed by VkAccessFlagBits2 name"
468
469        self.syncpipelinedict = {}
470        "dictionary of Sync*Info objects for `<syncpipeline>` elements keyed by pipeline type name"
471
472        self.emitFeatures = False
473        """True to actually emit features for a version / extension,
474        or False to just treat them as emitted"""
475
476        self.breakPat = None
477        "regexp pattern to break on when generating names"
478        # self.breakPat     = re.compile('VkFenceImportFlagBits.*')
479
480        self.requiredextensions = []  # Hack - can remove it after validity generator goes away
481
482        # ** Global types for automatic source generation **
483        # Length Member data
484        self.commandextensiontuple = namedtuple('commandextensiontuple',
485                                                ['command',        # The name of the command being modified
486                                                 'value',          # The value to append to the command
487                                                 'extension'])     # The name of the extension that added it
488        self.validextensionstructs = defaultdict(list)
489        self.commandextensionsuccesses = []
490        self.commandextensionerrors = []
491
492        self.filename     = None
493
494    def loadElementTree(self, tree):
495        """Load ElementTree into a Registry object and parse it."""
496        self.tree = tree
497        self.parseTree()
498
499    def loadFile(self, file):
500        """Load an API registry XML file into a Registry object and parse it"""
501        self.filename = file
502        self.tree = etree.parse(file)
503        self.parseTree()
504
505    def setGenerator(self, gen):
506        """Specify output generator object.
507
508        `None` restores the default generator."""
509        self.gen = gen
510        self.gen.setRegistry(self)
511
512    def addElementInfo(self, elem, info, infoName, dictionary):
513        """Add information about an element to the corresponding dictionary.
514
515        Intended for internal use only.
516
517        - elem - `<type>`/`<enums>`/`<enum>`/`<command>`/`<feature>`/`<extension>`/`<spirvextension>`/`<spirvcapability>`/`<format>`/`<syncstage>`/`<syncaccess>`/`<syncpipeline>` Element
518        - info - corresponding {Type|Group|Enum|Cmd|Feature|Spirv|Format|SyncStage|SyncAccess|SyncPipeline}Info object
519        - infoName - 'type' / 'group' / 'enum' / 'command' / 'feature' / 'extension' / 'spirvextension' / 'spirvcapability' / 'format' / 'syncstage' / 'syncaccess' / 'syncpipeline'
520        - dictionary - self.{type|group|enum|cmd|api|ext|format|spirvext|spirvcap|sync}dict
521
522        The dictionary key is the element 'name' attribute."""
523
524        # self.gen.logMsg('diag', 'Adding ElementInfo.required =',
525        #     info.required, 'name =', elem.get('name'))
526        key = elem.get('name')
527        if key in dictionary:
528            if not dictionary[key].compareElem(info, infoName):
529                self.gen.logMsg('warn', 'Attempt to redefine', key,
530                                '(this should not happen)')
531        else:
532            dictionary[key] = info
533
534    def lookupElementInfo(self, fname, dictionary):
535        """Find a {Type|Enum|Cmd}Info object by name.
536
537        Intended for internal use only.
538
539        If an object qualified by API name exists, use that.
540
541        - fname - name of type / enum / command
542        - dictionary - self.{type|enum|cmd}dict"""
543        key = (fname, self.genOpts.apiname)
544        if key in dictionary:
545            # self.gen.logMsg('diag', 'Found API-specific element for feature', fname)
546            return dictionary[key]
547        if fname in dictionary:
548            # self.gen.logMsg('diag', 'Found generic element for feature', fname)
549            return dictionary[fname]
550
551        return None
552
553    def breakOnName(self, regexp):
554        """Specify a feature name regexp to break on when generating features."""
555        self.breakPat = re.compile(regexp)
556
557    def addEnumValue(self, enum, type_name):
558        """Track aliasing and map back from enum values to their type"""
559        # Record alias, if any
560        value = enum.get('name')
561        alias = enum.get('alias')
562        if alias:
563            self.aliasdict[value] = alias
564        # Map the value back to the type
565        if type_name in self.aliasdict:
566            type_name = self.aliasdict[type_name]
567        if value in self.enumvaluedict:
568            # Some times the same enum is defined by multiple extensions
569            assert(type_name == self.enumvaluedict[value])
570        else:
571            self.enumvaluedict[value] = type_name
572
573    def parseTree(self):
574        """Parse the registry Element, once created"""
575        # This must be the Element for the root <registry>
576        if self.tree is None:
577            raise RuntimeError("Tree not initialized!")
578        self.reg = self.tree.getroot()
579
580        # Preprocess the tree in one of the following ways:
581        # - either merge a set of APIs to another API based on their 'api' attributes
582        # - or remove all elements with non-matching 'api' attributes
583        # The preprocessing happens through a breath-first tree traversal.
584        # This is a blunt hammer, but eliminates the need to track and test
585        # the apis deeper in processing to select the correct elements and
586        # avoid duplicates.
587        # Schema validation should prevent duplicate elements with
588        # overlapping api attributes, or where one element has an api
589        # attribute and the other does not.
590
591        if self.genOpts.mergeApiNames:
592            mergeAPIs(self.reg, self.genOpts.mergeApiNames.split(','), self.genOpts.apiname)
593        else:
594            stripNonmatchingAPIs(self.reg, self.genOpts.apiname, actuallyDelete = True)
595
596        self.aliasdict = {}
597        self.enumvaluedict = {}
598
599        # Create dictionary of registry types from toplevel <types> tags
600        # and add 'name' attribute to each <type> tag (where missing)
601        # based on its <name> element.
602        #
603        # There is usually one <types> block; more are OK
604        # Required <type> attributes: 'name' or nested <name> tag contents
605        self.typedict = {}
606        for type_elem in self.reg.findall('types/type'):
607            # If the <type> does not already have a 'name' attribute, set
608            # it from contents of its <name> tag.
609            name = type_elem.get('name')
610            if name is None:
611                name_elem = type_elem.find('name')
612                if name_elem is None or not name_elem.text:
613                    raise RuntimeError("Type without a name!")
614                name = name_elem.text
615                type_elem.set('name', name)
616            self.addElementInfo(type_elem, TypeInfo(type_elem), 'type', self.typedict)
617
618            # Record alias, if any
619            alias = type_elem.get('alias')
620            if alias:
621                self.aliasdict[name] = alias
622
623        # Create dictionary of registry enum groups from <enums> tags.
624        #
625        # Required <enums> attributes: 'name'. If no name is given, one is
626        # generated, but that group cannot be identified and turned into an
627        # enum type definition - it is just a container for <enum> tags.
628        self.groupdict = {}
629        for group in self.reg.findall('enums'):
630            self.addElementInfo(group, GroupInfo(group), 'group', self.groupdict)
631
632        # Create dictionary of registry enums from <enum> tags
633        #
634        # <enums> tags usually define different namespaces for the values
635        #   defined in those tags, but the actual names all share the
636        #   same dictionary.
637        # Required <enum> attributes: 'name', 'value'
638        # For containing <enums> which have type="enum" or type="bitmask",
639        # tag all contained <enum>s are required. This is a stopgap until
640        # a better scheme for tagging core and extension enums is created.
641        self.enumdict = {}
642        for enums in self.reg.findall('enums'):
643            required = (enums.get('type') is not None)
644            type_name = enums.get('name')
645            # Enum values are defined only for the type that is not aliased to something else.
646            assert(type_name not in self.aliasdict)
647            for enum in enums.findall('enum'):
648                enumInfo = EnumInfo(enum)
649                enumInfo.required = required
650                self.addElementInfo(enum, enumInfo, 'enum', self.enumdict)
651                self.addEnumValue(enum, type_name)
652
653        # Create dictionary of registry commands from <command> tags
654        # and add 'name' attribute to each <command> tag (where missing)
655        # based on its <proto><name> element.
656        #
657        # There is usually only one <commands> block; more are OK.
658        # Required <command> attributes: 'name' or <proto><name> tag contents
659        self.cmddict = {}
660        # List of commands which alias others. Contains
661        #   [ name, aliasName, element ]
662        # for each alias
663        cmdAlias = []
664        for cmd in self.reg.findall('commands/command'):
665            # If the <command> does not already have a 'name' attribute, set
666            # it from contents of its <proto><name> tag.
667            name = cmd.get('name')
668            if name is None:
669                name_elem = cmd.find('proto/name')
670                if name_elem is None or not name_elem.text:
671                    raise RuntimeError("Command without a name!")
672                name = cmd.set('name', name_elem.text)
673            ci = CmdInfo(cmd)
674            self.addElementInfo(cmd, ci, 'command', self.cmddict)
675            alias = cmd.get('alias')
676            if alias:
677                cmdAlias.append([name, alias, cmd])
678                self.aliasdict[name] = alias
679
680        # Now loop over aliases, injecting a copy of the aliased command's
681        # Element with the aliased prototype name replaced with the command
682        # name - if it exists.
683        for (name, alias, cmd) in cmdAlias:
684            if alias in self.cmddict:
685                aliasInfo = self.cmddict[alias]
686                cmdElem = copy.deepcopy(aliasInfo.elem)
687                cmdElem.find('proto/name').text = name
688                cmdElem.set('name', name)
689                cmdElem.set('alias', alias)
690                ci = CmdInfo(cmdElem)
691                # Replace the dictionary entry for the CmdInfo element
692                self.cmddict[name] = ci
693
694                # @  newString = etree.tostring(base, encoding="unicode").replace(aliasValue, aliasName)
695                # @elem.append(etree.fromstring(replacement))
696            else:
697                self.gen.logMsg('warn', 'No matching <command> found for command',
698                                cmd.get('name'), 'alias', alias)
699
700        # Create dictionaries of API and extension interfaces
701        #   from toplevel <api> and <extension> tags.
702        self.apidict = {}
703        format_condition = dict()
704        for feature in self.reg.findall('feature'):
705            featureInfo = FeatureInfo(feature)
706            self.addElementInfo(feature, featureInfo, 'feature', self.apidict)
707
708            # Add additional enums defined only in <feature> tags
709            # to the corresponding enumerated type.
710            # When seen here, the <enum> element, processed to contain the
711            # numeric enum value, is added to the corresponding <enums>
712            # element, as well as adding to the enum dictionary. It is no
713            # longer removed from the <require> element it is introduced in.
714            # Instead, generateRequiredInterface ignores <enum> elements
715            # that extend enumerated types.
716            #
717            # For <enum> tags which are actually just constants, if there is
718            # no 'extends' tag but there is a 'value' or 'bitpos' tag, just
719            # add an EnumInfo record to the dictionary. That works because
720            # output generation of constants is purely dependency-based, and
721            # does not need to iterate through the XML tags.
722            for elem in feature.findall('require'):
723                for enum in elem.findall('enum'):
724                    addEnumInfo = False
725                    groupName = enum.get('extends')
726                    if groupName is not None:
727                        # self.gen.logMsg('diag', 'Found extension enum',
728                        #     enum.get('name'))
729                        # Add version number attribute to the <enum> element
730                        enum.set('version', featureInfo.version)
731                        # Look up the GroupInfo with matching groupName
732                        if groupName in self.groupdict:
733                            # self.gen.logMsg('diag', 'Matching group',
734                            #     groupName, 'found, adding element...')
735                            gi = self.groupdict[groupName]
736                            gi.elem.append(copy.deepcopy(enum))
737                        else:
738                            self.gen.logMsg('warn', 'NO matching group',
739                                            groupName, 'for enum', enum.get('name'), 'found.')
740                        if groupName == "VkFormat":
741                            format_name = enum.get('name')
742                            if enum.get('alias'):
743                                format_name = enum.get('alias')
744                            format_condition[format_name] = featureInfo.name
745                        addEnumInfo = True
746                    elif enum.get('value') or enum.get('bitpos') or enum.get('alias'):
747                        # self.gen.logMsg('diag', 'Adding extension constant "enum"',
748                        #     enum.get('name'))
749                        addEnumInfo = True
750                    if addEnumInfo:
751                        enumInfo = EnumInfo(enum)
752                        self.addElementInfo(enum, enumInfo, 'enum', self.enumdict)
753                        self.addEnumValue(enum, groupName)
754
755        sync_pipeline_stage_condition = dict()
756        sync_access_condition = dict()
757
758        self.extensions = self.reg.findall('extensions/extension')
759        self.extdict = {}
760        for feature in self.extensions:
761            featureInfo = FeatureInfo(feature)
762            self.addElementInfo(feature, featureInfo, 'extension', self.extdict)
763
764            # Add additional enums defined only in <extension> tags
765            # to the corresponding core type.
766            # Algorithm matches that of enums in a "feature" tag as above.
767            #
768            # This code also adds a 'extnumber' attribute containing the
769            # extension number, used for enumerant value calculation.
770            for elem in feature.findall('require'):
771                for enum in elem.findall('enum'):
772                    addEnumInfo = False
773                    groupName = enum.get('extends')
774                    if groupName is not None:
775                        # self.gen.logMsg('diag', 'Found extension enum',
776                        #     enum.get('name'))
777
778                        # Add <extension> block's extension number attribute to
779                        # the <enum> element unless specified explicitly, such
780                        # as when redefining an enum in another extension.
781                        extnumber = enum.get('extnumber')
782                        if not extnumber:
783                            enum.set('extnumber', str(featureInfo.number))
784
785                        enum.set('extname', featureInfo.name)
786                        enum.set('supported', noneStr(featureInfo.supported))
787                        # Look up the GroupInfo with matching groupName
788                        if groupName in self.groupdict:
789                            # self.gen.logMsg('diag', 'Matching group',
790                            #     groupName, 'found, adding element...')
791                            gi = self.groupdict[groupName]
792                            gi.elem.append(copy.deepcopy(enum))
793                        else:
794                            self.gen.logMsg('warn', 'NO matching group',
795                                            groupName, 'for enum', enum.get('name'), 'found.')
796                        # This is Vulkan-specific
797                        if groupName == "VkFormat":
798                            format_name = enum.get('name')
799                            if enum.get('alias'):
800                                format_name = enum.get('alias')
801                            if format_name in format_condition:
802                                format_condition[format_name] += "," + featureInfo.name
803                            else:
804                                format_condition[format_name] = featureInfo.name
805                        elif groupName == "VkPipelineStageFlagBits2":
806                            stage_flag = enum.get('name')
807                            if enum.get('alias'):
808                                stage_flag = enum.get('alias')
809                            featureName = elem.get('depends') if elem.get('depends') is not None else featureInfo.name
810                            if stage_flag in sync_pipeline_stage_condition:
811                                sync_pipeline_stage_condition[stage_flag] += "," + featureName
812                            else:
813                                sync_pipeline_stage_condition[stage_flag] = featureName
814                        elif groupName == "VkAccessFlagBits2":
815                            access_flag = enum.get('name')
816                            if enum.get('alias'):
817                                access_flag = enum.get('alias')
818                            featureName = elem.get('depends') if elem.get('depends') is not None else featureInfo.name
819                            if access_flag in sync_access_condition:
820                                sync_access_condition[access_flag] += "," + featureName
821                            else:
822                                sync_access_condition[access_flag] = featureName
823
824                        addEnumInfo = True
825                    elif enum.get('value') or enum.get('bitpos') or enum.get('alias'):
826                        # self.gen.logMsg('diag', 'Adding extension constant "enum"',
827                        #     enum.get('name'))
828                        addEnumInfo = True
829                    if addEnumInfo:
830                        enumInfo = EnumInfo(enum)
831                        self.addElementInfo(enum, enumInfo, 'enum', self.enumdict)
832                        self.addEnumValue(enum, groupName)
833
834        # Parse out all spirv tags in dictionaries
835        # Use addElementInfo to catch duplicates
836        for spirv in self.reg.findall('spirvextensions/spirvextension'):
837            spirvInfo = SpirvInfo(spirv)
838            self.addElementInfo(spirv, spirvInfo, 'spirvextension', self.spirvextdict)
839        for spirv in self.reg.findall('spirvcapabilities/spirvcapability'):
840            spirvInfo = SpirvInfo(spirv)
841            self.addElementInfo(spirv, spirvInfo, 'spirvcapability', self.spirvcapdict)
842
843        for format in self.reg.findall('formats/format'):
844            condition = None
845            format_name = format.get('name')
846            if format_name in format_condition:
847                condition = format_condition[format_name]
848            formatInfo = FormatInfo(format, condition)
849            self.addElementInfo(format, formatInfo, 'format', self.formatsdict)
850
851        for stage in self.reg.findall('sync/syncstage'):
852            condition = None
853            stage_flag = stage.get('name')
854            if stage_flag in sync_pipeline_stage_condition:
855                condition = sync_pipeline_stage_condition[stage_flag]
856            syncInfo = SyncStageInfo(stage, condition)
857            self.addElementInfo(stage, syncInfo, 'syncstage', self.syncstagedict)
858
859        for access in self.reg.findall('sync/syncaccess'):
860            condition = None
861            access_flag = access.get('name')
862            if access_flag in sync_access_condition:
863                condition = sync_access_condition[access_flag]
864            syncInfo = SyncAccessInfo(access, condition)
865            self.addElementInfo(access, syncInfo, 'syncaccess', self.syncaccessdict)
866
867        for pipeline in self.reg.findall('sync/syncpipeline'):
868            syncInfo = SyncPipelineInfo(pipeline)
869            self.addElementInfo(pipeline, syncInfo, 'syncpipeline', self.syncpipelinedict)
870
871    def dumpReg(self, maxlen=120, filehandle=sys.stdout):
872        """Dump all the dictionaries constructed from the Registry object.
873
874        Diagnostic to dump the dictionaries to specified file handle (default stdout).
875        Truncates type / enum / command elements to maxlen characters (default 120)"""
876        write('***************************************', file=filehandle)
877        write('    ** Dumping Registry contents **',     file=filehandle)
878        write('***************************************', file=filehandle)
879        write('// Types', file=filehandle)
880        for name in self.typedict:
881            tobj = self.typedict[name]
882            write('    Type', name, '->', etree.tostring(tobj.elem)[0:maxlen], file=filehandle)
883        write('// Groups', file=filehandle)
884        for name in self.groupdict:
885            gobj = self.groupdict[name]
886            write('    Group', name, '->', etree.tostring(gobj.elem)[0:maxlen], file=filehandle)
887        write('// Enums', file=filehandle)
888        for name in self.enumdict:
889            eobj = self.enumdict[name]
890            write('    Enum', name, '->', etree.tostring(eobj.elem)[0:maxlen], file=filehandle)
891        write('// Commands', file=filehandle)
892        for name in self.cmddict:
893            cobj = self.cmddict[name]
894            write('    Command', name, '->', etree.tostring(cobj.elem)[0:maxlen], file=filehandle)
895        write('// APIs', file=filehandle)
896        for key in self.apidict:
897            write('    API Version ', key, '->',
898                  etree.tostring(self.apidict[key].elem)[0:maxlen], file=filehandle)
899        write('// Extensions', file=filehandle)
900        for key in self.extdict:
901            write('    Extension', key, '->',
902                  etree.tostring(self.extdict[key].elem)[0:maxlen], file=filehandle)
903        write('// SPIR-V', file=filehandle)
904        for key in self.spirvextdict:
905            write('    SPIR-V Extension', key, '->',
906                  etree.tostring(self.spirvextdict[key].elem)[0:maxlen], file=filehandle)
907        for key in self.spirvcapdict:
908            write('    SPIR-V Capability', key, '->',
909                  etree.tostring(self.spirvcapdict[key].elem)[0:maxlen], file=filehandle)
910        write('// VkFormat', file=filehandle)
911        for key in self.formatsdict:
912            write('    VkFormat', key, '->',
913                  etree.tostring(self.formatsdict[key].elem)[0:maxlen], file=filehandle)
914
915    def markTypeRequired(self, typename, required):
916        """Require (along with its dependencies) or remove (but not its dependencies) a type.
917
918        - typename - name of type
919        - required - boolean (to tag features as required or not)
920        """
921        self.gen.logMsg('diag', 'tagging type:', typename, '-> required =', required)
922
923        # Get TypeInfo object for <type> tag corresponding to typename
924        typeinfo = self.lookupElementInfo(typename, self.typedict)
925        if typeinfo is not None:
926            if required:
927                # Tag type dependencies in 'alias' and 'required' attributes as
928                # required. This does not un-tag dependencies in a <remove>
929                # tag. See comments in markRequired() below for the reason.
930                for attrib_name in ['requires', 'alias']:
931                    depname = typeinfo.elem.get(attrib_name)
932                    if depname:
933                        self.gen.logMsg('diag', 'Generating dependent type',
934                                        depname, 'for', attrib_name, 'type', typename)
935                        # Do not recurse on self-referential structures.
936                        if typename != depname:
937                            self.markTypeRequired(depname, required)
938                        else:
939                            self.gen.logMsg('diag', 'type', typename, 'is self-referential')
940                # Tag types used in defining this type (e.g. in nested
941                # <type> tags)
942                # Look for <type> in entire <command> tree,
943                # not just immediate children
944                for subtype in typeinfo.elem.findall('.//type'):
945                    self.gen.logMsg('diag', 'markRequired: type requires dependent <type>', subtype.text)
946                    if typename != subtype.text:
947                        self.markTypeRequired(subtype.text, required)
948                    else:
949                        self.gen.logMsg('diag', 'type', typename, 'is self-referential')
950                # Tag enums used in defining this type, for example in
951                #   <member><name>member</name>[<enum>MEMBER_SIZE</enum>]</member>
952                for subenum in typeinfo.elem.findall('.//enum'):
953                    self.gen.logMsg('diag', 'markRequired: type requires dependent <enum>', subenum.text)
954                    self.markEnumRequired(subenum.text, required)
955                # Tag type dependency in 'bitvalues' attributes as
956                # required. This ensures that the bit values for a flag
957                # are emitted
958                depType = typeinfo.elem.get('bitvalues')
959                if depType:
960                    self.gen.logMsg('diag', 'Generating bitflag type',
961                                    depType, 'for type', typename)
962                    self.markTypeRequired(depType, required)
963                    group = self.lookupElementInfo(depType, self.groupdict)
964                    if group is not None:
965                        group.flagType = typeinfo
966
967            typeinfo.required = required
968        elif '.h' not in typename:
969            self.gen.logMsg('warn', 'type:', typename, 'IS NOT DEFINED')
970
971    def markEnumRequired(self, enumname, required):
972        """Mark an enum as required or not.
973
974        - enumname - name of enum
975        - required - boolean (to tag features as required or not)"""
976
977        self.gen.logMsg('diag', 'markEnumRequired: tagging enum:', enumname, '-> required =', required)
978        enum = self.lookupElementInfo(enumname, self.enumdict)
979        if enum is not None:
980            # If the enum is part of a group, and is being removed, then
981            # look it up in that <enums> tag and remove the Element there,
982            # so that it is not visible to generators (which traverse the
983            # <enums> tag elements rather than using the dictionaries).
984            if not required:
985                groupName = enum.elem.get('extends')
986                if groupName is not None:
987                    self.gen.logMsg('diag', f'markEnumRequired: Removing extending enum {enum.elem.get("name")}')
988
989                    # Look up the Info with matching groupName
990                    if groupName in self.groupdict:
991                        gi = self.groupdict[groupName]
992                        gienum = gi.elem.find("enum[@name='" + enumname + "']")
993                        if gienum is not None:
994                            # Remove copy of this enum from the group
995                            gi.elem.remove(gienum)
996                        else:
997                            self.gen.logMsg('warn', 'markEnumRequired: Cannot remove enum',
998                                            enumname, 'not found in group',
999                                            groupName)
1000                    else:
1001                        self.gen.logMsg('warn', 'markEnumRequired: Cannot remove enum',
1002                                        enumname, 'from nonexistent group',
1003                                        groupName)
1004                else:
1005                    # This enum is not an extending enum.
1006                    # The XML tree must be searched for all <enums> that
1007                    # might have it, so we know the parent to delete from.
1008
1009                    enumName = enum.elem.get('name')
1010
1011                    self.gen.logMsg('diag', f'markEnumRequired: Removing non-extending enum {enumName}')
1012
1013                    count = 0
1014                    for enums in self.reg.findall('enums'):
1015                        for thisEnum in enums.findall('enum'):
1016                            if thisEnum.get('name') == enumName:
1017                                # Actually remove it
1018                                count = count + 1
1019                                enums.remove(thisEnum)
1020
1021                    if count == 0:
1022                        self.gen.logMsg('warn', f'markEnumRequired: {enumName}) not found in any <enums> tag')
1023
1024            enum.required = required
1025            # Tag enum dependencies in 'alias' attribute as required
1026            depname = enum.elem.get('alias')
1027            if depname:
1028                self.gen.logMsg('diag', 'markEnumRequired: Generating dependent enum',
1029                                depname, 'for alias', enumname, 'required =', enum.required)
1030                self.markEnumRequired(depname, required)
1031        else:
1032            self.gen.logMsg('warn', f'markEnumRequired: {enumname} IS NOT DEFINED')
1033
1034    def markCmdRequired(self, cmdname, required):
1035        """Mark a command as required or not.
1036
1037        - cmdname - name of command
1038        - required - boolean (to tag features as required or not)"""
1039        self.gen.logMsg('diag', 'tagging command:', cmdname, '-> required =', required)
1040        cmd = self.lookupElementInfo(cmdname, self.cmddict)
1041        if cmd is not None:
1042            cmd.required = required
1043
1044            # Tag command dependencies in 'alias' attribute as required
1045            #
1046            # This is usually not done, because command 'aliases' are not
1047            # actual C language aliases like type and enum aliases. Instead
1048            # they are just duplicates of the function signature of the
1049            # alias. This means that there is no dependency of a command
1050            # alias on what it aliases. One exception is validity includes,
1051            # where the spec markup needs the promoted-to validity include
1052            # even if only the promoted-from command is being built.
1053            if self.genOpts.requireCommandAliases:
1054                depname = cmd.elem.get('alias')
1055                if depname:
1056                    self.gen.logMsg('diag', 'Generating dependent command',
1057                                    depname, 'for alias', cmdname)
1058                    self.markCmdRequired(depname, required)
1059
1060            # Tag all parameter types of this command as required.
1061            # This does not remove types of commands in a <remove>
1062            # tag, because many other commands may use the same type.
1063            # We could be more clever and reference count types,
1064            # instead of using a boolean.
1065            if required:
1066                # Look for <type> in entire <command> tree,
1067                # not just immediate children
1068                for type_elem in cmd.elem.findall('.//type'):
1069                    self.gen.logMsg('diag', 'markRequired: command implicitly requires dependent type', type_elem.text)
1070                    self.markTypeRequired(type_elem.text, required)
1071        else:
1072            self.gen.logMsg('warn', 'command:', cmdname, 'IS NOT DEFINED')
1073
1074    def markRequired(self, featurename, feature, required):
1075        """Require or remove features specified in the Element.
1076
1077        - featurename - name of the feature
1078        - feature - Element for `<require>` or `<remove>` tag
1079        - required - boolean (to tag features as required or not)"""
1080        self.gen.logMsg('diag', 'markRequired (feature = <too long to print>, required =', required, ')')
1081
1082        # Loop over types, enums, and commands in the tag
1083        # @@ It would be possible to respect 'api' and 'profile' attributes
1084        #  in individual features, but that is not done yet.
1085        for typeElem in feature.findall('type'):
1086            self.markTypeRequired(typeElem.get('name'), required)
1087        for enumElem in feature.findall('enum'):
1088            self.markEnumRequired(enumElem.get('name'), required)
1089
1090        for cmdElem in feature.findall('command'):
1091            self.markCmdRequired(cmdElem.get('name'), required)
1092
1093        # Extensions may need to extend existing commands or other items in the future.
1094        # So, look for extend tags.
1095        for extendElem in feature.findall('extend'):
1096            extendType = extendElem.get('type')
1097            if extendType == 'command':
1098                commandName = extendElem.get('name')
1099                successExtends = extendElem.get('successcodes')
1100                if successExtends is not None:
1101                    for success in successExtends.split(','):
1102                        self.commandextensionsuccesses.append(self.commandextensiontuple(command=commandName,
1103                                                                                         value=success,
1104                                                                                         extension=featurename))
1105                errorExtends = extendElem.get('errorcodes')
1106                if errorExtends is not None:
1107                    for error in errorExtends.split(','):
1108                        self.commandextensionerrors.append(self.commandextensiontuple(command=commandName,
1109                                                                                      value=error,
1110                                                                                      extension=featurename))
1111            else:
1112                self.gen.logMsg('warn', 'extend type:', extendType, 'IS NOT SUPPORTED')
1113
1114    def getAlias(self, elem, dict):
1115        """Check for an alias in the same require block.
1116
1117        - elem - Element to check for an alias"""
1118
1119        # Try to find an alias
1120        alias = elem.get('alias')
1121        if alias is None:
1122            name = elem.get('name')
1123            typeinfo = self.lookupElementInfo(name, dict)
1124            if not typeinfo:
1125                self.gen.logMsg('error', name, 'is not a known name')
1126            alias = typeinfo.elem.get('alias')
1127
1128        return alias
1129
1130    def checkForCorrectionAliases(self, alias, require, tag):
1131        """Check for an alias in the same require block.
1132
1133        - alias - String name of the alias
1134        - require -  `<require>` block from the registry
1135        - tag - tag to look for in the require block"""
1136
1137        # For the time being, the code below is bypassed. It has the effect
1138        # of excluding "spelling aliases" created to comply with the style
1139        # guide, but this leaves references out of the specification and
1140        # causes broken internal links.
1141        #
1142        # if alias and require.findall(tag + "[@name='" + alias + "']"):
1143        #     return True
1144
1145        return False
1146
1147    def fillFeatureDictionary(self, interface, featurename, api, profile):
1148        """Capture added interfaces for a `<version>` or `<extension>`.
1149
1150        - interface - Element for `<version>` or `<extension>`, containing
1151          `<require>` and `<remove>` tags
1152        - featurename - name of the feature
1153        - api - string specifying API name being generated
1154        - profile - string specifying API profile being generated"""
1155
1156        # Explicitly initialize known types - errors for unhandled categories
1157        self.gen.featureDictionary[featurename] = {
1158            "enumconstant": {},
1159            "command": {},
1160            "enum": {},
1161            "struct": {},
1162            "handle": {},
1163            "basetype": {},
1164            "include": {},
1165            "define": {},
1166            "bitmask": {},
1167            "union": {},
1168            "funcpointer": {},
1169        }
1170
1171        # <require> marks things that are required by this version/profile
1172        for require in interface.findall('require'):
1173            if matchAPIProfile(api, profile, require):
1174
1175                # Determine the required extension or version needed for a require block
1176                # Assumes that only one of these is specified
1177                # 'extension', and therefore 'required_key', may be a boolean
1178                # expression of extension names.
1179                # 'required_key' is used only as a dictionary key at
1180                # present, and passed through to the script generators, so
1181                # they must be prepared to parse that boolean expression.
1182                required_key = require.get('depends')
1183
1184                # Loop over types, enums, and commands in the tag
1185                for typeElem in require.findall('type'):
1186                    typename = typeElem.get('name')
1187                    typeinfo = self.lookupElementInfo(typename, self.typedict)
1188
1189                    if typeinfo:
1190                        # Remove aliases in the same extension/feature; these are always added as a correction. Do not need the original to be visible.
1191                        alias = self.getAlias(typeElem, self.typedict)
1192                        if not self.checkForCorrectionAliases(alias, require, 'type'):
1193                            # Resolve the type info to the actual type, so we get an accurate read for 'structextends'
1194                            while alias:
1195                                typeinfo = self.lookupElementInfo(alias, self.typedict)
1196                                if not typeinfo:
1197                                    raise RuntimeError(f"Missing alias {alias}")
1198                                alias = typeinfo.elem.get('alias')
1199
1200                            typecat = typeinfo.elem.get('category')
1201                            typeextends = typeinfo.elem.get('structextends')
1202                            if not required_key in self.gen.featureDictionary[featurename][typecat]:
1203                                self.gen.featureDictionary[featurename][typecat][required_key] = {}
1204                            if not typeextends in self.gen.featureDictionary[featurename][typecat][required_key]:
1205                                self.gen.featureDictionary[featurename][typecat][required_key][typeextends] = []
1206                            self.gen.featureDictionary[featurename][typecat][required_key][typeextends].append(typename)
1207                        else:
1208                            self.gen.logMsg('warn', 'fillFeatureDictionary: NOT filling for {}'.format(typename))
1209
1210
1211                for enumElem in require.findall('enum'):
1212                    enumname = enumElem.get('name')
1213                    typeinfo = self.lookupElementInfo(enumname, self.enumdict)
1214
1215                    # Remove aliases in the same extension/feature; these are always added as a correction. Do not need the original to be visible.
1216                    alias = self.getAlias(enumElem, self.enumdict)
1217                    if not self.checkForCorrectionAliases(alias, require, 'enum'):
1218                        enumextends = enumElem.get('extends')
1219                        if not required_key in self.gen.featureDictionary[featurename]['enumconstant']:
1220                            self.gen.featureDictionary[featurename]['enumconstant'][required_key] = {}
1221                        if not enumextends in self.gen.featureDictionary[featurename]['enumconstant'][required_key]:
1222                            self.gen.featureDictionary[featurename]['enumconstant'][required_key][enumextends] = []
1223                        self.gen.featureDictionary[featurename]['enumconstant'][required_key][enumextends].append(enumname)
1224                    else:
1225                        self.gen.logMsg('warn', 'fillFeatureDictionary: NOT filling for {}'.format(typename))
1226
1227                for cmdElem in require.findall('command'):
1228                    # Remove aliases in the same extension/feature; these are always added as a correction. Do not need the original to be visible.
1229                    alias = self.getAlias(cmdElem, self.cmddict)
1230                    if not self.checkForCorrectionAliases(alias, require, 'command'):
1231                        if not required_key in self.gen.featureDictionary[featurename]['command']:
1232                            self.gen.featureDictionary[featurename]['command'][required_key] = []
1233                        self.gen.featureDictionary[featurename]['command'][required_key].append(cmdElem.get('name'))
1234                    else:
1235                        self.gen.logMsg('warn', 'fillFeatureDictionary: NOT filling for {}'.format(typename))
1236
1237    def requireFeatures(self, interface, featurename, api, profile):
1238        """Process `<require>` tags for a `<version>` or `<extension>`.
1239
1240        - interface - Element for `<version>` or `<extension>`, containing
1241          `<require>` tags
1242        - featurename - name of the feature
1243        - api - string specifying API name being generated
1244        - profile - string specifying API profile being generated"""
1245
1246        # <require> marks things that are required by this version/profile
1247        for feature in interface.findall('require'):
1248            if matchAPIProfile(api, profile, feature):
1249                self.markRequired(featurename, feature, True)
1250
1251    def removeFeatures(self, interface, featurename, api, profile):
1252        """Process `<remove>` tags for a `<version>` or `<extension>`.
1253
1254        - interface - Element for `<version>` or `<extension>`, containing
1255          `<remove>` tags
1256        - featurename - name of the feature
1257        - api - string specifying API name being generated
1258        - profile - string specifying API profile being generated"""
1259
1260        # <remove> marks things that are removed by this version/profile
1261        for feature in interface.findall('remove'):
1262            if matchAPIProfile(api, profile, feature):
1263                self.markRequired(featurename, feature, False)
1264
1265    def assignAdditionalValidity(self, interface, api, profile):
1266        # Loop over all usage inside all <require> tags.
1267        for feature in interface.findall('require'):
1268            if matchAPIProfile(api, profile, feature):
1269                for v in feature.findall('usage'):
1270                    if v.get('command'):
1271                        self.cmddict[v.get('command')].additionalValidity.append(copy.deepcopy(v))
1272                    if v.get('struct'):
1273                        self.typedict[v.get('struct')].additionalValidity.append(copy.deepcopy(v))
1274
1275    def removeAdditionalValidity(self, interface, api, profile):
1276        # Loop over all usage inside all <remove> tags.
1277        for feature in interface.findall('remove'):
1278            if matchAPIProfile(api, profile, feature):
1279                for v in feature.findall('usage'):
1280                    if v.get('command'):
1281                        self.cmddict[v.get('command')].removedValidity.append(copy.deepcopy(v))
1282                    if v.get('struct'):
1283                        self.typedict[v.get('struct')].removedValidity.append(copy.deepcopy(v))
1284
1285    def generateFeature(self, fname, ftype, dictionary, explicit=False):
1286        """Generate a single type / enum group / enum / command,
1287        and all its dependencies as needed.
1288
1289        - fname - name of feature (`<type>`/`<enum>`/`<command>`)
1290        - ftype - type of feature, 'type' | 'enum' | 'command'
1291        - dictionary - of *Info objects - self.{type|enum|cmd}dict
1292        - explicit - True if this is explicitly required by the top-level
1293          XML <require> tag, False if it is a dependency of an explicit
1294          requirement."""
1295
1296        self.gen.logMsg('diag', 'generateFeature: generating', ftype, fname)
1297
1298        if not (explicit or self.genOpts.requireDepends):
1299            self.gen.logMsg('diag', 'generateFeature: NOT generating', ftype, fname, 'because generator does not require dependencies')
1300            return
1301
1302        f = self.lookupElementInfo(fname, dictionary)
1303        if f is None:
1304            # No such feature. This is an error, but reported earlier
1305            self.gen.logMsg('diag', 'No entry found for feature', fname,
1306                            'returning!')
1307            return
1308
1309        # If feature is not required, or has already been declared, return
1310        if not f.required:
1311            self.gen.logMsg('diag', 'Skipping', ftype, fname, '(not required)')
1312            return
1313        if f.declared:
1314            self.gen.logMsg('diag', 'Skipping', ftype, fname, '(already declared)')
1315            return
1316        # Always mark feature declared, as though actually emitted
1317        f.declared = True
1318
1319        # Determine if this is an alias, and of what, if so
1320        alias = f.elem.get('alias')
1321        if alias:
1322            self.gen.logMsg('diag', fname, 'is an alias of', alias)
1323
1324        # Pull in dependent declaration(s) of the feature.
1325        # For types, there may be one type in the 'requires' attribute of
1326        #   the element, one in the 'alias' attribute, and many in
1327        #   embedded <type> and <enum> tags within the element.
1328        # For commands, there may be many in <type> tags within the element.
1329        # For enums, no dependencies are allowed (though perhaps if you
1330        #   have a uint64 enum, it should require that type).
1331        genProc = None
1332        followupFeature = None
1333        if ftype == 'type':
1334            genProc = self.gen.genType
1335
1336            # Generate type dependencies in 'alias' and 'requires' attributes
1337            if alias:
1338                self.generateFeature(alias, 'type', self.typedict)
1339            requires = f.elem.get('requires')
1340            if requires:
1341                self.gen.logMsg('diag', 'Generating required dependent type',
1342                                requires)
1343                self.generateFeature(requires, 'type', self.typedict)
1344
1345            # Generate types used in defining this type (e.g. in nested
1346            # <type> tags)
1347            # Look for <type> in entire <command> tree,
1348            # not just immediate children
1349            for subtype in f.elem.findall('.//type'):
1350                self.gen.logMsg('diag', 'Generating required dependent <type>',
1351                                subtype.text)
1352                self.generateFeature(subtype.text, 'type', self.typedict)
1353
1354            # Generate enums used in defining this type, for example in
1355            #   <member><name>member</name>[<enum>MEMBER_SIZE</enum>]</member>
1356            for subtype in f.elem.findall('.//enum'):
1357                self.gen.logMsg('diag', 'Generating required dependent <enum>',
1358                                subtype.text)
1359                self.generateFeature(subtype.text, 'enum', self.enumdict)
1360
1361            # If the type is an enum group, look up the corresponding
1362            # group in the group dictionary and generate that instead.
1363            if f.elem.get('category') == 'enum':
1364                self.gen.logMsg('diag', 'Type', fname, 'is an enum group, so generate that instead')
1365                group = self.lookupElementInfo(fname, self.groupdict)
1366                if alias is not None:
1367                    # An alias of another group name.
1368                    # Pass to genGroup with 'alias' parameter = aliased name
1369                    self.gen.logMsg('diag', 'Generating alias', fname,
1370                                    'for enumerated type', alias)
1371                    # Now, pass the *aliased* GroupInfo to the genGroup, but
1372                    # with an additional parameter which is the alias name.
1373                    genProc = self.gen.genGroup
1374                    f = self.lookupElementInfo(alias, self.groupdict)
1375                elif group is None:
1376                    self.gen.logMsg('warn', 'Skipping enum type', fname,
1377                                    ': No matching enumerant group')
1378                    return
1379                else:
1380                    genProc = self.gen.genGroup
1381                    f = group
1382
1383                    # @ The enum group is not ready for generation. At this
1384                    # @   point, it contains all <enum> tags injected by
1385                    # @   <extension> tags without any verification of whether
1386                    # @   they are required or not. It may also contain
1387                    # @   duplicates injected by multiple consistent
1388                    # @   definitions of an <enum>.
1389
1390                    # @ Pass over each enum, marking its enumdict[] entry as
1391                    # @ required or not. Mark aliases of enums as required,
1392                    # @ too.
1393
1394                    enums = group.elem.findall('enum')
1395
1396                    self.gen.logMsg('diag', 'generateFeature: checking enums for group', fname)
1397
1398                    # Check for required enums, including aliases
1399                    # LATER - Check for, report, and remove duplicates?
1400                    enumAliases = []
1401                    for elem in enums:
1402                        name = elem.get('name')
1403
1404                        required = False
1405
1406                        extname = elem.get('extname')
1407                        version = elem.get('version')
1408                        if extname is not None:
1409                            # 'supported' attribute was injected when the <enum> element was
1410                            # moved into the <enums> group in Registry.parseTree()
1411                            supported_list = elem.get('supported').split(",")
1412                            if self.genOpts.defaultExtensions in supported_list:
1413                                required = True
1414                            elif re.match(self.genOpts.addExtensions, extname) is not None:
1415                                required = True
1416                        elif version is not None:
1417                            required = re.match(self.genOpts.emitversions, version) is not None
1418                        else:
1419                            required = True
1420
1421                        self.gen.logMsg('diag', '* required =', required, 'for', name)
1422                        if required:
1423                            # Mark this element as required (in the element, not the EnumInfo)
1424                            elem.set('required', 'true')
1425                            # If it is an alias, track that for later use
1426                            enumAlias = elem.get('alias')
1427                            if enumAlias:
1428                                enumAliases.append(enumAlias)
1429                    for elem in enums:
1430                        name = elem.get('name')
1431                        if name in enumAliases:
1432                            elem.set('required', 'true')
1433                            self.gen.logMsg('diag', '* also need to require alias', name)
1434            if f is None:
1435                raise RuntimeError("Should not get here")
1436            if f.elem.get('category') == 'bitmask':
1437                followupFeature = f.elem.get('bitvalues')
1438        elif ftype == 'command':
1439            # Generate command dependencies in 'alias' attribute
1440            if alias:
1441                self.generateFeature(alias, 'command', self.cmddict)
1442
1443            genProc = self.gen.genCmd
1444            for type_elem in f.elem.findall('.//type'):
1445                depname = type_elem.text
1446                self.gen.logMsg('diag', 'Generating required parameter type',
1447                                depname)
1448                self.generateFeature(depname, 'type', self.typedict)
1449        elif ftype == 'enum':
1450            # Generate enum dependencies in 'alias' attribute
1451            if alias:
1452                self.generateFeature(alias, 'enum', self.enumdict)
1453            genProc = self.gen.genEnum
1454
1455        # Actually generate the type only if emitting declarations
1456        if self.emitFeatures:
1457            self.gen.logMsg('diag', 'Emitting', ftype, 'decl for', fname)
1458            if genProc is None:
1459                raise RuntimeError("genProc is None when we should be emitting")
1460            genProc(f, fname, alias)
1461        else:
1462            self.gen.logMsg('diag', 'Skipping', ftype, fname,
1463                            '(should not be emitted)')
1464
1465        if followupFeature:
1466            self.gen.logMsg('diag', 'Generating required bitvalues <enum>',
1467                            followupFeature)
1468            self.generateFeature(followupFeature, "type", self.typedict)
1469
1470    def generateRequiredInterface(self, interface):
1471        """Generate all interfaces required by an API version or extension.
1472
1473        - interface - Element for `<version>` or `<extension>`"""
1474
1475        # Loop over all features inside all <require> tags.
1476        for features in interface.findall('require'):
1477            for t in features.findall('type'):
1478                self.generateFeature(t.get('name'), 'type', self.typedict, explicit=True)
1479            for e in features.findall('enum'):
1480                # If this is an enum extending an enumerated type, do not
1481                # generate it - this has already been done in reg.parseTree,
1482                # by copying this element into the enumerated type.
1483                enumextends = e.get('extends')
1484                if not enumextends:
1485                    self.generateFeature(e.get('name'), 'enum', self.enumdict, explicit=True)
1486            for c in features.findall('command'):
1487                self.generateFeature(c.get('name'), 'command', self.cmddict, explicit=True)
1488
1489    def generateSpirv(self, spirv, dictionary):
1490        if spirv is None:
1491            self.gen.logMsg('diag', 'No entry found for element', name,
1492                            'returning!')
1493            return
1494
1495        name = spirv.elem.get('name')
1496        # No known alias for spirv elements
1497        alias = None
1498        if spirv.emit:
1499            genProc = self.gen.genSpirv
1500            genProc(spirv, name, alias)
1501
1502    def stripUnsupportedAPIs(self, dictionary, attribute, supportedDictionary):
1503        """Strip unsupported APIs from attributes of APIs.
1504           dictionary - *Info dictionary of APIs to be updated
1505           attribute - attribute name to look for in each API
1506           supportedDictionary - dictionary in which to look for supported
1507            API elements in the attribute"""
1508
1509        for key in dictionary:
1510            eleminfo = dictionary[key]
1511            attribstring = eleminfo.elem.get(attribute)
1512            if attribstring is not None:
1513                apis = []
1514                stripped = False
1515                for api in attribstring.split(','):
1516                    ##print('Checking API {} referenced by {}'.format(api, key))
1517                    if api in supportedDictionary and supportedDictionary[api].required:
1518                        apis.append(api)
1519                    else:
1520                        stripped = True
1521                        ##print('\t**STRIPPING API {} from {}'.format(api, key))
1522
1523                # Update the attribute after stripping stuff.
1524                # Could sort apis before joining, but it is not a clear win
1525                if stripped:
1526                    eleminfo.elem.set(attribute, ','.join(apis))
1527
1528    def stripUnsupportedAPIsFromList(self, dictionary, supportedDictionary):
1529        """Strip unsupported APIs from attributes of APIs.
1530           dictionary - dictionary of list of structure name strings
1531           supportedDictionary - dictionary in which to look for supported
1532            API elements in the attribute"""
1533
1534        for key in dictionary:
1535            attribstring = dictionary[key]
1536            if attribstring is not None:
1537                apis = []
1538                stripped = False
1539                for api in attribstring:
1540                    ##print('Checking API {} referenced by {}'.format(api, key))
1541                    if supportedDictionary[api].required:
1542                        apis.append(api)
1543                    else:
1544                        stripped = True
1545                        ##print('\t**STRIPPING API {} from {}'.format(api, key))
1546
1547                # Update the attribute after stripping stuff.
1548                # Could sort apis before joining, but it is not a clear win
1549                if stripped:
1550                    dictionary[key] = apis
1551
1552    def generateFormat(self, format, dictionary):
1553        if format is None:
1554            self.gen.logMsg('diag', 'No entry found for format element',
1555                            'returning!')
1556            return
1557
1558        name = format.elem.get('name')
1559        # No known alias for VkFormat elements
1560        alias = None
1561        if format.emit:
1562            genProc = self.gen.genFormat
1563            genProc(format, name, alias)
1564
1565    def generateSyncStage(self, sync):
1566        genProc = self.gen.genSyncStage
1567        genProc(sync)
1568
1569    def generateSyncAccess(self, sync):
1570        genProc = self.gen.genSyncAccess
1571        genProc(sync)
1572
1573    def generateSyncPipeline(self, sync):
1574        genProc = self.gen.genSyncPipeline
1575        genProc(sync)
1576
1577    def tagValidExtensionStructs(self):
1578        """Construct a "validextensionstructs" list for parent structures
1579           based on "structextends" tags in child structures.
1580           Only do this for structures tagged as required."""
1581
1582        for typeinfo in self.typedict.values():
1583            type_elem = typeinfo.elem
1584            if typeinfo.required and type_elem.get('category') == 'struct':
1585                struct_extends = type_elem.get('structextends')
1586                if struct_extends is not None:
1587                    for parent in struct_extends.split(','):
1588                        # self.gen.logMsg('diag', type_elem.get('name'), 'extends', parent)
1589                        self.validextensionstructs[parent].append(type_elem.get('name'))
1590
1591        # Sort the lists so they do not depend on the XML order
1592        for parent in self.validextensionstructs:
1593            self.validextensionstructs[parent].sort()
1594
1595    def apiGen(self):
1596        """Generate interface for specified versions using the current
1597        generator and generator options"""
1598
1599        self.gen.logMsg('diag', '*******************************************')
1600        self.gen.logMsg('diag', '  Registry.apiGen file:', self.genOpts.filename,
1601                        'api:', self.genOpts.apiname,
1602                        'profile:', self.genOpts.profile)
1603        self.gen.logMsg('diag', '*******************************************')
1604
1605        # Could reset required/declared flags for all features here.
1606        # This has been removed as never used. The initial motivation was
1607        # the idea of calling apiGen() repeatedly for different targets, but
1608        # this has never been done. The 20% or so build-time speedup that
1609        # might result is not worth the effort to make it actually work.
1610        #
1611        # self.apiReset()
1612
1613        # Compile regexps used to select versions & extensions
1614        regVersions = re.compile(self.genOpts.versions)
1615        regEmitVersions = re.compile(self.genOpts.emitversions)
1616        regAddExtensions = re.compile(self.genOpts.addExtensions)
1617        regRemoveExtensions = re.compile(self.genOpts.removeExtensions)
1618        regEmitExtensions = re.compile(self.genOpts.emitExtensions)
1619        regEmitSpirv = re.compile(self.genOpts.emitSpirv)
1620        regEmitFormats = re.compile(self.genOpts.emitFormats)
1621
1622        # Get all matching API feature names & add to list of FeatureInfo
1623        # Note we used to select on feature version attributes, not names.
1624        features = []
1625        apiMatch = False
1626        for key in self.apidict:
1627            fi = self.apidict[key]
1628            api = fi.elem.get('api')
1629            if apiNameMatch(self.genOpts.apiname, api):
1630                apiMatch = True
1631                if regVersions.match(fi.name):
1632                    # Matches API & version #s being generated. Mark for
1633                    # emission and add to the features[] list .
1634                    # @@ Could use 'declared' instead of 'emit'?
1635                    fi.emit = (regEmitVersions.match(fi.name) is not None)
1636                    features.append(fi)
1637                    if not fi.emit:
1638                        self.gen.logMsg('diag', 'NOT tagging feature api =', api,
1639                                        'name =', fi.name, 'version =', fi.version,
1640                                        'for emission (does not match emitversions pattern)')
1641                    else:
1642                        self.gen.logMsg('diag', 'Including feature api =', api,
1643                                        'name =', fi.name, 'version =', fi.version,
1644                                        'for emission (matches emitversions pattern)')
1645                else:
1646                    self.gen.logMsg('diag', 'NOT including feature api =', api,
1647                                    'name =', fi.name, 'version =', fi.version,
1648                                    '(does not match requested versions)')
1649            else:
1650                self.gen.logMsg('diag', 'NOT including feature api =', api,
1651                                'name =', fi.name,
1652                                '(does not match requested API)')
1653        if not apiMatch:
1654            self.gen.logMsg('warn', 'No matching API versions found!')
1655
1656        # Get all matching extensions, in order by their extension number,
1657        # and add to the list of features.
1658        # Start with extensions whose 'supported' attributes match the API
1659        # being generated. Add extensions matching the pattern specified in
1660        # regExtensions, then remove extensions matching the pattern
1661        # specified in regRemoveExtensions
1662        for (extName, ei) in sorted(self.extdict.items(), key=lambda x: x[1].number if x[1].number is not None else '0'):
1663            extName = ei.name
1664            include = False
1665
1666            # Include extension if defaultExtensions is not None and is
1667            # exactly matched by the 'supported' attribute.
1668            if apiNameMatch(self.genOpts.defaultExtensions,
1669                            ei.elem.get('supported')):
1670                self.gen.logMsg('diag', 'Including extension',
1671                                extName, "(defaultExtensions matches the 'supported' attribute)")
1672                include = True
1673
1674            # Include additional extensions if the extension name matches
1675            # the regexp specified in the generator options. This allows
1676            # forcing extensions into an interface even if they are not
1677            # tagged appropriately in the registry.
1678            # However, we still respect the 'supported' attribute.
1679            if regAddExtensions.match(extName) is not None:
1680                if not apiNameMatch(self.genOpts.apiname, ei.elem.get('supported')):
1681                    self.gen.logMsg('diag', 'NOT including extension',
1682                                    extName, '(matches explicitly requested, but does not match the \'supported\' attribute)')
1683                    include = False
1684                else:
1685                    self.gen.logMsg('diag', 'Including extension',
1686                                    extName, '(matches explicitly requested extensions to add)')
1687                    include = True
1688            # Remove extensions if the name matches the regexp specified
1689            # in generator options. This allows forcing removal of
1690            # extensions from an interface even if they are tagged that
1691            # way in the registry.
1692            if regRemoveExtensions.match(extName) is not None:
1693                self.gen.logMsg('diag', 'Removing extension',
1694                                extName, '(matches explicitly requested extensions to remove)')
1695                include = False
1696
1697            # If the extension is to be included, add it to the
1698            # extension features list.
1699            if include:
1700                ei.emit = (regEmitExtensions.match(extName) is not None)
1701                features.append(ei)
1702                if not ei.emit:
1703                    self.gen.logMsg('diag', 'NOT tagging extension',
1704                                    extName,
1705                                    'for emission (does not match emitextensions pattern)')
1706
1707                # Hack - can be removed when validity generator goes away
1708                # (Jon) I am not sure what this does, or if it should
1709                # respect the ei.emit flag above.
1710                self.requiredextensions.append(extName)
1711            else:
1712                self.gen.logMsg('diag', 'NOT including extension',
1713                                extName, '(does not match api attribute or explicitly requested extensions)')
1714
1715        # Add all spirv elements to list
1716        # generators decide to emit them all or not
1717        # Currently no filtering as no client of these elements needs filtering
1718        spirvexts = []
1719        for key in self.spirvextdict:
1720            si = self.spirvextdict[key]
1721            si.emit = (regEmitSpirv.match(key) is not None)
1722            spirvexts.append(si)
1723        spirvcaps = []
1724        for key in self.spirvcapdict:
1725            si = self.spirvcapdict[key]
1726            si.emit = (regEmitSpirv.match(key) is not None)
1727            spirvcaps.append(si)
1728
1729        formats = []
1730        for key in self.formatsdict:
1731            si = self.formatsdict[key]
1732            si.emit = (regEmitFormats.match(key) is not None)
1733            formats.append(si)
1734
1735        # Sort the features list, if a sort procedure is defined
1736        if self.genOpts.sortProcedure:
1737            self.genOpts.sortProcedure(features)
1738
1739        # Passes 1+2: loop over requested API versions and extensions tagging
1740        #   types/commands/features as required (in an <require> block) or no
1741        #   longer required (in an <remove> block). <remove>s are processed
1742        #   after all <require>s, so removals win.
1743        # If a profile other than 'None' is being generated, it must
1744        #   match the profile attribute (if any) of the <require> and
1745        #   <remove> tags.
1746        self.gen.logMsg('diag', 'PASS 1: TAG FEATURES')
1747        for f in features:
1748            self.gen.logMsg('diag', 'PASS 1: Tagging required and features for', f.name)
1749            self.fillFeatureDictionary(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile)
1750            self.requireFeatures(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile)
1751            self.assignAdditionalValidity(f.elem, self.genOpts.apiname, self.genOpts.profile)
1752
1753        for f in features:
1754            self.gen.logMsg('diag', 'PASS 2: Tagging removed features for', f.name)
1755            self.removeFeatures(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile)
1756            self.removeAdditionalValidity(f.elem, self.genOpts.apiname, self.genOpts.profile)
1757
1758        # Now, strip references to APIs that are not required.
1759        # At present such references may occur in:
1760        #   Structs in <type category="struct"> 'structextends' attributes
1761        #   Enums in <command> 'successcodes' and 'errorcodes' attributes
1762        self.stripUnsupportedAPIs(self.typedict, 'structextends', self.typedict)
1763        self.stripUnsupportedAPIs(self.cmddict, 'successcodes', self.enumdict)
1764        self.stripUnsupportedAPIs(self.cmddict, 'errorcodes', self.enumdict)
1765        self.stripUnsupportedAPIsFromList(self.validextensionstructs, self.typedict)
1766
1767        # Construct lists of valid extension structures
1768        self.tagValidExtensionStructs()
1769
1770        # @@May need to strip <spirvcapability> / <spirvextension> <enable>
1771        # tags of these forms:
1772        #   <enable version="VK_API_VERSION_1_0"/>
1773        #   <enable struct="VkPhysicalDeviceFeatures" feature="geometryShader" requires="VK_VERSION_1_0"/>
1774        #   <enable extension="VK_KHR_shader_draw_parameters"/>
1775        #   <enable property="VkPhysicalDeviceVulkan12Properties" member="shaderDenormPreserveFloat16" value="VK_TRUE" requires="VK_VERSION_1_2,VK_KHR_shader_float_controls"/>
1776
1777        # Pass 3: loop over specified API versions and extensions printing
1778        #   declarations for required things which have not already been
1779        #   generated.
1780        self.gen.logMsg('diag', 'PASS 3: GENERATE INTERFACES FOR FEATURES')
1781        self.gen.beginFile(self.genOpts)
1782        for f in features:
1783            self.gen.logMsg('diag', 'PASS 3: Generating interface for',
1784                            f.name)
1785            emit = self.emitFeatures = f.emit
1786            if not emit:
1787                self.gen.logMsg('diag', 'PASS 3: NOT declaring feature',
1788                                f.elem.get('name'), 'because it is not tagged for emission')
1789            # Generate the interface (or just tag its elements as having been
1790            # emitted, if they have not been).
1791            self.gen.beginFeature(f.elem, emit)
1792            self.generateRequiredInterface(f.elem)
1793            self.gen.endFeature()
1794        # Generate spirv elements
1795        for s in spirvexts:
1796            self.generateSpirv(s, self.spirvextdict)
1797        for s in spirvcaps:
1798            self.generateSpirv(s, self.spirvcapdict)
1799        for s in formats:
1800            self.generateFormat(s, self.formatsdict)
1801        for s in self.syncstagedict:
1802            self.generateSyncStage(self.syncstagedict[s])
1803        for s in self.syncaccessdict:
1804            self.generateSyncAccess(self.syncaccessdict[s])
1805        for s in self.syncpipelinedict:
1806            self.generateSyncPipeline(self.syncpipelinedict[s])
1807        self.gen.endFile()
1808
1809    def apiReset(self):
1810        """Reset type/enum/command dictionaries before generating another API.
1811
1812        Use between apiGen() calls to reset internal state."""
1813        for datatype in self.typedict:
1814            self.typedict[datatype].resetState()
1815        for enum in self.enumdict:
1816            self.enumdict[enum].resetState()
1817        for cmd in self.cmddict:
1818            self.cmddict[cmd].resetState()
1819        for cmd in self.apidict:
1820            self.apidict[cmd].resetState()
1821