15bd8deadSopenharmony_ci#!/usr/bin/python3 -i
25bd8deadSopenharmony_ci#
35bd8deadSopenharmony_ci# Copyright 2013-2020 The Khronos Group Inc.
45bd8deadSopenharmony_ci# SPDX-License-Identifier: Apache-2.0
55bd8deadSopenharmony_ci
65bd8deadSopenharmony_ciimport io,os,re,string,sys
75bd8deadSopenharmony_cifrom lxml import etree
85bd8deadSopenharmony_ci
95bd8deadSopenharmony_cidef write(*args, **kwargs):
105bd8deadSopenharmony_ci    file = kwargs.pop('file', sys.stdout)
115bd8deadSopenharmony_ci    end = kwargs.pop('end', '\n')
125bd8deadSopenharmony_ci    file.write(' '.join([str(arg) for arg in args]))
135bd8deadSopenharmony_ci    file.write(end)
145bd8deadSopenharmony_ci
155bd8deadSopenharmony_ci# noneStr - returns string argument, or "" if argument is None.
165bd8deadSopenharmony_ci# Used in converting lxml Elements into text.
175bd8deadSopenharmony_ci#   str - string to convert
185bd8deadSopenharmony_cidef noneStr(str):
195bd8deadSopenharmony_ci    if (str):
205bd8deadSopenharmony_ci        return str
215bd8deadSopenharmony_ci    else:
225bd8deadSopenharmony_ci        return ""
235bd8deadSopenharmony_ci
245bd8deadSopenharmony_ci# matchAPIProfile - returns whether an API and profile
255bd8deadSopenharmony_ci#   being generated matches an element's profile
265bd8deadSopenharmony_ci# api - string naming the API to match
275bd8deadSopenharmony_ci# profile - string naming the profile to match
285bd8deadSopenharmony_ci# elem - Element which (may) have 'api' and 'profile'
295bd8deadSopenharmony_ci#   attributes to match to.
305bd8deadSopenharmony_ci# If a tag is not present in the Element, the corresponding API
315bd8deadSopenharmony_ci#   or profile always matches.
325bd8deadSopenharmony_ci# Otherwise, the tag must exactly match the API or profile.
335bd8deadSopenharmony_ci# Thus, if 'profile' = core:
345bd8deadSopenharmony_ci#   <remove> with no attribute will match
355bd8deadSopenharmony_ci#   <remove profile='core'> will match
365bd8deadSopenharmony_ci#   <remove profile='compatibility'> will not match
375bd8deadSopenharmony_ci# Possible match conditions:
385bd8deadSopenharmony_ci#   Requested   Element
395bd8deadSopenharmony_ci#   Profile     Profile
405bd8deadSopenharmony_ci#   ---------   --------
415bd8deadSopenharmony_ci#   None        None        Always matches
425bd8deadSopenharmony_ci#   'string'    None        Always matches
435bd8deadSopenharmony_ci#   None        'string'    Does not match. Can't generate multiple APIs
445bd8deadSopenharmony_ci#                           or profiles, so if an API/profile constraint
455bd8deadSopenharmony_ci#                           is present, it must be asked for explicitly.
465bd8deadSopenharmony_ci#   'string'    'string'    Strings must match
475bd8deadSopenharmony_ci#
485bd8deadSopenharmony_ci#   ** In the future, we will allow regexes for the attributes,
495bd8deadSopenharmony_ci#   not just strings, so that api="^(gl|gles2)" will match. Even
505bd8deadSopenharmony_ci#   this isn't really quite enough, we might prefer something
515bd8deadSopenharmony_ci#   like "gl(core)|gles1(common-lite)".
525bd8deadSopenharmony_cidef matchAPIProfile(api, profile, elem):
535bd8deadSopenharmony_ci    """Match a requested API & profile name to a api & profile attributes of an Element"""
545bd8deadSopenharmony_ci    match = True
555bd8deadSopenharmony_ci    # Match 'api', if present
565bd8deadSopenharmony_ci    if ('api' in elem.attrib):
575bd8deadSopenharmony_ci        if (api == None):
585bd8deadSopenharmony_ci            raise UserWarning("No API requested, but 'api' attribute is present with value '" +
595bd8deadSopenharmony_ci                              elem.get('api') + "'")
605bd8deadSopenharmony_ci        elif (api != elem.get('api')):
615bd8deadSopenharmony_ci            # Requested API doesn't match attribute
625bd8deadSopenharmony_ci            return False
635bd8deadSopenharmony_ci    if ('profile' in elem.attrib):
645bd8deadSopenharmony_ci        if (profile == None):
655bd8deadSopenharmony_ci            raise UserWarning("No profile requested, but 'profile' attribute is present with value '" +
665bd8deadSopenharmony_ci                elem.get('profile') + "'")
675bd8deadSopenharmony_ci        elif (profile != elem.get('profile')):
685bd8deadSopenharmony_ci            # Requested profile doesn't match attribute
695bd8deadSopenharmony_ci            return False
705bd8deadSopenharmony_ci    return True
715bd8deadSopenharmony_ci
725bd8deadSopenharmony_ci# BaseInfo - base class for information about a registry feature
735bd8deadSopenharmony_ci# (type/group/enum/command/API/extension).
745bd8deadSopenharmony_ci#   required - should this feature be defined during header generation
755bd8deadSopenharmony_ci#     (has it been removed by a profile or version)?
765bd8deadSopenharmony_ci#   declared - has this feature been defined already?
775bd8deadSopenharmony_ci#   elem - lxml.etree Element for this feature
785bd8deadSopenharmony_ci#   resetState() - reset required/declared to initial values. Used
795bd8deadSopenharmony_ci#     prior to generating a new API interface.
805bd8deadSopenharmony_ciclass BaseInfo:
815bd8deadSopenharmony_ci    """Represents the state of a registry feature, used during API generation"""
825bd8deadSopenharmony_ci    def __init__(self, elem):
835bd8deadSopenharmony_ci        self.required = False
845bd8deadSopenharmony_ci        self.declared = False
855bd8deadSopenharmony_ci        self.elem = elem
865bd8deadSopenharmony_ci    def resetState(self):
875bd8deadSopenharmony_ci        self.required = False
885bd8deadSopenharmony_ci        self.declared = False
895bd8deadSopenharmony_ci
905bd8deadSopenharmony_ci# TypeInfo - registry information about a type. No additional state
915bd8deadSopenharmony_ci#   beyond BaseInfo is required.
925bd8deadSopenharmony_ciclass TypeInfo(BaseInfo):
935bd8deadSopenharmony_ci    """Represents the state of a registry type"""
945bd8deadSopenharmony_ci    def __init__(self, elem):
955bd8deadSopenharmony_ci        BaseInfo.__init__(self, elem)
965bd8deadSopenharmony_ci
975bd8deadSopenharmony_ci# GroupInfo - registry information about a group of related enums.
985bd8deadSopenharmony_ci#   enums - dictionary of enum names which are in the group
995bd8deadSopenharmony_ciclass GroupInfo(BaseInfo):
1005bd8deadSopenharmony_ci    """Represents the state of a registry enumerant group"""
1015bd8deadSopenharmony_ci    def __init__(self, elem):
1025bd8deadSopenharmony_ci        BaseInfo.__init__(self, elem)
1035bd8deadSopenharmony_ci        self.enums = {}
1045bd8deadSopenharmony_ci
1055bd8deadSopenharmony_ci# EnumInfo - registry information about an enum
1065bd8deadSopenharmony_ci#   type - numeric type of the value of the <enum> tag
1075bd8deadSopenharmony_ci#     ( '' for GLint, 'u' for GLuint, 'ull' for GLuint64 )
1085bd8deadSopenharmony_ciclass EnumInfo(BaseInfo):
1095bd8deadSopenharmony_ci    """Represents the state of a registry enum"""
1105bd8deadSopenharmony_ci    def __init__(self, elem):
1115bd8deadSopenharmony_ci        BaseInfo.__init__(self, elem)
1125bd8deadSopenharmony_ci        self.type = elem.get('type')
1135bd8deadSopenharmony_ci        if (self.type == None):
1145bd8deadSopenharmony_ci            self.type = ''
1155bd8deadSopenharmony_ci
1165bd8deadSopenharmony_ci# CmdInfo - registry information about a command
1175bd8deadSopenharmony_ci#   glxtype - type of GLX protocol { None, 'render', 'single', 'vendor' }
1185bd8deadSopenharmony_ci#   glxopcode - GLX protocol opcode { None, number }
1195bd8deadSopenharmony_ci#   glxequiv - equivalent command at GLX dispatch level { None, string }
1205bd8deadSopenharmony_ci#   vecequiv - equivalent vector form of a command taking multiple scalar args
1215bd8deadSopenharmony_ci#     { None, string }
1225bd8deadSopenharmony_ciclass CmdInfo(BaseInfo):
1235bd8deadSopenharmony_ci    """Represents the state of a registry command"""
1245bd8deadSopenharmony_ci    def __init__(self, elem):
1255bd8deadSopenharmony_ci        BaseInfo.__init__(self, elem)
1265bd8deadSopenharmony_ci        self.glxtype = None
1275bd8deadSopenharmony_ci        self.glxopcode = None
1285bd8deadSopenharmony_ci        self.glxequiv = None
1295bd8deadSopenharmony_ci        self.vecequiv = None
1305bd8deadSopenharmony_ci
1315bd8deadSopenharmony_ci# FeatureInfo - registry information about an API <feature>
1325bd8deadSopenharmony_ci# or <extension>
1335bd8deadSopenharmony_ci#   name - feature name string (e.g. 'GL_ARB_multitexture')
1345bd8deadSopenharmony_ci#   number - feature version number (e.g. 1.2). <extension>
1355bd8deadSopenharmony_ci#     features are unversioned and assigned version number 0.
1365bd8deadSopenharmony_ci#   category - category, e.g. VERSION or ARB/KHR/OES/ETC/vendor
1375bd8deadSopenharmony_ci#   emit - has this feature been defined already?
1385bd8deadSopenharmony_ciclass FeatureInfo(BaseInfo):
1395bd8deadSopenharmony_ci    """Represents the state of an API feature (version/extension)"""
1405bd8deadSopenharmony_ci    def __init__(self, elem):
1415bd8deadSopenharmony_ci        BaseInfo.__init__(self, elem)
1425bd8deadSopenharmony_ci        self.name = elem.get('name')
1435bd8deadSopenharmony_ci        # Determine element category (vendor). Only works
1445bd8deadSopenharmony_ci        # for <extension> elements.
1455bd8deadSopenharmony_ci        if (elem.tag == 'feature'):
1465bd8deadSopenharmony_ci            self.category = 'VERSION'
1475bd8deadSopenharmony_ci            self.number = elem.get('number')
1485bd8deadSopenharmony_ci        else:
1495bd8deadSopenharmony_ci            self.category = self.name.split('_', 2)[1]
1505bd8deadSopenharmony_ci            self.number = "0"
1515bd8deadSopenharmony_ci        self.emit = False
1525bd8deadSopenharmony_ci
1535bd8deadSopenharmony_ci# Primary sort key for regSortFeatures.
1545bd8deadSopenharmony_ci# Sorts by category of the feature name string:
1555bd8deadSopenharmony_ci#   Core API features (those defined with a <feature> tag)
1565bd8deadSopenharmony_ci#   ARB/KHR/OES (Khronos extensions)
1575bd8deadSopenharmony_ci#   other       (EXT/vendor extensions)
1585bd8deadSopenharmony_cidef regSortCategoryKey(feature):
1595bd8deadSopenharmony_ci    if (feature.elem.tag == 'feature'):
1605bd8deadSopenharmony_ci        return 0
1615bd8deadSopenharmony_ci    elif (feature.category == 'ARB' or
1625bd8deadSopenharmony_ci          feature.category == 'KHR' or
1635bd8deadSopenharmony_ci          feature.category == 'OES'):
1645bd8deadSopenharmony_ci        return 1
1655bd8deadSopenharmony_ci    else:
1665bd8deadSopenharmony_ci        return 2
1675bd8deadSopenharmony_ci
1685bd8deadSopenharmony_ci# Secondary sort key for regSortFeatures.
1695bd8deadSopenharmony_ci# Sorts by extension name.
1705bd8deadSopenharmony_cidef regSortNameKey(feature):
1715bd8deadSopenharmony_ci    return feature.name
1725bd8deadSopenharmony_ci
1735bd8deadSopenharmony_ci# Tertiary sort key for regSortFeatures.
1745bd8deadSopenharmony_ci# Sorts by feature version number. <extension>
1755bd8deadSopenharmony_ci# elements all have version number "0"
1765bd8deadSopenharmony_cidef regSortNumberKey(feature):
1775bd8deadSopenharmony_ci    return feature.number
1785bd8deadSopenharmony_ci
1795bd8deadSopenharmony_ci# regSortFeatures - default sort procedure for features.
1805bd8deadSopenharmony_ci# Sorts by primary key of feature category,
1815bd8deadSopenharmony_ci# then by feature name within the category,
1825bd8deadSopenharmony_ci# then by version number
1835bd8deadSopenharmony_cidef regSortFeatures(featureList):
1845bd8deadSopenharmony_ci    featureList.sort(key = regSortNumberKey)
1855bd8deadSopenharmony_ci    featureList.sort(key = regSortNameKey)
1865bd8deadSopenharmony_ci    featureList.sort(key = regSortCategoryKey)
1875bd8deadSopenharmony_ci
1885bd8deadSopenharmony_ci# GeneratorOptions - base class for options used during header production
1895bd8deadSopenharmony_ci# These options are target language independent, and used by
1905bd8deadSopenharmony_ci# Registry.apiGen() and by base OutputGenerator objects.
1915bd8deadSopenharmony_ci#
1925bd8deadSopenharmony_ci# Members
1935bd8deadSopenharmony_ci#   filename - name of file to generate, or None to write to stdout.
1945bd8deadSopenharmony_ci#   apiname - string matching <api> 'apiname' attribute, e.g. 'gl'.
1955bd8deadSopenharmony_ci#   profile - string specifying API profile , e.g. 'core', or None.
1965bd8deadSopenharmony_ci#   versions - regex matching API versions to process interfaces for.
1975bd8deadSopenharmony_ci#     Normally '.*' or '[0-9]\.[0-9]' to match all defined versions.
1985bd8deadSopenharmony_ci#   emitversions - regex matching API versions to actually emit
1995bd8deadSopenharmony_ci#    interfaces for (though all requested versions are considered
2005bd8deadSopenharmony_ci#    when deciding which interfaces to generate). For GL 4.3 glext.h,
2015bd8deadSopenharmony_ci#     this might be '1\.[2-5]|[2-4]\.[0-9]'.
2025bd8deadSopenharmony_ci#   defaultExtensions - If not None, a string which must in its
2035bd8deadSopenharmony_ci#     entirety match the pattern in the "supported" attribute of
2045bd8deadSopenharmony_ci#     the <extension>. Defaults to None. Usually the same as apiname.
2055bd8deadSopenharmony_ci#   addExtensions - regex matching names of additional extensions
2065bd8deadSopenharmony_ci#     to include. Defaults to None.
2075bd8deadSopenharmony_ci#   removeExtensions - regex matching names of extensions to
2085bd8deadSopenharmony_ci#     remove (after defaultExtensions and addExtensions). Defaults
2095bd8deadSopenharmony_ci#     to None.
2105bd8deadSopenharmony_ci#   sortProcedure - takes a list of FeatureInfo objects and sorts
2115bd8deadSopenharmony_ci#     them in place to a preferred order in the generated output.
2125bd8deadSopenharmony_ci#     Default is core API versions, ARB/KHR/OES extensions, all
2135bd8deadSopenharmony_ci#     other extensions, alphabetically within each group.
2145bd8deadSopenharmony_ci# The regex patterns can be None or empty, in which case they match
2155bd8deadSopenharmony_ci#   nothing.
2165bd8deadSopenharmony_ciclass GeneratorOptions:
2175bd8deadSopenharmony_ci    """Represents options during header production from an API registry"""
2185bd8deadSopenharmony_ci    def __init__(self,
2195bd8deadSopenharmony_ci                 filename = None,
2205bd8deadSopenharmony_ci                 apiname = None,
2215bd8deadSopenharmony_ci                 profile = None,
2225bd8deadSopenharmony_ci                 versions = '.*',
2235bd8deadSopenharmony_ci                 emitversions = '.*',
2245bd8deadSopenharmony_ci                 defaultExtensions = None,
2255bd8deadSopenharmony_ci                 addExtensions = None,
2265bd8deadSopenharmony_ci                 removeExtensions = None,
2275bd8deadSopenharmony_ci                 sortProcedure = regSortFeatures):
2285bd8deadSopenharmony_ci        self.filename          = filename
2295bd8deadSopenharmony_ci        self.apiname           = apiname
2305bd8deadSopenharmony_ci        self.profile           = profile
2315bd8deadSopenharmony_ci        self.versions          = self.emptyRegex(versions)
2325bd8deadSopenharmony_ci        self.emitversions      = self.emptyRegex(emitversions)
2335bd8deadSopenharmony_ci        self.defaultExtensions = defaultExtensions
2345bd8deadSopenharmony_ci        self.addExtensions     = self.emptyRegex(addExtensions)
2355bd8deadSopenharmony_ci        self.removeExtensions  = self.emptyRegex(removeExtensions)
2365bd8deadSopenharmony_ci        self.sortProcedure     = sortProcedure
2375bd8deadSopenharmony_ci    #
2385bd8deadSopenharmony_ci    # Substitute a regular expression which matches no version
2395bd8deadSopenharmony_ci    # or extension names for None or the empty string.
2405bd8deadSopenharmony_ci    def emptyRegex(self,pat):
2415bd8deadSopenharmony_ci        if (pat == None or pat == ''):
2425bd8deadSopenharmony_ci            return '_nomatch_^'
2435bd8deadSopenharmony_ci        else:
2445bd8deadSopenharmony_ci            return pat
2455bd8deadSopenharmony_ci
2465bd8deadSopenharmony_ci# CGeneratorOptions - subclass of GeneratorOptions.
2475bd8deadSopenharmony_ci#
2485bd8deadSopenharmony_ci# Adds options used by COutputGenerator objects during C language header
2495bd8deadSopenharmony_ci# generation.
2505bd8deadSopenharmony_ci#
2515bd8deadSopenharmony_ci# Additional members
2525bd8deadSopenharmony_ci#   prefixText - list of strings to prefix generated header with
2535bd8deadSopenharmony_ci#     (usually a copyright statement + calling convention macros).
2545bd8deadSopenharmony_ci#   protectFile - True if multiple inclusion protection should be
2555bd8deadSopenharmony_ci#     generated (based on the filename) around the entire header.
2565bd8deadSopenharmony_ci#   protectFeature - True if #ifndef..#endif protection should be
2575bd8deadSopenharmony_ci#     generated around a feature interface in the header file.
2585bd8deadSopenharmony_ci#   genFuncPointers - True if function pointer typedefs should be
2595bd8deadSopenharmony_ci#     generated
2605bd8deadSopenharmony_ci#   protectProto - Controls cpp protection around prototypes:
2615bd8deadSopenharmony_ci#     False - no protection
2625bd8deadSopenharmony_ci#     'nonzero' - protectProtoStr must be defined to a nonzero value
2635bd8deadSopenharmony_ci#     True - protectProtoStr must be defined
2645bd8deadSopenharmony_ci#   protectProtoStr - #ifdef symbol to use around prototype
2655bd8deadSopenharmony_ci#     declarations, if protected
2665bd8deadSopenharmony_ci#   apicall - string to use for the function declaration prefix,
2675bd8deadSopenharmony_ci#     such as APICALL on Windows.
2685bd8deadSopenharmony_ci#   apientry - string to use for the calling convention macro,
2695bd8deadSopenharmony_ci#     in typedefs, such as APIENTRY.
2705bd8deadSopenharmony_ci#   apientryp - string to use for the calling convention macro
2715bd8deadSopenharmony_ci#     in function pointer typedefs, such as APIENTRYP.
2725bd8deadSopenharmony_ciclass CGeneratorOptions(GeneratorOptions):
2735bd8deadSopenharmony_ci    """Represents options during C header production from an API registry"""
2745bd8deadSopenharmony_ci    def __init__(self,
2755bd8deadSopenharmony_ci                 filename = None,
2765bd8deadSopenharmony_ci                 apiname = None,
2775bd8deadSopenharmony_ci                 profile = None,
2785bd8deadSopenharmony_ci                 versions = '.*',
2795bd8deadSopenharmony_ci                 emitversions = '.*',
2805bd8deadSopenharmony_ci                 defaultExtensions = None,
2815bd8deadSopenharmony_ci                 addExtensions = None,
2825bd8deadSopenharmony_ci                 removeExtensions = None,
2835bd8deadSopenharmony_ci                 sortProcedure = regSortFeatures,
2845bd8deadSopenharmony_ci                 prefixText = "",
2855bd8deadSopenharmony_ci                 genFuncPointers = True,
2865bd8deadSopenharmony_ci                 protectFile = True,
2875bd8deadSopenharmony_ci                 protectFeature = True,
2885bd8deadSopenharmony_ci                 protectProto = True,
2895bd8deadSopenharmony_ci                 protectProtoStr = True,
2905bd8deadSopenharmony_ci                 apicall = '',
2915bd8deadSopenharmony_ci                 apientry = '',
2925bd8deadSopenharmony_ci                 apientryp = ''):
2935bd8deadSopenharmony_ci        GeneratorOptions.__init__(self, filename, apiname, profile,
2945bd8deadSopenharmony_ci                                  versions, emitversions, defaultExtensions,
2955bd8deadSopenharmony_ci                                  addExtensions, removeExtensions, sortProcedure)
2965bd8deadSopenharmony_ci        self.prefixText      = prefixText
2975bd8deadSopenharmony_ci        self.genFuncPointers = genFuncPointers
2985bd8deadSopenharmony_ci        self.protectFile     = protectFile
2995bd8deadSopenharmony_ci        self.protectFeature  = protectFeature
3005bd8deadSopenharmony_ci        self.protectProto    = protectProto
3015bd8deadSopenharmony_ci        self.protectProtoStr = protectProtoStr
3025bd8deadSopenharmony_ci        self.apicall         = apicall
3035bd8deadSopenharmony_ci        self.apientry        = apientry
3045bd8deadSopenharmony_ci        self.apientryp       = apientryp
3055bd8deadSopenharmony_ci
3065bd8deadSopenharmony_ci# OutputGenerator - base class for generating API interfaces.
3075bd8deadSopenharmony_ci# Manages basic logic, logging, and output file control
3085bd8deadSopenharmony_ci# Derived classes actually generate formatted output.
3095bd8deadSopenharmony_ci#
3105bd8deadSopenharmony_ci# ---- methods ----
3115bd8deadSopenharmony_ci# OutputGenerator(errFile, warnFile, diagFile)
3125bd8deadSopenharmony_ci#   errFile, warnFile, diagFile - file handles to write errors,
3135bd8deadSopenharmony_ci#     warnings, diagnostics to. May be None to not write.
3145bd8deadSopenharmony_ci# logMsg(level, *args) - log messages of different categories
3155bd8deadSopenharmony_ci#   level - 'error', 'warn', or 'diag'. 'error' will also
3165bd8deadSopenharmony_ci#     raise a UserWarning exception
3175bd8deadSopenharmony_ci#   *args - print()-style arguments
3185bd8deadSopenharmony_ci# beginFile(genOpts) - start a new interface file
3195bd8deadSopenharmony_ci#   genOpts - GeneratorOptions controlling what's generated and how
3205bd8deadSopenharmony_ci# endFile() - finish an interface file, closing it when done
3215bd8deadSopenharmony_ci# beginFeature(interface, emit) - write interface for a feature
3225bd8deadSopenharmony_ci# and tag generated features as having been done.
3235bd8deadSopenharmony_ci#   interface - element for the <version> / <extension> to generate
3245bd8deadSopenharmony_ci#   emit - actually write to the header only when True
3255bd8deadSopenharmony_ci# endFeature() - finish an interface.
3265bd8deadSopenharmony_ci# genType(typeinfo,name) - generate interface for a type
3275bd8deadSopenharmony_ci#   typeinfo - TypeInfo for a type
3285bd8deadSopenharmony_ci# genEnum(enuminfo, name) - generate interface for an enum
3295bd8deadSopenharmony_ci#   enuminfo - EnumInfo for an enum
3305bd8deadSopenharmony_ci#   name - enum name
3315bd8deadSopenharmony_ci# genCmd(cmdinfo) - generate interface for a command
3325bd8deadSopenharmony_ci#   cmdinfo - CmdInfo for a command
3335bd8deadSopenharmony_ciclass OutputGenerator:
3345bd8deadSopenharmony_ci    """Generate specified API interfaces in a specific style, such as a C header"""
3355bd8deadSopenharmony_ci    def __init__(self,
3365bd8deadSopenharmony_ci                 errFile = sys.stderr,
3375bd8deadSopenharmony_ci                 warnFile = sys.stderr,
3385bd8deadSopenharmony_ci                 diagFile = sys.stdout):
3395bd8deadSopenharmony_ci        self.outFile = None
3405bd8deadSopenharmony_ci        self.errFile = errFile
3415bd8deadSopenharmony_ci        self.warnFile = warnFile
3425bd8deadSopenharmony_ci        self.diagFile = diagFile
3435bd8deadSopenharmony_ci        # Internal state
3445bd8deadSopenharmony_ci        self.featureName = None
3455bd8deadSopenharmony_ci        self.genOpts = None
3465bd8deadSopenharmony_ci    #
3475bd8deadSopenharmony_ci    # logMsg - write a message of different categories to different
3485bd8deadSopenharmony_ci    #   destinations.
3495bd8deadSopenharmony_ci    # level -
3505bd8deadSopenharmony_ci    #   'diag' (diagnostic, voluminous)
3515bd8deadSopenharmony_ci    #   'warn' (warning)
3525bd8deadSopenharmony_ci    #   'error' (fatal error - raises exception after logging)
3535bd8deadSopenharmony_ci    # *args - print()-style arguments to direct to corresponding log
3545bd8deadSopenharmony_ci    def logMsg(self, level, *args):
3555bd8deadSopenharmony_ci        """Log a message at the given level. Can be ignored or log to a file"""
3565bd8deadSopenharmony_ci        if (level == 'error'):
3575bd8deadSopenharmony_ci            strfile = io.StringIO()
3585bd8deadSopenharmony_ci            write('ERROR:', *args, file=strfile)
3595bd8deadSopenharmony_ci            if (self.errFile != None):
3605bd8deadSopenharmony_ci                write(strfile.getvalue(), file=self.errFile)
3615bd8deadSopenharmony_ci            raise UserWarning(strfile.getvalue())
3625bd8deadSopenharmony_ci        elif (level == 'warn'):
3635bd8deadSopenharmony_ci            if (self.warnFile != None):
3645bd8deadSopenharmony_ci                write('WARNING:', *args, file=self.warnFile)
3655bd8deadSopenharmony_ci        elif (level == 'diag'):
3665bd8deadSopenharmony_ci            if (self.diagFile != None):
3675bd8deadSopenharmony_ci                write('DIAG:', *args, file=self.diagFile)
3685bd8deadSopenharmony_ci        else:
3695bd8deadSopenharmony_ci            raise UserWarning(
3705bd8deadSopenharmony_ci                '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
3715bd8deadSopenharmony_ci    #
3725bd8deadSopenharmony_ci    def beginFile(self, genOpts):
3735bd8deadSopenharmony_ci        self.genOpts = genOpts
3745bd8deadSopenharmony_ci        #
3755bd8deadSopenharmony_ci        # Open specified output file. Not done in constructor since a
3765bd8deadSopenharmony_ci        # Generator can be used without writing to a file.
3775bd8deadSopenharmony_ci        if (self.genOpts.filename != None):
3785bd8deadSopenharmony_ci            self.outFile = open(self.genOpts.filename, 'w')
3795bd8deadSopenharmony_ci        else:
3805bd8deadSopenharmony_ci            self.outFile = sys.stdout
3815bd8deadSopenharmony_ci    def endFile(self):
3825bd8deadSopenharmony_ci        self.errFile and self.errFile.flush()
3835bd8deadSopenharmony_ci        self.warnFile and self.warnFile.flush()
3845bd8deadSopenharmony_ci        self.diagFile and self.diagFile.flush()
3855bd8deadSopenharmony_ci        self.outFile.flush()
3865bd8deadSopenharmony_ci        if (self.outFile != sys.stdout and self.outFile != sys.stderr):
3875bd8deadSopenharmony_ci            self.outFile.close()
3885bd8deadSopenharmony_ci        self.genOpts = None
3895bd8deadSopenharmony_ci    #
3905bd8deadSopenharmony_ci    def beginFeature(self, interface, emit):
3915bd8deadSopenharmony_ci        self.emit = emit
3925bd8deadSopenharmony_ci        self.featureName = interface.get('name')
3935bd8deadSopenharmony_ci        # If there's an additional 'protect' attribute in the feature, save it
3945bd8deadSopenharmony_ci        self.featureExtraProtect = interface.get('protect')
3955bd8deadSopenharmony_ci    def endFeature(self):
3965bd8deadSopenharmony_ci        # Derived classes responsible for emitting feature
3975bd8deadSopenharmony_ci        self.featureName = None
3985bd8deadSopenharmony_ci        self.featureExtraProtect = None
3995bd8deadSopenharmony_ci    #
4005bd8deadSopenharmony_ci    # Type generation
4015bd8deadSopenharmony_ci    def genType(self, typeinfo, name):
4025bd8deadSopenharmony_ci        if (self.featureName == None):
4035bd8deadSopenharmony_ci            raise UserWarning('Attempt to generate type', name,
4045bd8deadSopenharmony_ci                    'when not in feature')
4055bd8deadSopenharmony_ci    #
4065bd8deadSopenharmony_ci    # Enumerant generation
4075bd8deadSopenharmony_ci    def genEnum(self, enuminfo, name):
4085bd8deadSopenharmony_ci        if (self.featureName == None):
4095bd8deadSopenharmony_ci            raise UserWarning('Attempt to generate enum', name,
4105bd8deadSopenharmony_ci                    'when not in feature')
4115bd8deadSopenharmony_ci    #
4125bd8deadSopenharmony_ci    # Command generation
4135bd8deadSopenharmony_ci    def genCmd(self, cmd, name):
4145bd8deadSopenharmony_ci        if (self.featureName == None):
4155bd8deadSopenharmony_ci            raise UserWarning('Attempt to generate command', name,
4165bd8deadSopenharmony_ci                    'when not in feature')
4175bd8deadSopenharmony_ci
4185bd8deadSopenharmony_ci# COutputGenerator - subclass of OutputGenerator.
4195bd8deadSopenharmony_ci# Generates C-language API interfaces.
4205bd8deadSopenharmony_ci#
4215bd8deadSopenharmony_ci# ---- methods ----
4225bd8deadSopenharmony_ci# COutputGenerator(errFile, warnFile, diagFile) - args as for
4235bd8deadSopenharmony_ci#   OutputGenerator. Defines additional internal state.
4245bd8deadSopenharmony_ci# makeCDecls(cmd) - return C prototype and function pointer typedef for a
4255bd8deadSopenharmony_ci#     <command> Element, as a list of two strings
4265bd8deadSopenharmony_ci#   cmd - Element for the <command>
4275bd8deadSopenharmony_ci# newline() - print a newline to the output file (utility function)
4285bd8deadSopenharmony_ci# ---- methods overriding base class ----
4295bd8deadSopenharmony_ci# beginFile(genOpts)
4305bd8deadSopenharmony_ci# endFile()
4315bd8deadSopenharmony_ci# beginFeature(interface, emit)
4325bd8deadSopenharmony_ci# endFeature()
4335bd8deadSopenharmony_ci# genType(typeinfo,name) - generate interface for a type
4345bd8deadSopenharmony_ci# genEnum(enuminfo, name)
4355bd8deadSopenharmony_ci# genCmd(cmdinfo)
4365bd8deadSopenharmony_ciclass COutputGenerator(OutputGenerator):
4375bd8deadSopenharmony_ci    """Generate specified API interfaces in a specific style, such as a C header"""
4385bd8deadSopenharmony_ci    def __init__(self,
4395bd8deadSopenharmony_ci                 errFile = sys.stderr,
4405bd8deadSopenharmony_ci                 warnFile = sys.stderr,
4415bd8deadSopenharmony_ci                 diagFile = sys.stdout):
4425bd8deadSopenharmony_ci        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
4435bd8deadSopenharmony_ci        # Internal state - accumulators for different inner block text
4445bd8deadSopenharmony_ci        self.typeBody = ''
4455bd8deadSopenharmony_ci        self.enumBody = ''
4465bd8deadSopenharmony_ci        self.cmdBody = ''
4475bd8deadSopenharmony_ci    #
4485bd8deadSopenharmony_ci    # makeCDecls - return C prototype and function pointer typedef for a
4495bd8deadSopenharmony_ci    #   command, as a two-element list of strings.
4505bd8deadSopenharmony_ci    # cmd - Element containing a <command> tag
4515bd8deadSopenharmony_ci    def makeCDecls(self, cmd):
4525bd8deadSopenharmony_ci        """Generate C function pointer typedef for <command> Element"""
4535bd8deadSopenharmony_ci        proto = cmd.find('proto')
4545bd8deadSopenharmony_ci        params = cmd.findall('param')
4555bd8deadSopenharmony_ci        # Begin accumulating prototype and typedef strings
4565bd8deadSopenharmony_ci        pdecl = self.genOpts.apicall
4575bd8deadSopenharmony_ci        tdecl = 'typedef '
4585bd8deadSopenharmony_ci        #
4595bd8deadSopenharmony_ci        # Insert the function return type/name.
4605bd8deadSopenharmony_ci        # For prototypes, add APIENTRY macro before the name
4615bd8deadSopenharmony_ci        # For typedefs, add (APIENTRYP <name>) around the name and
4625bd8deadSopenharmony_ci        #   use the PFNGLCMDNAMEPROC nameng convention.
4635bd8deadSopenharmony_ci        # Done by walking the tree for <proto> element by element.
4645bd8deadSopenharmony_ci        # lxml.etree has elem.text followed by (elem[i], elem[i].tail)
4655bd8deadSopenharmony_ci        #   for each child element and any following text
4665bd8deadSopenharmony_ci        # Leading text
4675bd8deadSopenharmony_ci        pdecl += noneStr(proto.text)
4685bd8deadSopenharmony_ci        tdecl += noneStr(proto.text)
4695bd8deadSopenharmony_ci        # For each child element, if it's a <name> wrap in appropriate
4705bd8deadSopenharmony_ci        # declaration. Otherwise append its contents and tail contents.
4715bd8deadSopenharmony_ci        for elem in proto:
4725bd8deadSopenharmony_ci            text = noneStr(elem.text)
4735bd8deadSopenharmony_ci            tail = noneStr(elem.tail)
4745bd8deadSopenharmony_ci            if (elem.tag == 'name'):
4755bd8deadSopenharmony_ci                pdecl += self.genOpts.apientry + text + tail
4765bd8deadSopenharmony_ci                tdecl += '(' + self.genOpts.apientryp + 'PFN' + text.upper() + 'PROC' + tail + ')'
4775bd8deadSopenharmony_ci            else:
4785bd8deadSopenharmony_ci                pdecl += text + tail
4795bd8deadSopenharmony_ci                tdecl += text + tail
4805bd8deadSopenharmony_ci        # Now add the parameter declaration list, which is identical
4815bd8deadSopenharmony_ci        # for prototypes and typedefs. Concatenate all the text from
4825bd8deadSopenharmony_ci        # a <param> node without the tags. No tree walking required
4835bd8deadSopenharmony_ci        # since all tags are ignored.
4845bd8deadSopenharmony_ci        n = len(params)
4855bd8deadSopenharmony_ci        paramdecl = ' ('
4865bd8deadSopenharmony_ci        if n > 0:
4875bd8deadSopenharmony_ci            for i in range(0,n):
4885bd8deadSopenharmony_ci                paramdecl += ''.join([t for t in params[i].itertext()])
4895bd8deadSopenharmony_ci                if (i < n - 1):
4905bd8deadSopenharmony_ci                    paramdecl += ', '
4915bd8deadSopenharmony_ci        else:
4925bd8deadSopenharmony_ci            paramdecl += 'void'
4935bd8deadSopenharmony_ci        paramdecl += ');\n';
4945bd8deadSopenharmony_ci        return [ pdecl + paramdecl, tdecl + paramdecl ]
4955bd8deadSopenharmony_ci    #
4965bd8deadSopenharmony_ci    def newline(self):
4975bd8deadSopenharmony_ci        write('', file=self.outFile)
4985bd8deadSopenharmony_ci    #
4995bd8deadSopenharmony_ci    def beginFile(self, genOpts):
5005bd8deadSopenharmony_ci        OutputGenerator.beginFile(self, genOpts)
5015bd8deadSopenharmony_ci        # C-specific
5025bd8deadSopenharmony_ci        #
5035bd8deadSopenharmony_ci        # Multiple inclusion protection & C++ wrappers.
5045bd8deadSopenharmony_ci        if (genOpts.protectFile and self.genOpts.filename):
5055bd8deadSopenharmony_ci            headerSym = '__' + self.genOpts.apiname + '_' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
5065bd8deadSopenharmony_ci            write('#ifndef', headerSym, file=self.outFile)
5075bd8deadSopenharmony_ci            write('#define', headerSym, '1', file=self.outFile)
5085bd8deadSopenharmony_ci            self.newline()
5095bd8deadSopenharmony_ci        write('#ifdef __cplusplus', file=self.outFile)
5105bd8deadSopenharmony_ci        write('extern "C" {', file=self.outFile)
5115bd8deadSopenharmony_ci        write('#endif', file=self.outFile)
5125bd8deadSopenharmony_ci        self.newline()
5135bd8deadSopenharmony_ci        #
5145bd8deadSopenharmony_ci        # User-supplied prefix text, if any (list of strings)
5155bd8deadSopenharmony_ci        if (genOpts.prefixText):
5165bd8deadSopenharmony_ci            for s in genOpts.prefixText:
5175bd8deadSopenharmony_ci                write(s, file=self.outFile)
5185bd8deadSopenharmony_ci        #
5195bd8deadSopenharmony_ci        # Some boilerplate describing what was generated - this
5205bd8deadSopenharmony_ci        # will probably be removed later since the extensions
5215bd8deadSopenharmony_ci        # pattern may be very long.
5225bd8deadSopenharmony_ci        write('/* Generated C header for:', file=self.outFile)
5235bd8deadSopenharmony_ci        write(' * API:', genOpts.apiname, file=self.outFile)
5245bd8deadSopenharmony_ci        if (genOpts.profile):
5255bd8deadSopenharmony_ci            write(' * Profile:', genOpts.profile, file=self.outFile)
5265bd8deadSopenharmony_ci        write(' * Versions considered:', genOpts.versions, file=self.outFile)
5275bd8deadSopenharmony_ci        write(' * Versions emitted:', genOpts.emitversions, file=self.outFile)
5285bd8deadSopenharmony_ci        write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile)
5295bd8deadSopenharmony_ci        write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile)
5305bd8deadSopenharmony_ci        write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile)
5315bd8deadSopenharmony_ci        write(' */', file=self.outFile)
5325bd8deadSopenharmony_ci    def endFile(self):
5335bd8deadSopenharmony_ci        # C-specific
5345bd8deadSopenharmony_ci        # Finish C++ wrapper and multiple inclusion protection
5355bd8deadSopenharmony_ci        self.newline()
5365bd8deadSopenharmony_ci        write('#ifdef __cplusplus', file=self.outFile)
5375bd8deadSopenharmony_ci        write('}', file=self.outFile)
5385bd8deadSopenharmony_ci        write('#endif', file=self.outFile)
5395bd8deadSopenharmony_ci        if (self.genOpts.protectFile and self.genOpts.filename):
5405bd8deadSopenharmony_ci            self.newline()
5415bd8deadSopenharmony_ci            write('#endif', file=self.outFile)
5425bd8deadSopenharmony_ci        # Finish processing in superclass
5435bd8deadSopenharmony_ci        OutputGenerator.endFile(self)
5445bd8deadSopenharmony_ci    def beginFeature(self, interface, emit):
5455bd8deadSopenharmony_ci        # Start processing in superclass
5465bd8deadSopenharmony_ci        OutputGenerator.beginFeature(self, interface, emit)
5475bd8deadSopenharmony_ci        # C-specific
5485bd8deadSopenharmony_ci        # Accumulate types, enums, function pointer typedefs, end function
5495bd8deadSopenharmony_ci        # prototypes separately for this feature. They're only printed in
5505bd8deadSopenharmony_ci        # endFeature().
5515bd8deadSopenharmony_ci        self.typeBody = ''
5525bd8deadSopenharmony_ci        self.enumBody = ''
5535bd8deadSopenharmony_ci        self.cmdPointerBody = ''
5545bd8deadSopenharmony_ci        self.cmdBody = ''
5555bd8deadSopenharmony_ci    def endFeature(self):
5565bd8deadSopenharmony_ci        # C-specific
5575bd8deadSopenharmony_ci        # Actually write the interface to the output file.
5585bd8deadSopenharmony_ci        if (self.emit):
5595bd8deadSopenharmony_ci            self.newline()
5605bd8deadSopenharmony_ci            if (self.genOpts.protectFeature):
5615bd8deadSopenharmony_ci                write('#ifndef', self.featureName, file=self.outFile)
5625bd8deadSopenharmony_ci            write('#define', self.featureName, '1', file=self.outFile)
5635bd8deadSopenharmony_ci            if (self.typeBody != ''):
5645bd8deadSopenharmony_ci                write(self.typeBody, end='', file=self.outFile)
5655bd8deadSopenharmony_ci            #
5665bd8deadSopenharmony_ci            # Don't add additional protection for derived type declarations,
5675bd8deadSopenharmony_ci            # which may be needed by other features later on.
5685bd8deadSopenharmony_ci            if (self.featureExtraProtect != None):
5695bd8deadSopenharmony_ci                write('#ifdef', self.featureExtraProtect, file=self.outFile)
5705bd8deadSopenharmony_ci            if (self.enumBody != ''):
5715bd8deadSopenharmony_ci                write(self.enumBody, end='', file=self.outFile)
5725bd8deadSopenharmony_ci            if (self.genOpts.genFuncPointers and self.cmdPointerBody != ''):
5735bd8deadSopenharmony_ci                write(self.cmdPointerBody, end='', file=self.outFile)
5745bd8deadSopenharmony_ci            if (self.cmdBody != ''):
5755bd8deadSopenharmony_ci                if (self.genOpts.protectProto == True):
5765bd8deadSopenharmony_ci                    prefix = '#ifdef ' + self.genOpts.protectProtoStr + '\n'
5775bd8deadSopenharmony_ci                    suffix = '#endif\n'
5785bd8deadSopenharmony_ci                elif (self.genOpts.protectProto == 'nonzero'):
5795bd8deadSopenharmony_ci                    prefix = '#if ' + self.genOpts.protectProtoStr + '\n'
5805bd8deadSopenharmony_ci                    suffix = '#endif\n'
5815bd8deadSopenharmony_ci                elif (self.genOpts.protectProto == False):
5825bd8deadSopenharmony_ci                    prefix = ''
5835bd8deadSopenharmony_ci                    suffix = ''
5845bd8deadSopenharmony_ci                else:
5855bd8deadSopenharmony_ci                    self.gen.logMsg('warn',
5865bd8deadSopenharmony_ci                                    '*** Unrecognized value for protectProto:',
5875bd8deadSopenharmony_ci                                    self.genOpts.protectProto,
5885bd8deadSopenharmony_ci                                    'not generating prototype wrappers')
5895bd8deadSopenharmony_ci                    prefix = ''
5905bd8deadSopenharmony_ci                    suffix = ''
5915bd8deadSopenharmony_ci
5925bd8deadSopenharmony_ci                write(prefix + self.cmdBody + suffix, end='', file=self.outFile)
5935bd8deadSopenharmony_ci            if (self.featureExtraProtect != None):
5945bd8deadSopenharmony_ci                write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
5955bd8deadSopenharmony_ci            if (self.genOpts.protectFeature):
5965bd8deadSopenharmony_ci                write('#endif /*', self.featureName, '*/', file=self.outFile)
5975bd8deadSopenharmony_ci        # Finish processing in superclass
5985bd8deadSopenharmony_ci        OutputGenerator.endFeature(self)
5995bd8deadSopenharmony_ci    #
6005bd8deadSopenharmony_ci    # Type generation
6015bd8deadSopenharmony_ci    def genType(self, typeinfo, name):
6025bd8deadSopenharmony_ci        OutputGenerator.genType(self, typeinfo, name)
6035bd8deadSopenharmony_ci        #
6045bd8deadSopenharmony_ci        # Replace <apientry /> tags with an APIENTRY-style string
6055bd8deadSopenharmony_ci        # (from self.genOpts). Copy other text through unchanged.
6065bd8deadSopenharmony_ci        # If the resulting text is an empty string, don't emit it.
6075bd8deadSopenharmony_ci        typeElem = typeinfo.elem
6085bd8deadSopenharmony_ci        s = noneStr(typeElem.text)
6095bd8deadSopenharmony_ci        for elem in typeElem:
6105bd8deadSopenharmony_ci            if (elem.tag == 'apientry'):
6115bd8deadSopenharmony_ci                s += self.genOpts.apientry + noneStr(elem.tail)
6125bd8deadSopenharmony_ci            else:
6135bd8deadSopenharmony_ci                s += noneStr(elem.text) + noneStr(elem.tail)
6145bd8deadSopenharmony_ci        if (len(s) > 0):
6155bd8deadSopenharmony_ci            self.typeBody += s + '\n'
6165bd8deadSopenharmony_ci    #
6175bd8deadSopenharmony_ci    # Enumerant generation
6185bd8deadSopenharmony_ci    def genEnum(self, enuminfo, name):
6195bd8deadSopenharmony_ci        OutputGenerator.genEnum(self, enuminfo, name)
6205bd8deadSopenharmony_ci        #
6215bd8deadSopenharmony_ci        # EnumInfo.type is a C value suffix (e.g. u, ull)
6225bd8deadSopenharmony_ci        self.enumBody += '#define ' + name.ljust(33) + ' ' + enuminfo.elem.get('value')
6235bd8deadSopenharmony_ci        #
6245bd8deadSopenharmony_ci        # Handle non-integer 'type' fields by using it as the C value suffix
6255bd8deadSopenharmony_ci        t = enuminfo.elem.get('type')
6265bd8deadSopenharmony_ci        if (t != '' and t != 'i'):
6275bd8deadSopenharmony_ci            self.enumBody += enuminfo.type
6285bd8deadSopenharmony_ci        self.enumBody += '\n'
6295bd8deadSopenharmony_ci    #
6305bd8deadSopenharmony_ci    # Command generation
6315bd8deadSopenharmony_ci    def genCmd(self, cmdinfo, name):
6325bd8deadSopenharmony_ci        OutputGenerator.genCmd(self, cmdinfo, name)
6335bd8deadSopenharmony_ci        #
6345bd8deadSopenharmony_ci        decls = self.makeCDecls(cmdinfo.elem)
6355bd8deadSopenharmony_ci        self.cmdBody += decls[0]
6365bd8deadSopenharmony_ci        if (self.genOpts.genFuncPointers):
6375bd8deadSopenharmony_ci            self.cmdPointerBody += decls[1]
6385bd8deadSopenharmony_ci
6395bd8deadSopenharmony_ci# Registry - object representing an API registry, loaded from an XML file
6405bd8deadSopenharmony_ci# Members
6415bd8deadSopenharmony_ci#   tree - ElementTree containing the root <registry>
6425bd8deadSopenharmony_ci#   typedict - dictionary of TypeInfo objects keyed by type name
6435bd8deadSopenharmony_ci#   groupdict - dictionary of GroupInfo objects keyed by group name
6445bd8deadSopenharmony_ci#   enumdict - dictionary of EnumInfo objects keyed by enum name
6455bd8deadSopenharmony_ci#   cmddict - dictionary of CmdInfo objects keyed by command name
6465bd8deadSopenharmony_ci#   apidict - dictionary of <api> Elements keyed by API name
6475bd8deadSopenharmony_ci#   extensions - list of <extension> Elements
6485bd8deadSopenharmony_ci#   extdict - dictionary of <extension> Elements keyed by extension name
6495bd8deadSopenharmony_ci#   gen - OutputGenerator object used to write headers / messages
6505bd8deadSopenharmony_ci#   genOpts - GeneratorOptions object used to control which
6515bd8deadSopenharmony_ci#     fetures to write and how to format them
6525bd8deadSopenharmony_ci#   emitFeatures - True to actually emit features for a version / extension,
6535bd8deadSopenharmony_ci#     or False to just treat them as emitted
6545bd8deadSopenharmony_ci# Public methods
6555bd8deadSopenharmony_ci#   loadElementTree(etree) - load registry from specified ElementTree
6565bd8deadSopenharmony_ci#   loadFile(filename) - load registry from XML file
6575bd8deadSopenharmony_ci#   setGenerator(gen) - OutputGenerator to use
6585bd8deadSopenharmony_ci#   parseTree() - parse the registry once loaded & create dictionaries
6595bd8deadSopenharmony_ci#   dumpReg(maxlen, filehandle) - diagnostic to dump the dictionaries
6605bd8deadSopenharmony_ci#     to specified file handle (default stdout). Truncates type /
6615bd8deadSopenharmony_ci#     enum / command elements to maxlen characters (default 80)
6625bd8deadSopenharmony_ci#   generator(g) - specify the output generator object
6635bd8deadSopenharmony_ci#   apiGen(apiname, genOpts) - generate API headers for the API type
6645bd8deadSopenharmony_ci#     and profile specified in genOpts, but only for the versions and
6655bd8deadSopenharmony_ci#     extensions specified there.
6665bd8deadSopenharmony_ci#   apiReset() - call between calls to apiGen() to reset internal state
6675bd8deadSopenharmony_ci#   validateGroups() - call to verify that each <proto> or <param>
6685bd8deadSopenharmony_ci#     with a 'group' attribute matches an actual existing group.
6695bd8deadSopenharmony_ci# Private methods
6705bd8deadSopenharmony_ci#   addElementInfo(elem,info,infoName,dictionary) - add feature info to dict
6715bd8deadSopenharmony_ci#   lookupElementInfo(fname,dictionary) - lookup feature info in dict
6725bd8deadSopenharmony_ciclass Registry:
6735bd8deadSopenharmony_ci    """Represents an API registry loaded from XML"""
6745bd8deadSopenharmony_ci    def __init__(self):
6755bd8deadSopenharmony_ci        self.tree         = None
6765bd8deadSopenharmony_ci        self.typedict     = {}
6775bd8deadSopenharmony_ci        self.groupdict    = {}
6785bd8deadSopenharmony_ci        self.enumdict     = {}
6795bd8deadSopenharmony_ci        self.cmddict      = {}
6805bd8deadSopenharmony_ci        self.apidict      = {}
6815bd8deadSopenharmony_ci        self.extensions   = []
6825bd8deadSopenharmony_ci        self.extdict      = {}
6835bd8deadSopenharmony_ci        # A default output generator, so commands prior to apiGen can report
6845bd8deadSopenharmony_ci        # errors via the generator object.
6855bd8deadSopenharmony_ci        self.gen          = OutputGenerator()
6865bd8deadSopenharmony_ci        self.genOpts      = None
6875bd8deadSopenharmony_ci        self.emitFeatures = False
6885bd8deadSopenharmony_ci    def loadElementTree(self, tree):
6895bd8deadSopenharmony_ci        """Load ElementTree into a Registry object and parse it"""
6905bd8deadSopenharmony_ci        self.tree = tree
6915bd8deadSopenharmony_ci        self.parseTree()
6925bd8deadSopenharmony_ci    def loadFile(self, file):
6935bd8deadSopenharmony_ci        """Load an API registry XML file into a Registry object and parse it"""
6945bd8deadSopenharmony_ci        self.tree = etree.parse(file)
6955bd8deadSopenharmony_ci        self.parseTree()
6965bd8deadSopenharmony_ci    def setGenerator(self, gen):
6975bd8deadSopenharmony_ci        """Specify output generator object. None restores the default generator"""
6985bd8deadSopenharmony_ci        self.gen = gen
6995bd8deadSopenharmony_ci    # addElementInfo - add information about an element to the
7005bd8deadSopenharmony_ci    # corresponding dictionary
7015bd8deadSopenharmony_ci    #   elem - <type>/<group>/<enum>/<command>/<feature>/<extension> Element
7025bd8deadSopenharmony_ci    #   info - corresponding {Type|Group|Enum|Cmd|Feature}Info object
7035bd8deadSopenharmony_ci    #   infoName - 'type' / 'group' / 'enum' / 'command' / 'feature' / 'extension'
7045bd8deadSopenharmony_ci    #   dictionary - self.{type|group|enum|cmd|api|ext}dict
7055bd8deadSopenharmony_ci    # If the Element has an 'api' attribute, the dictionary key is the
7065bd8deadSopenharmony_ci    # tuple (name,api). If not, the key is the name. 'name' is an
7075bd8deadSopenharmony_ci    # attribute of the Element
7085bd8deadSopenharmony_ci    def addElementInfo(self, elem, info, infoName, dictionary):
7095bd8deadSopenharmony_ci        if ('api' in elem.attrib):
7105bd8deadSopenharmony_ci            key = (elem.get('name'),elem.get('api'))
7115bd8deadSopenharmony_ci        else:
7125bd8deadSopenharmony_ci            key = elem.get('name')
7135bd8deadSopenharmony_ci        if key in dictionary:
7145bd8deadSopenharmony_ci            self.gen.logMsg('warn', '*** Attempt to redefine',
7155bd8deadSopenharmony_ci                            infoName, 'with key:', key)
7165bd8deadSopenharmony_ci        else:
7175bd8deadSopenharmony_ci            dictionary[key] = info
7185bd8deadSopenharmony_ci    #
7195bd8deadSopenharmony_ci    # lookupElementInfo - find a {Type|Enum|Cmd}Info object by name.
7205bd8deadSopenharmony_ci    # If an object qualified by API name exists, use that.
7215bd8deadSopenharmony_ci    #   fname - name of type / enum / command
7225bd8deadSopenharmony_ci    #   dictionary - self.{type|enum|cmd}dict
7235bd8deadSopenharmony_ci    def lookupElementInfo(self, fname, dictionary):
7245bd8deadSopenharmony_ci        key = (fname, self.genOpts.apiname)
7255bd8deadSopenharmony_ci        if (key in dictionary):
7265bd8deadSopenharmony_ci            # self.gen.logMsg('diag', 'Found API-specific element for feature', fname)
7275bd8deadSopenharmony_ci            return dictionary[key]
7285bd8deadSopenharmony_ci        elif (fname in dictionary):
7295bd8deadSopenharmony_ci            # self.gen.logMsg('diag', 'Found generic element for feature', fname)
7305bd8deadSopenharmony_ci            return dictionary[fname]
7315bd8deadSopenharmony_ci        else:
7325bd8deadSopenharmony_ci            return None
7335bd8deadSopenharmony_ci    def parseTree(self):
7345bd8deadSopenharmony_ci        """Parse the registry Element, once created"""
7355bd8deadSopenharmony_ci        # This must be the Element for the root <registry>
7365bd8deadSopenharmony_ci        self.reg = self.tree.getroot()
7375bd8deadSopenharmony_ci        #
7385bd8deadSopenharmony_ci        # Create dictionary of registry types from toplevel <types> tags
7395bd8deadSopenharmony_ci        # and add 'name' attribute to each <type> tag (where missing)
7405bd8deadSopenharmony_ci        # based on its <name> element.
7415bd8deadSopenharmony_ci        #
7425bd8deadSopenharmony_ci        # There's usually one <types> block; more are OK
7435bd8deadSopenharmony_ci        # Required <type> attributes: 'name' or nested <name> tag contents
7445bd8deadSopenharmony_ci        self.typedict = {}
7455bd8deadSopenharmony_ci        for type in self.reg.findall('types/type'):
7465bd8deadSopenharmony_ci            # If the <type> doesn't already have a 'name' attribute, set
7475bd8deadSopenharmony_ci            # it from contents of its <name> tag.
7485bd8deadSopenharmony_ci            if (type.get('name') == None):
7495bd8deadSopenharmony_ci                type.attrib['name'] = type.find('name').text
7505bd8deadSopenharmony_ci            self.addElementInfo(type, TypeInfo(type), 'type', self.typedict)
7515bd8deadSopenharmony_ci        #
7525bd8deadSopenharmony_ci        # Create dictionary of registry groups from toplevel <groups> tags.
7535bd8deadSopenharmony_ci        #
7545bd8deadSopenharmony_ci        # There's usually one <groups> block; more are OK.
7555bd8deadSopenharmony_ci        # Required <group> attributes: 'name'
7565bd8deadSopenharmony_ci        self.groupdict = {}
7575bd8deadSopenharmony_ci        for group in self.reg.findall('groups/group'):
7585bd8deadSopenharmony_ci            self.addElementInfo(group, GroupInfo(group), 'group', self.groupdict)
7595bd8deadSopenharmony_ci        #
7605bd8deadSopenharmony_ci        # Create dictionary of registry enums from toplevel <enums> tags
7615bd8deadSopenharmony_ci        #
7625bd8deadSopenharmony_ci        # There are usually many <enums> tags in different namespaces, but
7635bd8deadSopenharmony_ci        #   these are functional namespaces of the values, while the actual
7645bd8deadSopenharmony_ci        #   enum names all share the dictionary.
7655bd8deadSopenharmony_ci        # Required <enums> attributes: 'name', 'value'
7665bd8deadSopenharmony_ci        self.enumdict = {}
7675bd8deadSopenharmony_ci        for enum in self.reg.findall('enums/enum'):
7685bd8deadSopenharmony_ci            self.addElementInfo(enum, EnumInfo(enum), 'enum', self.enumdict)
7695bd8deadSopenharmony_ci        #
7705bd8deadSopenharmony_ci        # Create dictionary of registry commands from <command> tags
7715bd8deadSopenharmony_ci        # and add 'name' attribute to each <command> tag (where missing)
7725bd8deadSopenharmony_ci        # based on its <proto><name> element.
7735bd8deadSopenharmony_ci        #
7745bd8deadSopenharmony_ci        # There's usually only one <commands> block; more are OK.
7755bd8deadSopenharmony_ci        # Required <command> attributes: 'name' or <proto><name> tag contents
7765bd8deadSopenharmony_ci        self.cmddict = {}
7775bd8deadSopenharmony_ci        for cmd in self.reg.findall('commands/command'):
7785bd8deadSopenharmony_ci            # If the <command> doesn't already have a 'name' attribute, set
7795bd8deadSopenharmony_ci            # it from contents of its <proto><name> tag.
7805bd8deadSopenharmony_ci            if (cmd.get('name') == None):
7815bd8deadSopenharmony_ci                cmd.attrib['name'] = cmd.find('proto/name').text
7825bd8deadSopenharmony_ci            ci = CmdInfo(cmd)
7835bd8deadSopenharmony_ci            self.addElementInfo(cmd, ci, 'command', self.cmddict)
7845bd8deadSopenharmony_ci        #
7855bd8deadSopenharmony_ci        # Create dictionaries of API and extension interfaces
7865bd8deadSopenharmony_ci        #   from toplevel <api> and <extension> tags.
7875bd8deadSopenharmony_ci        #
7885bd8deadSopenharmony_ci        self.apidict = {}
7895bd8deadSopenharmony_ci        for feature in self.reg.findall('feature'):
7905bd8deadSopenharmony_ci            ai = FeatureInfo(feature)
7915bd8deadSopenharmony_ci            self.addElementInfo(feature, ai, 'feature', self.apidict)
7925bd8deadSopenharmony_ci        self.extensions = self.reg.findall('extensions/extension')
7935bd8deadSopenharmony_ci        self.extdict = {}
7945bd8deadSopenharmony_ci        for feature in self.extensions:
7955bd8deadSopenharmony_ci            ei = FeatureInfo(feature)
7965bd8deadSopenharmony_ci            self.addElementInfo(feature, ei, 'extension', self.extdict)
7975bd8deadSopenharmony_ci    def dumpReg(self, maxlen = 40, filehandle = sys.stdout):
7985bd8deadSopenharmony_ci        """Dump all the dictionaries constructed from the Registry object"""
7995bd8deadSopenharmony_ci        write('***************************************', file=filehandle)
8005bd8deadSopenharmony_ci        write('    ** Dumping Registry contents **',     file=filehandle)
8015bd8deadSopenharmony_ci        write('***************************************', file=filehandle)
8025bd8deadSopenharmony_ci        write('// Types', file=filehandle)
8035bd8deadSopenharmony_ci        for name in self.typedict:
8045bd8deadSopenharmony_ci            tobj = self.typedict[name]
8055bd8deadSopenharmony_ci            write('    Type', name, '->', etree.tostring(tobj.elem)[0:maxlen], file=filehandle)
8065bd8deadSopenharmony_ci        write('// Groups', file=filehandle)
8075bd8deadSopenharmony_ci        for name in self.groupdict:
8085bd8deadSopenharmony_ci            gobj = self.groupdict[name]
8095bd8deadSopenharmony_ci            write('    Group', name, '->', etree.tostring(gobj.elem)[0:maxlen], file=filehandle)
8105bd8deadSopenharmony_ci        write('// Enums', file=filehandle)
8115bd8deadSopenharmony_ci        for name in self.enumdict:
8125bd8deadSopenharmony_ci            eobj = self.enumdict[name]
8135bd8deadSopenharmony_ci            write('    Enum', name, '->', etree.tostring(eobj.elem)[0:maxlen], file=filehandle)
8145bd8deadSopenharmony_ci        write('// Commands', file=filehandle)
8155bd8deadSopenharmony_ci        for name in self.cmddict:
8165bd8deadSopenharmony_ci            cobj = self.cmddict[name]
8175bd8deadSopenharmony_ci            write('    Command', name, '->', etree.tostring(cobj.elem)[0:maxlen], file=filehandle)
8185bd8deadSopenharmony_ci        write('// APIs', file=filehandle)
8195bd8deadSopenharmony_ci        for key in self.apidict:
8205bd8deadSopenharmony_ci            write('    API Version ', key, '->',
8215bd8deadSopenharmony_ci                etree.tostring(self.apidict[key].elem)[0:maxlen], file=filehandle)
8225bd8deadSopenharmony_ci        write('// Extensions', file=filehandle)
8235bd8deadSopenharmony_ci        for key in self.extdict:
8245bd8deadSopenharmony_ci            write('    Extension', key, '->',
8255bd8deadSopenharmony_ci                etree.tostring(self.extdict[key].elem)[0:maxlen], file=filehandle)
8265bd8deadSopenharmony_ci        # write('***************************************', file=filehandle)
8275bd8deadSopenharmony_ci        # write('    ** Dumping XML ElementTree **', file=filehandle)
8285bd8deadSopenharmony_ci        # write('***************************************', file=filehandle)
8295bd8deadSopenharmony_ci        # write(etree.tostring(self.tree.getroot(),pretty_print=True), file=filehandle)
8305bd8deadSopenharmony_ci    #
8315bd8deadSopenharmony_ci    # typename - name of type
8325bd8deadSopenharmony_ci    # required - boolean (to tag features as required or not)
8335bd8deadSopenharmony_ci    def markTypeRequired(self, typename, required):
8345bd8deadSopenharmony_ci        """Require (along with its dependencies) or remove (but not its dependencies) a type"""
8355bd8deadSopenharmony_ci        self.gen.logMsg('diag', '*** tagging type:', typename, '-> required =', required)
8365bd8deadSopenharmony_ci        # Get TypeInfo object for <type> tag corresponding to typename
8375bd8deadSopenharmony_ci        type = self.lookupElementInfo(typename, self.typedict)
8385bd8deadSopenharmony_ci        if (type != None):
8395bd8deadSopenharmony_ci            # Tag required type dependencies as required.
8405bd8deadSopenharmony_ci            # This DOES NOT un-tag dependencies in a <remove> tag.
8415bd8deadSopenharmony_ci            # See comments in markRequired() below for the reason.
8425bd8deadSopenharmony_ci            if (required and ('requires' in type.elem.attrib)):
8435bd8deadSopenharmony_ci                depType = type.elem.get('requires')
8445bd8deadSopenharmony_ci                self.gen.logMsg('diag', '*** Generating dependent type',
8455bd8deadSopenharmony_ci                    depType, 'for type', typename)
8465bd8deadSopenharmony_ci                self.markTypeRequired(depType, required)
8475bd8deadSopenharmony_ci            type.required = required
8485bd8deadSopenharmony_ci        else:
8495bd8deadSopenharmony_ci            self.gen.logMsg('warn', '*** type:', typename , 'IS NOT DEFINED')
8505bd8deadSopenharmony_ci    #
8515bd8deadSopenharmony_ci    # features - Element for <require> or <remove> tag
8525bd8deadSopenharmony_ci    # required - boolean (to tag features as required or not)
8535bd8deadSopenharmony_ci    def markRequired(self, features, required):
8545bd8deadSopenharmony_ci        """Require or remove features specified in the Element"""
8555bd8deadSopenharmony_ci        self.gen.logMsg('diag', '*** markRequired (features = <too long to print>, required =', required, ')')
8565bd8deadSopenharmony_ci        # Loop over types, enums, and commands in the tag
8575bd8deadSopenharmony_ci        # @@ It would be possible to respect 'api' and 'profile' attributes
8585bd8deadSopenharmony_ci        #  in individual features, but that's not done yet.
8595bd8deadSopenharmony_ci        for typeElem in features.findall('type'):
8605bd8deadSopenharmony_ci            self.markTypeRequired(typeElem.get('name'), required)
8615bd8deadSopenharmony_ci        for enumElem in features.findall('enum'):
8625bd8deadSopenharmony_ci            name = enumElem.get('name')
8635bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** tagging enum:', name, '-> required =', required)
8645bd8deadSopenharmony_ci            enum = self.lookupElementInfo(name, self.enumdict)
8655bd8deadSopenharmony_ci            if (enum != None):
8665bd8deadSopenharmony_ci                enum.required = required
8675bd8deadSopenharmony_ci            else:
8685bd8deadSopenharmony_ci                self.gen.logMsg('warn', '*** enum:', name , 'IS NOT DEFINED')
8695bd8deadSopenharmony_ci        for cmdElem in features.findall('command'):
8705bd8deadSopenharmony_ci            name = cmdElem.get('name')
8715bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** tagging command:', name, '-> required =', required)
8725bd8deadSopenharmony_ci            cmd = self.lookupElementInfo(name, self.cmddict)
8735bd8deadSopenharmony_ci            if (cmd != None):
8745bd8deadSopenharmony_ci                cmd.required = required
8755bd8deadSopenharmony_ci                # Tag all parameter types of this command as required.
8765bd8deadSopenharmony_ci                # This DOES NOT remove types of commands in a <remove>
8775bd8deadSopenharmony_ci                # tag, because many other commands may use the same type.
8785bd8deadSopenharmony_ci                # We could be more clever and reference count types,
8795bd8deadSopenharmony_ci                # instead of using a boolean.
8805bd8deadSopenharmony_ci                if (required):
8815bd8deadSopenharmony_ci                    # Look for <ptype> in entire <command> tree,
8825bd8deadSopenharmony_ci                    # not just immediate children
8835bd8deadSopenharmony_ci                    for ptype in cmd.elem.findall('.//ptype'):
8845bd8deadSopenharmony_ci                        self.gen.logMsg('diag', '*** markRequired: command implicitly requires dependent type', ptype.text)
8855bd8deadSopenharmony_ci                        self.markTypeRequired(ptype.text, required)
8865bd8deadSopenharmony_ci            else:
8875bd8deadSopenharmony_ci                self.gen.logMsg('warn', '*** command:', name, 'IS NOT DEFINED')
8885bd8deadSopenharmony_ci    #
8895bd8deadSopenharmony_ci    # interface - Element for <version> or <extension>, containing
8905bd8deadSopenharmony_ci    #   <require> and <remove> tags
8915bd8deadSopenharmony_ci    # api - string specifying API name being generated
8925bd8deadSopenharmony_ci    # profile - string specifying API profile being generated
8935bd8deadSopenharmony_ci    def requireAndRemoveFeatures(self, interface, api, profile):
8945bd8deadSopenharmony_ci        """Process <recquire> and <remove> tags for a <version> or <extension>"""
8955bd8deadSopenharmony_ci        # <require> marks things that are required by this version/profile
8965bd8deadSopenharmony_ci        for feature in interface.findall('require'):
8975bd8deadSopenharmony_ci            if (matchAPIProfile(api, profile, feature)):
8985bd8deadSopenharmony_ci                self.markRequired(feature,True)
8995bd8deadSopenharmony_ci        # <remove> marks things that are removed by this version/profile
9005bd8deadSopenharmony_ci        for feature in interface.findall('remove'):
9015bd8deadSopenharmony_ci            if (matchAPIProfile(api, profile, feature)):
9025bd8deadSopenharmony_ci                self.markRequired(feature,False)
9035bd8deadSopenharmony_ci    #
9045bd8deadSopenharmony_ci    # generateFeature - generate a single type / enum / command,
9055bd8deadSopenharmony_ci    # and all its dependencies as needed.
9065bd8deadSopenharmony_ci    #   fname - name of feature (<type>/<enum>/<command>
9075bd8deadSopenharmony_ci    #   ftype - type of feature, 'type' | 'enum' | 'command'
9085bd8deadSopenharmony_ci    #   dictionary - of *Info objects - self.{type|enum|cmd}dict
9095bd8deadSopenharmony_ci    #   genProc - bound function pointer for self.gen.gen{Type|Enum|Cmd}
9105bd8deadSopenharmony_ci    def generateFeature(self, fname, ftype, dictionary, genProc):
9115bd8deadSopenharmony_ci        f = self.lookupElementInfo(fname, dictionary)
9125bd8deadSopenharmony_ci        if (f == None):
9135bd8deadSopenharmony_ci            # No such feature. This is an error, but reported earlier
9145bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** No entry found for feature', fname,
9155bd8deadSopenharmony_ci                            'returning!')
9165bd8deadSopenharmony_ci            return
9175bd8deadSopenharmony_ci        #
9185bd8deadSopenharmony_ci        # If feature isn't required, or has already been declared, return
9195bd8deadSopenharmony_ci        if (not f.required):
9205bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** Skipping', ftype, fname, '(not required)')
9215bd8deadSopenharmony_ci            return
9225bd8deadSopenharmony_ci        if (f.declared):
9235bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** Skipping', ftype, fname, '(already declared)')
9245bd8deadSopenharmony_ci            return
9255bd8deadSopenharmony_ci        #
9265bd8deadSopenharmony_ci        # Pull in dependent type declaration(s) of the feature.
9275bd8deadSopenharmony_ci        # For types, there may be one in the 'required' attribute of the element
9285bd8deadSopenharmony_ci        # For commands, there may be many in <ptype> tags within the element
9295bd8deadSopenharmony_ci        # For enums, no dependencies are allowed (though perhasps if you
9305bd8deadSopenharmony_ci        #   have a uint64 enum, it should require GLuint64)
9315bd8deadSopenharmony_ci        if (ftype == 'type'):
9325bd8deadSopenharmony_ci            if ('requires' in f.elem.attrib):
9335bd8deadSopenharmony_ci                depname = f.elem.get('requires')
9345bd8deadSopenharmony_ci                self.gen.logMsg('diag', '*** Generating required dependent type',
9355bd8deadSopenharmony_ci                                depname)
9365bd8deadSopenharmony_ci                self.generateFeature(depname, 'type', self.typedict,
9375bd8deadSopenharmony_ci                                     self.gen.genType)
9385bd8deadSopenharmony_ci        elif (ftype == 'command'):
9395bd8deadSopenharmony_ci            for ptype in f.elem.findall('.//ptype'):
9405bd8deadSopenharmony_ci                depname = ptype.text
9415bd8deadSopenharmony_ci                self.gen.logMsg('diag', '*** Generating required parameter type',
9425bd8deadSopenharmony_ci                                depname)
9435bd8deadSopenharmony_ci                self.generateFeature(depname, 'type', self.typedict,
9445bd8deadSopenharmony_ci                                     self.gen.genType)
9455bd8deadSopenharmony_ci        #
9465bd8deadSopenharmony_ci        # Actually generate the type only if emitting declarations
9475bd8deadSopenharmony_ci        if self.emitFeatures:
9485bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** Emitting', ftype, 'decl for', fname)
9495bd8deadSopenharmony_ci            genProc(f, fname)
9505bd8deadSopenharmony_ci        else:
9515bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** Skipping', ftype, fname,
9525bd8deadSopenharmony_ci                            '(not emitting this feature)')
9535bd8deadSopenharmony_ci        # Always mark feature declared, as though actually emitted
9545bd8deadSopenharmony_ci        f.declared = True
9555bd8deadSopenharmony_ci    #
9565bd8deadSopenharmony_ci    # generateRequiredInterface - generate all interfaces required
9575bd8deadSopenharmony_ci    # by an API version or extension
9585bd8deadSopenharmony_ci    #   interface - Element for <version> or <extension>
9595bd8deadSopenharmony_ci    def generateRequiredInterface(self, interface):
9605bd8deadSopenharmony_ci        """Generate required C interface for specified API version/extension"""
9615bd8deadSopenharmony_ci        #
9625bd8deadSopenharmony_ci        # Loop over all features inside all <require> tags.
9635bd8deadSopenharmony_ci        # <remove> tags are ignored (handled in pass 1).
9645bd8deadSopenharmony_ci        for features in interface.findall('require'):
9655bd8deadSopenharmony_ci            for t in features.findall('type'):
9665bd8deadSopenharmony_ci                self.generateFeature(t.get('name'), 'type', self.typedict,
9675bd8deadSopenharmony_ci                                     self.gen.genType)
9685bd8deadSopenharmony_ci            for e in features.findall('enum'):
9695bd8deadSopenharmony_ci                self.generateFeature(e.get('name'), 'enum', self.enumdict,
9705bd8deadSopenharmony_ci                                     self.gen.genEnum)
9715bd8deadSopenharmony_ci            for c in features.findall('command'):
9725bd8deadSopenharmony_ci                self.generateFeature(c.get('name'), 'command', self.cmddict,
9735bd8deadSopenharmony_ci                                     self.gen.genCmd)
9745bd8deadSopenharmony_ci    #
9755bd8deadSopenharmony_ci    # apiGen(genOpts) - generate interface for specified versions
9765bd8deadSopenharmony_ci    #   genOpts - GeneratorOptions object with parameters used
9775bd8deadSopenharmony_ci    #   by the Generator object.
9785bd8deadSopenharmony_ci    def apiGen(self, genOpts):
9795bd8deadSopenharmony_ci        """Generate interfaces for the specified API type and range of versions"""
9805bd8deadSopenharmony_ci        #
9815bd8deadSopenharmony_ci        self.gen.logMsg('diag', '*******************************************')
9825bd8deadSopenharmony_ci        self.gen.logMsg('diag', '  Registry.apiGen file:', genOpts.filename,
9835bd8deadSopenharmony_ci                        'api:', genOpts.apiname,
9845bd8deadSopenharmony_ci                        'profile:', genOpts.profile)
9855bd8deadSopenharmony_ci        self.gen.logMsg('diag', '*******************************************')
9865bd8deadSopenharmony_ci        #
9875bd8deadSopenharmony_ci        self.genOpts = genOpts
9885bd8deadSopenharmony_ci        # Reset required/declared flags for all features
9895bd8deadSopenharmony_ci        self.apiReset()
9905bd8deadSopenharmony_ci        #
9915bd8deadSopenharmony_ci        # Compile regexps used to select versions & extensions
9925bd8deadSopenharmony_ci        regVersions = re.compile(self.genOpts.versions)
9935bd8deadSopenharmony_ci        regEmitVersions = re.compile(self.genOpts.emitversions)
9945bd8deadSopenharmony_ci        regAddExtensions = re.compile(self.genOpts.addExtensions)
9955bd8deadSopenharmony_ci        regRemoveExtensions = re.compile(self.genOpts.removeExtensions)
9965bd8deadSopenharmony_ci        #
9975bd8deadSopenharmony_ci        # Get all matching API versions & add to list of FeatureInfo
9985bd8deadSopenharmony_ci        features = []
9995bd8deadSopenharmony_ci        apiMatch = False
10005bd8deadSopenharmony_ci        for key in self.apidict:
10015bd8deadSopenharmony_ci            fi = self.apidict[key]
10025bd8deadSopenharmony_ci            api = fi.elem.get('api')
10035bd8deadSopenharmony_ci            if (api == self.genOpts.apiname):
10045bd8deadSopenharmony_ci                apiMatch = True
10055bd8deadSopenharmony_ci                if (regVersions.match(fi.number)):
10065bd8deadSopenharmony_ci                    # Matches API & version #s being generated. Mark for
10075bd8deadSopenharmony_ci                    # emission and add to the features[] list .
10085bd8deadSopenharmony_ci                    # @@ Could use 'declared' instead of 'emit'?
10095bd8deadSopenharmony_ci                    fi.emit = (regEmitVersions.match(fi.number) != None)
10105bd8deadSopenharmony_ci                    features.append(fi)
10115bd8deadSopenharmony_ci                    if (not fi.emit):
10125bd8deadSopenharmony_ci                        self.gen.logMsg('diag', '*** NOT tagging feature api =', api,
10135bd8deadSopenharmony_ci                            'name =', fi.name, 'number =', fi.number,
10145bd8deadSopenharmony_ci                            'for emission (does not match emitversions pattern)')
10155bd8deadSopenharmony_ci                else:
10165bd8deadSopenharmony_ci                    self.gen.logMsg('diag', '*** NOT including feature api =', api,
10175bd8deadSopenharmony_ci                        'name =', fi.name, 'number =', fi.number,
10185bd8deadSopenharmony_ci                        '(does not match requested versions)')
10195bd8deadSopenharmony_ci            else:
10205bd8deadSopenharmony_ci                self.gen.logMsg('diag', '*** NOT including feature api =', api,
10215bd8deadSopenharmony_ci                    'name =', fi.name,
10225bd8deadSopenharmony_ci                    '(does not match requested API)')
10235bd8deadSopenharmony_ci        if (not apiMatch):
10245bd8deadSopenharmony_ci            self.gen.logMsg('warn', '*** No matching API versions found!')
10255bd8deadSopenharmony_ci        #
10265bd8deadSopenharmony_ci        # Get all matching extensions & add to the list.
10275bd8deadSopenharmony_ci        # Start with extensions tagged with 'api' pattern matching the API
10285bd8deadSopenharmony_ci        # being generated. Add extensions matching the pattern specified in
10295bd8deadSopenharmony_ci        # regExtensions, then remove extensions matching the pattern
10305bd8deadSopenharmony_ci        # specified in regRemoveExtensions
10315bd8deadSopenharmony_ci        for key in self.extdict:
10325bd8deadSopenharmony_ci            ei = self.extdict[key]
10335bd8deadSopenharmony_ci            extName = ei.name
10345bd8deadSopenharmony_ci            include = False
10355bd8deadSopenharmony_ci            #
10365bd8deadSopenharmony_ci            # Include extension if defaultExtensions is not None and if the
10375bd8deadSopenharmony_ci            # 'supported' attribute matches defaultExtensions. The regexp in
10385bd8deadSopenharmony_ci            # 'supported' must exactly match defaultExtensions, so bracket
10395bd8deadSopenharmony_ci            # it with ^(pat)$.
10405bd8deadSopenharmony_ci            pat = '^(' + ei.elem.get('supported') + ')$'
10415bd8deadSopenharmony_ci            if (self.genOpts.defaultExtensions and
10425bd8deadSopenharmony_ci                     re.match(pat, self.genOpts.defaultExtensions)):
10435bd8deadSopenharmony_ci                self.gen.logMsg('diag', '*** Including extension',
10445bd8deadSopenharmony_ci                    extName, "(defaultExtensions matches the 'supported' attribute)")
10455bd8deadSopenharmony_ci                include = True
10465bd8deadSopenharmony_ci            #
10475bd8deadSopenharmony_ci            # Include additional extensions if the extension name matches
10485bd8deadSopenharmony_ci            # the regexp specified in the generator options. This allows
10495bd8deadSopenharmony_ci            # forcing extensions into an interface even if they're not
10505bd8deadSopenharmony_ci            # tagged appropriately in the registry.
10515bd8deadSopenharmony_ci            if (regAddExtensions.match(extName) != None):
10525bd8deadSopenharmony_ci                self.gen.logMsg('diag', '*** Including extension',
10535bd8deadSopenharmony_ci                    extName, '(matches explicitly requested extensions to add)')
10545bd8deadSopenharmony_ci                include = True
10555bd8deadSopenharmony_ci            # Remove extensions if the name matches the regexp specified
10565bd8deadSopenharmony_ci            # in generator options. This allows forcing removal of
10575bd8deadSopenharmony_ci            # extensions from an interface even if they're tagged that
10585bd8deadSopenharmony_ci            # way in the registry.
10595bd8deadSopenharmony_ci            if (regRemoveExtensions.match(extName) != None):
10605bd8deadSopenharmony_ci                self.gen.logMsg('diag', '*** Removing extension',
10615bd8deadSopenharmony_ci                    extName, '(matches explicitly requested extensions to remove)')
10625bd8deadSopenharmony_ci                include = False
10635bd8deadSopenharmony_ci            #
10645bd8deadSopenharmony_ci            # If the extension is to be included, add it to the
10655bd8deadSopenharmony_ci            # extension features list.
10665bd8deadSopenharmony_ci            if (include):
10675bd8deadSopenharmony_ci                ei.emit = True
10685bd8deadSopenharmony_ci                features.append(ei)
10695bd8deadSopenharmony_ci            else:
10705bd8deadSopenharmony_ci                self.gen.logMsg('diag', '*** NOT including extension',
10715bd8deadSopenharmony_ci                    extName, '(does not match api attribute or explicitly requested extensions)')
10725bd8deadSopenharmony_ci        #
10735bd8deadSopenharmony_ci        # Sort the extension features list, if a sort procedure is defined
10745bd8deadSopenharmony_ci        if (self.genOpts.sortProcedure):
10755bd8deadSopenharmony_ci            self.genOpts.sortProcedure(features)
10765bd8deadSopenharmony_ci        #
10775bd8deadSopenharmony_ci        # Pass 1: loop over requested API versions and extensions tagging
10785bd8deadSopenharmony_ci        #   types/commands/features as required (in an <require> block) or no
10795bd8deadSopenharmony_ci        #   longer required (in an <exclude> block). It is possible to remove
10805bd8deadSopenharmony_ci        #   a feature in one version and restore it later by requiring it in
10815bd8deadSopenharmony_ci        #   a later version.
10825bd8deadSopenharmony_ci        # If a profile other than 'None' is being generated, it must
10835bd8deadSopenharmony_ci        #   match the profile attribute (if any) of the <require> and
10845bd8deadSopenharmony_ci        #   <remove> tags.
10855bd8deadSopenharmony_ci        self.gen.logMsg('diag', '*** PASS 1: TAG FEATURES ********************************************')
10865bd8deadSopenharmony_ci        for f in features:
10875bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** PASS 1: Tagging required and removed features for',
10885bd8deadSopenharmony_ci                f.name)
10895bd8deadSopenharmony_ci            self.requireAndRemoveFeatures(f.elem, self.genOpts.apiname, self.genOpts.profile)
10905bd8deadSopenharmony_ci        #
10915bd8deadSopenharmony_ci        # Pass 2: loop over specified API versions and extensions printing
10925bd8deadSopenharmony_ci        #   declarations for required things which haven't already been
10935bd8deadSopenharmony_ci        #   generated.
10945bd8deadSopenharmony_ci        self.gen.logMsg('diag', '*** PASS 2: GENERATE INTERFACES FOR FEATURES ************************')
10955bd8deadSopenharmony_ci        self.gen.beginFile(self.genOpts)
10965bd8deadSopenharmony_ci        for f in features:
10975bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** PASS 2: Generating interface for',
10985bd8deadSopenharmony_ci                f.name)
10995bd8deadSopenharmony_ci            emit = self.emitFeatures = f.emit
11005bd8deadSopenharmony_ci            if (not emit):
11015bd8deadSopenharmony_ci                self.gen.logMsg('diag', '*** PASS 2: NOT declaring feature',
11025bd8deadSopenharmony_ci                    f.elem.get('name'), 'because it is not tagged for emission')
11035bd8deadSopenharmony_ci            # Generate the interface (or just tag its elements as having been
11045bd8deadSopenharmony_ci            # emitted, if they haven't been).
11055bd8deadSopenharmony_ci            self.gen.beginFeature(f.elem, emit)
11065bd8deadSopenharmony_ci            self.generateRequiredInterface(f.elem)
11075bd8deadSopenharmony_ci            self.gen.endFeature()
11085bd8deadSopenharmony_ci        self.gen.endFile()
11095bd8deadSopenharmony_ci    #
11105bd8deadSopenharmony_ci    # apiReset - use between apiGen() calls to reset internal state
11115bd8deadSopenharmony_ci    #
11125bd8deadSopenharmony_ci    def apiReset(self):
11135bd8deadSopenharmony_ci        """Reset type/enum/command dictionaries before generating another API"""
11145bd8deadSopenharmony_ci        for type in self.typedict:
11155bd8deadSopenharmony_ci            self.typedict[type].resetState()
11165bd8deadSopenharmony_ci        for enum in self.enumdict:
11175bd8deadSopenharmony_ci            self.enumdict[enum].resetState()
11185bd8deadSopenharmony_ci        for cmd in self.cmddict:
11195bd8deadSopenharmony_ci            self.cmddict[cmd].resetState()
11205bd8deadSopenharmony_ci        for cmd in self.apidict:
11215bd8deadSopenharmony_ci            self.apidict[cmd].resetState()
11225bd8deadSopenharmony_ci    #
11235bd8deadSopenharmony_ci    # validateGroups - check that group= attributes match actual groups
11245bd8deadSopenharmony_ci    #
11255bd8deadSopenharmony_ci    def validateGroups(self):
11265bd8deadSopenharmony_ci        """Validate group= attributes on <param> and <proto> tags"""
11275bd8deadSopenharmony_ci        # Keep track of group names not in <group> tags
11285bd8deadSopenharmony_ci        badGroup = {}
11295bd8deadSopenharmony_ci        self.gen.logMsg('diag', '*** VALIDATING GROUP ATTRIBUTES ***')
11305bd8deadSopenharmony_ci        for cmd in self.reg.findall('commands/command'):
11315bd8deadSopenharmony_ci            proto = cmd.find('proto')
11325bd8deadSopenharmony_ci            funcname = cmd.find('proto/name').text
11335bd8deadSopenharmony_ci            if ('group' in proto.attrib.keys()):
11345bd8deadSopenharmony_ci                group = proto.get('group')
11355bd8deadSopenharmony_ci                # self.gen.logMsg('diag', '*** Command ', funcname, ' has return group ', group)
11365bd8deadSopenharmony_ci                if (group not in self.groupdict.keys()):
11375bd8deadSopenharmony_ci                    # self.gen.logMsg('diag', '*** Command ', funcname, ' has UNKNOWN return group ', group)
11385bd8deadSopenharmony_ci                    if (group not in badGroup.keys()):
11395bd8deadSopenharmony_ci                        badGroup[group] = 1
11405bd8deadSopenharmony_ci                    else:
11415bd8deadSopenharmony_ci                        badGroup[group] = badGroup[group] +  1
11425bd8deadSopenharmony_ci            for param in cmd.findall('param'):
11435bd8deadSopenharmony_ci                pname = param.find('name')
11445bd8deadSopenharmony_ci                if (pname != None):
11455bd8deadSopenharmony_ci                    pname = pname.text
11465bd8deadSopenharmony_ci                else:
11475bd8deadSopenharmony_ci                    pname = type.get('name')
11485bd8deadSopenharmony_ci                if ('group' in param.attrib.keys()):
11495bd8deadSopenharmony_ci                    group = param.get('group')
11505bd8deadSopenharmony_ci                    if (group not in self.groupdict.keys()):
11515bd8deadSopenharmony_ci                        # self.gen.logMsg('diag', '*** Command ', funcname, ' param ', pname, ' has UNKNOWN group ', group)
11525bd8deadSopenharmony_ci                        if (group not in badGroup.keys()):
11535bd8deadSopenharmony_ci                            badGroup[group] = 1
11545bd8deadSopenharmony_ci                        else:
11555bd8deadSopenharmony_ci                            badGroup[group] = badGroup[group] +  1
11565bd8deadSopenharmony_ci        if (len(badGroup.keys()) > 0):
11575bd8deadSopenharmony_ci            self.gen.logMsg('diag', '*** SUMMARY OF UNRECOGNIZED GROUPS ***')
11585bd8deadSopenharmony_ci            for key in sorted(badGroup.keys()):
11595bd8deadSopenharmony_ci                self.gen.logMsg('diag', '    ', key, ' occurred ', badGroup[key], ' times')
1160