vk_api 1.0.69

The Vulkan API XML exposed as a byte string constant
Documentation
#!/usr/bin/python3 -i
#
# Copyright (c) 2013-2018 The Khronos Group Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os,re,sys
from generator import *

# PyOutputGenerator - subclass of OutputGenerator.
# Generates Python data structures describing API names and relationships.
# Similar to DocOutputGenerator, but writes a single file.
#
# ---- methods ----
# PyOutputGenerator(errFile, warnFile, diagFile) - args as for
#   OutputGenerator. Defines additional internal state.
# ---- methods overriding base class ----
# beginFile(genOpts)
# endFile()
# genType(typeinfo,name)
# genStruct(typeinfo,name)
# genGroup(groupinfo,name)
# genEnum(enuminfo, name)
# genCmd(cmdinfo)
class PyOutputGenerator(OutputGenerator):
    """Generate specified API interfaces in a specific style, such as a C header"""
    def __init__(self,
                 errFile = sys.stderr,
                 warnFile = sys.stderr,
                 diagFile = sys.stdout):
        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
    #
    def beginFile(self, genOpts):
        OutputGenerator.beginFile(self, genOpts)
        #
        # Dictionaries are keyed by the name of the entity (e.g.
        # self.structs is keyed by structure names). Values are
        # the names of related entities (e.g. structs contain
        # a list of type names of members, enums contain a list
        # of enumerants belong to the enumerated type, etc.), or
        # just None if there are no directly related entities.
        #
        # Collect the mappings, then emit the Python script in endFile
        self.basetypes = {}
        self.consts = {}
        self.enums = {}
        self.flags = {}
        self.funcpointers = {}
        self.protos = {}
        self.structs = {}
        self.handles = {}
        self.defines = {}
        # Dictionary containing the type of a type name
        # (e.g. the string name of the dictionary with its contents).
        self.typeCategory = {}
        self.mapDict = {}
    def endFile(self):
        # Print out all the dictionaries as Python strings.
        # Could just print(dict) but that's not human-readable
        dicts = [ [ self.basetypes,     'basetypes' ],
                  [ self.consts,        'consts' ],
                  [ self.enums,         'enums' ],
                  [ self.flags,         'flags' ],
                  [ self.funcpointers,  'funcpointers' ],
                  [ self.protos,        'protos' ],
                  [ self.structs,       'structs' ],
                  [ self.handles,       'handles' ],
                  [ self.defines,       'defines' ],
                  [ self.typeCategory,  'typeCategory' ] ]
        for (dict, name) in dicts:
            write(name + ' = {}', file=self.outFile)
            for key in sorted(dict.keys()):
                write(name + '[' + enquote(key) + '] = ', dict[key], file=self.outFile)

        # Dictionary containing the relationships of a type
        # (e.g. a dictionary with each related type as keys).
        write('mapDict = {}', file=self.outFile)

        # Could just print(self.mapDict), but prefer something human-readable
        for baseType in sorted(self.mapDict.keys()):
            write('mapDict[' + enquote(baseType) + '] = ', self.mapDict[baseType], file=self.outFile)

        OutputGenerator.endFile(self)
    # Add a string entry to the dictionary, quoting it so it gets printed
    # out correctly in self.endFile()
    def addName(self, dict, name, value):
        dict[name] = enquote(value)
    # Add a mapping between types to mapDict. Only include Vulkan types,
    # so we don't end up with a lot of useless uint32_t and void types.
    def addMapping(self, baseType, refType):
        if (not apiName(baseType) or not apiName(refType)):
            self.logMsg('diag', 'PyOutputGenerator::addMapping: IGNORE map from', baseType, '<->', refType)
            return
        else:
            self.logMsg('diag', 'PyOutputGenerator::addMapping: map from', baseType, '<->', refType)

        if (not baseType in self.mapDict.keys()):
            baseDict = {}
            self.mapDict[baseType] = baseDict
        else:
            baseDict = self.mapDict[baseType]
        if (not refType in self.mapDict.keys()):
            refDict = {}
            self.mapDict[refType] = refDict
        else:
            refDict = self.mapDict[refType]

        baseDict[refType] = None
        refDict[baseType] = None
    #
    # Type generation
    # For 'struct' or 'union' types, defer to genStruct() to
    #   add to the dictionary.
    # For 'bitmask' types, add the type name to the 'flags' dictionary,
    #   with the value being the corresponding 'enums' name defining
    #   the acceptable flag bits.
    # For 'enum' types, add the type name to the 'enums' dictionary,
    #   with the value being '@STOPHERE@' (because this case seems
    #   never to happen).
    # For 'funcpointer' types, add the type name to the 'funcpointers'
    #   dictionary.
    # For 'handle' and 'define' types, add the handle or #define name
    #   to the 'struct' dictionary, because that's how the spec sources
    #   tag these types even though they aren't structs.
    def genType(self, typeinfo, name):
        OutputGenerator.genType(self, typeinfo, name)
        typeElem = typeinfo.elem
        # If the type is a struct type, traverse the imbedded <member> tags
        # generating a structure. Otherwise, emit the tag text.
        category = typeElem.get('category')

        # Add a typeCategory{} entry for the category of this type.
        self.addName(self.typeCategory, name, category)

        if (category == 'struct' or category == 'union'):
            self.genStruct(typeinfo, name)
        else:
            # Extract the type name
            # (from self.genOpts). Copy other text through unchanged.
            # If the resulting text is an empty string, don't emit it.
            count = len(noneStr(typeElem.text))
            for elem in typeElem:
                count += len(noneStr(elem.text)) + len(noneStr(elem.tail))
            if (count > 0):
                if (category == 'bitmask'):
                    requiredEnum = typeElem.get('requires')
                    self.addName(self.flags, name, requiredEnum)

                    # This happens when the Flags type is defined, but no
                    # FlagBits are defined yet.
                    if (requiredEnum != None):
                        self.addMapping(name, requiredEnum)
                elif (category == 'enum'):
                    # This case does not seem to come up. It nominally would
                    # result from
                    #   <type name="VkSomething" category="enum"/>,
                    # but the output generator doesn't emit them directly.
                    self.logMsg('warn', 'PyOutputGenerator::genType: invalid \'enum\' category for name:', name)
                elif (category == 'funcpointer'):
                    self.funcpointers[name] = None
                elif (category == 'handle'):
                    self.handles[name] = None
                elif (category == 'define'):
                    self.defines[name] = None
                elif (category == 'basetype'):
                    # Don't add an entry for base types that aren't Vulkan types
                    # e.g. VkBool32 gets one, uint32_t does not
                    if (apiName(name)):
                        self.basetypes[name] = None
                        self.addName(self.typeCategory, name, 'basetype')
                    else:
                        self.logMsg('diag', 'PyOutputGenerator::genType: unprocessed type:', name, 'category:', category)
            else:
                self.logMsg('diag', 'PyOutputGenerator::genType: unprocessed type:', name)
    #
    # Struct (e.g. C "struct" type) generation.
    #
    # Add the struct name to the 'structs' dictionary, with the
    # value being an ordered list of the struct member names.
    def genStruct(self, typeinfo, typeName):
        OutputGenerator.genStruct(self, typeinfo, typeName)

        members = [member.text for member in typeinfo.elem.findall('.//member/name')]
        self.structs[typeName] = members
        memberTypes = [member.text for member in typeinfo.elem.findall('.//member/type')]
        for type in memberTypes:
            self.addMapping(typeName, type)
    #
    # Group (e.g. C "enum" type) generation.
    # These are concatenated together with other types.
    #
    # Add the enum type name to the 'enums' dictionary, with
    #   the value being an ordered list of the enumerant names.
    # Add each enumerant name to the 'consts' dictionary, with
    #   the value being the enum type the enumerant is part of.
    def genGroup(self, groupinfo, groupName):
        OutputGenerator.genGroup(self, groupinfo, groupName)
        groupElem = groupinfo.elem

        # Loop over the nested 'enum' tags.
        enumerants = [elem.get('name') for elem in groupElem.findall('enum')]
        for name in enumerants:
            self.addName(self.consts, name, groupName)
        self.enums[groupName] = enumerants
    # Enumerant generation (compile-time constants)
    #
    # Add the constant name to the 'consts' dictionary, with the
    #   value being None to indicate that the constant isn't
    #   an enumeration value.
    def genEnum(self, enuminfo, name):
        OutputGenerator.genEnum(self, enuminfo, name)

        # Add a typeCategory{} entry for the category of this type.
        self.addName(self.typeCategory, name, 'consts')

        self.consts[name] = None
    #
    # Command generation
    #
    # Add the command name to the 'protos' dictionary, with the
    #   value being an ordered list of the parameter names.
    def genCmd(self, cmdinfo, name):
        OutputGenerator.genCmd(self, cmdinfo, name)

        # Add a typeCategory{} entry for the category of this type.
        self.addName(self.typeCategory, name, 'protos')

        params = [param.text for param in cmdinfo.elem.findall('param/name')]
        self.protos[name] = params
        paramTypes = [param.text for param in cmdinfo.elem.findall('param/type')]
        for type in paramTypes:
            self.addMapping(name, type)