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