xref: /aosp_15_r20/external/deqp/scripts/khr_util/registry.py (revision 35238bce31c2a825756842865a792f8cf7f89930)
1*35238bceSAndroid Build Coastguard Worker# -*- coding: utf-8 -*-
2*35238bceSAndroid Build Coastguard Worker
3*35238bceSAndroid Build Coastguard Worker#-------------------------------------------------------------------------
4*35238bceSAndroid Build Coastguard Worker# drawElements Quality Program utilities
5*35238bceSAndroid Build Coastguard Worker# --------------------------------------
6*35238bceSAndroid Build Coastguard Worker#
7*35238bceSAndroid Build Coastguard Worker# Copyright 2015 The Android Open Source Project
8*35238bceSAndroid Build Coastguard Worker#
9*35238bceSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
10*35238bceSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
11*35238bceSAndroid Build Coastguard Worker# You may obtain a copy of the License at
12*35238bceSAndroid Build Coastguard Worker#
13*35238bceSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
14*35238bceSAndroid Build Coastguard Worker#
15*35238bceSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
16*35238bceSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
17*35238bceSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18*35238bceSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
19*35238bceSAndroid Build Coastguard Worker# limitations under the License.
20*35238bceSAndroid Build Coastguard Worker#
21*35238bceSAndroid Build Coastguard Worker#-------------------------------------------------------------------------
22*35238bceSAndroid Build Coastguard Worker
23*35238bceSAndroid Build Coastguard Workerimport sys, logging, re
24*35238bceSAndroid Build Coastguard Workerfrom lxml import etree
25*35238bceSAndroid Build Coastguard Workerfrom collections import OrderedDict
26*35238bceSAndroid Build Coastguard Workerfrom functools import wraps, partial
27*35238bceSAndroid Build Coastguard Worker
28*35238bceSAndroid Build Coastguard Workerlog = logging.getLogger(__name__)
29*35238bceSAndroid Build Coastguard Worker
30*35238bceSAndroid Build Coastguard Workerdebug = log.debug
31*35238bceSAndroid Build Coastguard Workerinfo = log.info
32*35238bceSAndroid Build Coastguard Workerwarning = log.warning
33*35238bceSAndroid Build Coastguard Worker
34*35238bceSAndroid Build Coastguard Workerdef warnElem(elem, fmt, *args):
35*35238bceSAndroid Build Coastguard Worker    warning('%s:%d, %s %s: ' + fmt, elem.base, elem.sourceline, elem.tag, elem.get('name') or '', *args)
36*35238bceSAndroid Build Coastguard Worker
37*35238bceSAndroid Build Coastguard Workerclass Object(object):
38*35238bceSAndroid Build Coastguard Worker    def __init__(self, **kwargs):
39*35238bceSAndroid Build Coastguard Worker        self.__dict__.update(kwargs)
40*35238bceSAndroid Build Coastguard Worker
41*35238bceSAndroid Build Coastguard Workerclass Located(Object):
42*35238bceSAndroid Build Coastguard Worker    location = None
43*35238bceSAndroid Build Coastguard Worker
44*35238bceSAndroid Build Coastguard Workerclass Group(Located): pass
45*35238bceSAndroid Build Coastguard Workerclass Enum(Located): pass
46*35238bceSAndroid Build Coastguard Workerclass Enums(Located):
47*35238bceSAndroid Build Coastguard Worker    name = None
48*35238bceSAndroid Build Coastguard Worker    comment = None
49*35238bceSAndroid Build Coastguard Worker    enums = None
50*35238bceSAndroid Build Coastguard Worker
51*35238bceSAndroid Build Coastguard Workerclass Type(Located):
52*35238bceSAndroid Build Coastguard Worker    location = None
53*35238bceSAndroid Build Coastguard Worker    name=None
54*35238bceSAndroid Build Coastguard Worker    definition=None
55*35238bceSAndroid Build Coastguard Worker    api=None
56*35238bceSAndroid Build Coastguard Worker    requires=None
57*35238bceSAndroid Build Coastguard Worker
58*35238bceSAndroid Build Coastguard Workerdef makeObject(cls, elem, **kwargs):
59*35238bceSAndroid Build Coastguard Worker    kwargs.setdefault('name', elem.get('name'))
60*35238bceSAndroid Build Coastguard Worker    kwargs.setdefault('comment', elem.get('comment'))
61*35238bceSAndroid Build Coastguard Worker    kwargs['location'] = (elem.base, elem.sourceline)
62*35238bceSAndroid Build Coastguard Worker    return cls(**kwargs)
63*35238bceSAndroid Build Coastguard Worker
64*35238bceSAndroid Build Coastguard Workerdef parseEnum(eEnum):
65*35238bceSAndroid Build Coastguard Worker    return makeObject(
66*35238bceSAndroid Build Coastguard Worker        Enum, eEnum,
67*35238bceSAndroid Build Coastguard Worker        value=eEnum.get('value'),
68*35238bceSAndroid Build Coastguard Worker        type=eEnum.get('type'),
69*35238bceSAndroid Build Coastguard Worker        alias=eEnum.get('alias'))
70*35238bceSAndroid Build Coastguard Worker
71*35238bceSAndroid Build Coastguard Workerclass Param(Located): pass
72*35238bceSAndroid Build Coastguard Worker
73*35238bceSAndroid Build Coastguard Workerclass Command(Located):
74*35238bceSAndroid Build Coastguard Worker    name=None
75*35238bceSAndroid Build Coastguard Worker    declaration=None
76*35238bceSAndroid Build Coastguard Worker    type=None
77*35238bceSAndroid Build Coastguard Worker    ptype=None
78*35238bceSAndroid Build Coastguard Worker    group=None
79*35238bceSAndroid Build Coastguard Worker    params=None
80*35238bceSAndroid Build Coastguard Worker    alias=None
81*35238bceSAndroid Build Coastguard Worker
82*35238bceSAndroid Build Coastguard Workerclass Interface(Object): pass
83*35238bceSAndroid Build Coastguard Worker
84*35238bceSAndroid Build Coastguard Workerclass Index:
85*35238bceSAndroid Build Coastguard Worker    def __init__(self, items=[], **kwargs):
86*35238bceSAndroid Build Coastguard Worker        self.index = {}
87*35238bceSAndroid Build Coastguard Worker        self.items = []
88*35238bceSAndroid Build Coastguard Worker        self.__dict__.update(kwargs)
89*35238bceSAndroid Build Coastguard Worker        self.update(items)
90*35238bceSAndroid Build Coastguard Worker
91*35238bceSAndroid Build Coastguard Worker    def append(self, item):
92*35238bceSAndroid Build Coastguard Worker        keys = self.getkeys(item)
93*35238bceSAndroid Build Coastguard Worker        for key in keys:
94*35238bceSAndroid Build Coastguard Worker            self[key] = item
95*35238bceSAndroid Build Coastguard Worker        self.items.append(item)
96*35238bceSAndroid Build Coastguard Worker
97*35238bceSAndroid Build Coastguard Worker    def update(self, items):
98*35238bceSAndroid Build Coastguard Worker        for item in items:
99*35238bceSAndroid Build Coastguard Worker            self.append(item)
100*35238bceSAndroid Build Coastguard Worker
101*35238bceSAndroid Build Coastguard Worker    def __iter__(self):
102*35238bceSAndroid Build Coastguard Worker        return iter(self.items)
103*35238bceSAndroid Build Coastguard Worker
104*35238bceSAndroid Build Coastguard Worker    def nextkey(self, key):
105*35238bceSAndroid Build Coastguard Worker        raise KeyError
106*35238bceSAndroid Build Coastguard Worker
107*35238bceSAndroid Build Coastguard Worker    def getkeys(self, item):
108*35238bceSAndroid Build Coastguard Worker        return []
109*35238bceSAndroid Build Coastguard Worker
110*35238bceSAndroid Build Coastguard Worker    def __contains__(self, key):
111*35238bceSAndroid Build Coastguard Worker        return key in self.index
112*35238bceSAndroid Build Coastguard Worker
113*35238bceSAndroid Build Coastguard Worker    def __setitem__(self, key, item):
114*35238bceSAndroid Build Coastguard Worker        if key in self.index:
115*35238bceSAndroid Build Coastguard Worker            if key is not None:
116*35238bceSAndroid Build Coastguard Worker                self.duplicateKey(key, item)
117*35238bceSAndroid Build Coastguard Worker        else:
118*35238bceSAndroid Build Coastguard Worker            self.index[key] = item
119*35238bceSAndroid Build Coastguard Worker
120*35238bceSAndroid Build Coastguard Worker    def duplicateKey(self, key, item):
121*35238bceSAndroid Build Coastguard Worker        warning("Duplicate %s: %r", type(item).__name__.lower(), key)
122*35238bceSAndroid Build Coastguard Worker
123*35238bceSAndroid Build Coastguard Worker    def __getitem__(self, key):
124*35238bceSAndroid Build Coastguard Worker        try:
125*35238bceSAndroid Build Coastguard Worker            while True:
126*35238bceSAndroid Build Coastguard Worker                try:
127*35238bceSAndroid Build Coastguard Worker                    return self.index[key]
128*35238bceSAndroid Build Coastguard Worker                except KeyError:
129*35238bceSAndroid Build Coastguard Worker                    pass
130*35238bceSAndroid Build Coastguard Worker                key = self.nextkey(key)
131*35238bceSAndroid Build Coastguard Worker        except KeyError:
132*35238bceSAndroid Build Coastguard Worker            item = self.missingKey(key)
133*35238bceSAndroid Build Coastguard Worker            self.append(item)
134*35238bceSAndroid Build Coastguard Worker            return item
135*35238bceSAndroid Build Coastguard Worker
136*35238bceSAndroid Build Coastguard Worker    def missingKey(self, key):
137*35238bceSAndroid Build Coastguard Worker        raise KeyError(key)
138*35238bceSAndroid Build Coastguard Worker
139*35238bceSAndroid Build Coastguard Worker    def __len__(self):
140*35238bceSAndroid Build Coastguard Worker        return len(self.items)
141*35238bceSAndroid Build Coastguard Worker
142*35238bceSAndroid Build Coastguard Workerclass ElemNameIndex(Index):
143*35238bceSAndroid Build Coastguard Worker    def getkeys(self, item):
144*35238bceSAndroid Build Coastguard Worker        return [item.get('name')]
145*35238bceSAndroid Build Coastguard Worker
146*35238bceSAndroid Build Coastguard Worker    def duplicateKey(self, key, item):
147*35238bceSAndroid Build Coastguard Worker        warnElem(item, "Duplicate key: %s", key)
148*35238bceSAndroid Build Coastguard Worker
149*35238bceSAndroid Build Coastguard Workerclass CommandIndex(Index):
150*35238bceSAndroid Build Coastguard Worker    def getkeys(self, item):
151*35238bceSAndroid Build Coastguard Worker        #BOZA: No reason to add alias: it has its own entry in enums in xml file
152*35238bceSAndroid Build Coastguard Worker        #return [(name, api)] + ([(alias, api)] if alias is not None else [])
153*35238bceSAndroid Build Coastguard Worker        return [item.findtext('proto/name')]
154*35238bceSAndroid Build Coastguard Worker
155*35238bceSAndroid Build Coastguard Workerclass NameApiIndex(Index):
156*35238bceSAndroid Build Coastguard Worker    def getkeys(self, item):
157*35238bceSAndroid Build Coastguard Worker        return [(item.get('name'), item.get('api'))]
158*35238bceSAndroid Build Coastguard Worker
159*35238bceSAndroid Build Coastguard Worker    def nextkey(self, key):
160*35238bceSAndroid Build Coastguard Worker        if len(key) == 2 and key[1] is not None:
161*35238bceSAndroid Build Coastguard Worker            return key[0], None
162*35238bceSAndroid Build Coastguard Worker        raise KeyError
163*35238bceSAndroid Build Coastguard Worker
164*35238bceSAndroid Build Coastguard Worker    def duplicateKey(self, key, item):
165*35238bceSAndroid Build Coastguard Worker        warnElem(item, "Duplicate key: %s", key)
166*35238bceSAndroid Build Coastguard Worker
167*35238bceSAndroid Build Coastguard Workerclass TypeIndex(NameApiIndex):
168*35238bceSAndroid Build Coastguard Worker    def getkeys(self, item):
169*35238bceSAndroid Build Coastguard Worker        return [(item.get('name') or item.findtext('name'), item.get('api'))]
170*35238bceSAndroid Build Coastguard Worker
171*35238bceSAndroid Build Coastguard Workerclass EnumIndex(NameApiIndex):
172*35238bceSAndroid Build Coastguard Worker    def getkeys(self, item):
173*35238bceSAndroid Build Coastguard Worker        name, api, alias = (item.get(attrib) for attrib in ['name', 'api', 'alias'])
174*35238bceSAndroid Build Coastguard Worker        #BOZA: No reason to add alias: it has its own entry in enums
175*35238bceSAndroid Build Coastguard Worker        #return [(name, api)] + ([(alias, api)] if alias is not None else [])
176*35238bceSAndroid Build Coastguard Worker        return [(name, api)]
177*35238bceSAndroid Build Coastguard Worker
178*35238bceSAndroid Build Coastguard Worker    def duplicateKey(self, nameapipair, item):
179*35238bceSAndroid Build Coastguard Worker        (name, api) = nameapipair
180*35238bceSAndroid Build Coastguard Worker        if name == item.get('alias'):
181*35238bceSAndroid Build Coastguard Worker            warnElem(item, "Alias already present: %s", name)
182*35238bceSAndroid Build Coastguard Worker        else:
183*35238bceSAndroid Build Coastguard Worker            warnElem(item, "Already present")
184*35238bceSAndroid Build Coastguard Worker
185*35238bceSAndroid Build Coastguard Workerclass Registry:
186*35238bceSAndroid Build Coastguard Worker    def __init__(self, eRegistry):
187*35238bceSAndroid Build Coastguard Worker        self.types = TypeIndex(eRegistry.findall('types/type'))
188*35238bceSAndroid Build Coastguard Worker        self.groups = ElemNameIndex(eRegistry.findall('groups/group'))
189*35238bceSAndroid Build Coastguard Worker        self.enums = EnumIndex(eRegistry.findall('enums/enum'))
190*35238bceSAndroid Build Coastguard Worker        for eEnum in self.enums:
191*35238bceSAndroid Build Coastguard Worker            groupName = eEnum.get('group')
192*35238bceSAndroid Build Coastguard Worker            if groupName is not None:
193*35238bceSAndroid Build Coastguard Worker                self.groups[groupName] = eEnum
194*35238bceSAndroid Build Coastguard Worker        self.commands = CommandIndex(eRegistry.findall('commands/command'))
195*35238bceSAndroid Build Coastguard Worker        self.features = ElemNameIndex(eRegistry.findall('feature'))
196*35238bceSAndroid Build Coastguard Worker        self.apis = {}
197*35238bceSAndroid Build Coastguard Worker        for eFeature in self.features:
198*35238bceSAndroid Build Coastguard Worker            self.apis.setdefault(eFeature.get('api'), []).append(eFeature)
199*35238bceSAndroid Build Coastguard Worker        for apiFeatures in self.apis.values():
200*35238bceSAndroid Build Coastguard Worker            apiFeatures.sort(key=lambda eFeature: eFeature.get('number'))
201*35238bceSAndroid Build Coastguard Worker        self.extensions = ElemNameIndex(eRegistry.findall('extensions/extension'))
202*35238bceSAndroid Build Coastguard Worker        self.element = eRegistry
203*35238bceSAndroid Build Coastguard Worker
204*35238bceSAndroid Build Coastguard Worker    def getFeatures(self, api, checkVersion=None):
205*35238bceSAndroid Build Coastguard Worker        return [eFeature for eFeature in self.apis[api]
206*35238bceSAndroid Build Coastguard Worker                if checkVersion is None or checkVersion(eFeature.get('number'))]
207*35238bceSAndroid Build Coastguard Worker
208*35238bceSAndroid Build Coastguard Workerclass NameIndex(Index):
209*35238bceSAndroid Build Coastguard Worker    createMissing = None
210*35238bceSAndroid Build Coastguard Worker    kind = "item"
211*35238bceSAndroid Build Coastguard Worker
212*35238bceSAndroid Build Coastguard Worker    def getkeys(self, item):
213*35238bceSAndroid Build Coastguard Worker        return [item.name]
214*35238bceSAndroid Build Coastguard Worker
215*35238bceSAndroid Build Coastguard Worker    def missingKey(self, key):
216*35238bceSAndroid Build Coastguard Worker        if self.createMissing:
217*35238bceSAndroid Build Coastguard Worker            warning("Reference to implicit %s: %r", self.kind, key)
218*35238bceSAndroid Build Coastguard Worker            return self.createMissing(name=key)
219*35238bceSAndroid Build Coastguard Worker        else:
220*35238bceSAndroid Build Coastguard Worker            raise KeyError
221*35238bceSAndroid Build Coastguard Worker
222*35238bceSAndroid Build Coastguard Workerdef matchApi(api1, api2):
223*35238bceSAndroid Build Coastguard Worker    return api1 is None or api2 is None or api1 == api2
224*35238bceSAndroid Build Coastguard Worker
225*35238bceSAndroid Build Coastguard Workerclass Interface(Object):
226*35238bceSAndroid Build Coastguard Worker    pass
227*35238bceSAndroid Build Coastguard Worker
228*35238bceSAndroid Build Coastguard Workerdef extractAlias(eCommand):
229*35238bceSAndroid Build Coastguard Worker    aliases = eCommand.xpath('alias/@name')
230*35238bceSAndroid Build Coastguard Worker    return aliases[0] if aliases else None
231*35238bceSAndroid Build Coastguard Worker
232*35238bceSAndroid Build Coastguard Workerdef getExtensionName(eExtension):
233*35238bceSAndroid Build Coastguard Worker    return eExtension.get('name')
234*35238bceSAndroid Build Coastguard Worker
235*35238bceSAndroid Build Coastguard Workerdef extensionSupports(eExtension, api, profile=None):
236*35238bceSAndroid Build Coastguard Worker    if api == 'gl' and profile == 'core':
237*35238bceSAndroid Build Coastguard Worker        needSupport = 'glcore'
238*35238bceSAndroid Build Coastguard Worker    else:
239*35238bceSAndroid Build Coastguard Worker        needSupport = api
240*35238bceSAndroid Build Coastguard Worker    supporteds = eExtension.get('supported').split('|')
241*35238bceSAndroid Build Coastguard Worker    return needSupport in supporteds
242*35238bceSAndroid Build Coastguard Worker
243*35238bceSAndroid Build Coastguard Workerclass InterfaceSpec(Object):
244*35238bceSAndroid Build Coastguard Worker    def __init__(self):
245*35238bceSAndroid Build Coastguard Worker        self.enums = set()
246*35238bceSAndroid Build Coastguard Worker        self.types = set()
247*35238bceSAndroid Build Coastguard Worker        self.commands = set()
248*35238bceSAndroid Build Coastguard Worker        self.versions = set()
249*35238bceSAndroid Build Coastguard Worker
250*35238bceSAndroid Build Coastguard Worker    def addComponent(self, eComponent):
251*35238bceSAndroid Build Coastguard Worker        if eComponent.tag == 'require':
252*35238bceSAndroid Build Coastguard Worker            def modify(items, item): items.add(item)
253*35238bceSAndroid Build Coastguard Worker        else:
254*35238bceSAndroid Build Coastguard Worker            assert eComponent.tag == 'remove'
255*35238bceSAndroid Build Coastguard Worker            def modify(items, item):
256*35238bceSAndroid Build Coastguard Worker                try:
257*35238bceSAndroid Build Coastguard Worker                    items.remove(item)
258*35238bceSAndroid Build Coastguard Worker                except KeyError:
259*35238bceSAndroid Build Coastguard Worker                    warning("Tried to remove absent item: %s", item)
260*35238bceSAndroid Build Coastguard Worker        for typeName in eComponent.xpath('type/@name'):
261*35238bceSAndroid Build Coastguard Worker            modify(self.types, typeName)
262*35238bceSAndroid Build Coastguard Worker        for enumName in eComponent.xpath('enum/@name'):
263*35238bceSAndroid Build Coastguard Worker            modify(self.enums, enumName)
264*35238bceSAndroid Build Coastguard Worker        for commandName in eComponent.xpath('command/@name'):
265*35238bceSAndroid Build Coastguard Worker            modify(self.commands, commandName)
266*35238bceSAndroid Build Coastguard Worker
267*35238bceSAndroid Build Coastguard Worker    def addComponents(self, elem, api, profile=None):
268*35238bceSAndroid Build Coastguard Worker        for eComponent in elem.xpath('require|remove'):
269*35238bceSAndroid Build Coastguard Worker            cApi = eComponent.get('api')
270*35238bceSAndroid Build Coastguard Worker            cProfile = eComponent.get('profile')
271*35238bceSAndroid Build Coastguard Worker            if (matchApi(api, eComponent.get('api')) and
272*35238bceSAndroid Build Coastguard Worker                matchApi(profile, eComponent.get('profile'))):
273*35238bceSAndroid Build Coastguard Worker                self.addComponent(eComponent)
274*35238bceSAndroid Build Coastguard Worker
275*35238bceSAndroid Build Coastguard Worker    def addFeature(self, eFeature, api=None, profile=None, force=False):
276*35238bceSAndroid Build Coastguard Worker        info('Feature %s', eFeature.get('name'))
277*35238bceSAndroid Build Coastguard Worker        if not matchApi(api, eFeature.get('api')):
278*35238bceSAndroid Build Coastguard Worker            if not force: return
279*35238bceSAndroid Build Coastguard Worker            warnElem(eFeature, 'API %s is not supported', api)
280*35238bceSAndroid Build Coastguard Worker        self.addComponents(eFeature, api, profile)
281*35238bceSAndroid Build Coastguard Worker        self.versions.add(eFeature.get('name'))
282*35238bceSAndroid Build Coastguard Worker
283*35238bceSAndroid Build Coastguard Worker    def addExtension(self, eExtension, api=None, profile=None, force=False):
284*35238bceSAndroid Build Coastguard Worker        if not extensionSupports(eExtension, api, profile):
285*35238bceSAndroid Build Coastguard Worker            if not force: return
286*35238bceSAndroid Build Coastguard Worker            warnElem(eExtension, '%s is not supported in API %s' % (getExtensionName(eExtension), api))
287*35238bceSAndroid Build Coastguard Worker        self.addComponents(eExtension, api, profile)
288*35238bceSAndroid Build Coastguard Worker
289*35238bceSAndroid Build Coastguard Workerdef createInterface(registry, spec, api=None):
290*35238bceSAndroid Build Coastguard Worker    def parseType(eType):
291*35238bceSAndroid Build Coastguard Worker        # todo: apientry
292*35238bceSAndroid Build Coastguard Worker        #requires = eType.get('requires')
293*35238bceSAndroid Build Coastguard Worker        #if requires is not None:
294*35238bceSAndroid Build Coastguard Worker        #    types[requires]
295*35238bceSAndroid Build Coastguard Worker        return makeObject(
296*35238bceSAndroid Build Coastguard Worker            Type, eType,
297*35238bceSAndroid Build Coastguard Worker            name=eType.get('name') or eType.findtext('name'),
298*35238bceSAndroid Build Coastguard Worker            definition=''.join(eType.xpath('.//text()')),
299*35238bceSAndroid Build Coastguard Worker            api=eType.get('api'),
300*35238bceSAndroid Build Coastguard Worker            requires=eType.get('requires'))
301*35238bceSAndroid Build Coastguard Worker
302*35238bceSAndroid Build Coastguard Worker    def createType(name):
303*35238bceSAndroid Build Coastguard Worker        info('Add type %s', name)
304*35238bceSAndroid Build Coastguard Worker        try:
305*35238bceSAndroid Build Coastguard Worker            return parseType(registry.types[name, api])
306*35238bceSAndroid Build Coastguard Worker        except KeyError:
307*35238bceSAndroid Build Coastguard Worker            return Type(name=name)
308*35238bceSAndroid Build Coastguard Worker
309*35238bceSAndroid Build Coastguard Worker    def createEnum(enumName):
310*35238bceSAndroid Build Coastguard Worker        info('Add enum %s', enumName)
311*35238bceSAndroid Build Coastguard Worker        return parseEnum(registry.enums[enumName, api])
312*35238bceSAndroid Build Coastguard Worker
313*35238bceSAndroid Build Coastguard Worker    def extractPtype(elem):
314*35238bceSAndroid Build Coastguard Worker        ePtype = elem.find('ptype')
315*35238bceSAndroid Build Coastguard Worker        if ePtype is None:
316*35238bceSAndroid Build Coastguard Worker            return None
317*35238bceSAndroid Build Coastguard Worker        return types[ePtype.text]
318*35238bceSAndroid Build Coastguard Worker
319*35238bceSAndroid Build Coastguard Worker    def extractGroup(elem):
320*35238bceSAndroid Build Coastguard Worker        groupName = elem.get('group')
321*35238bceSAndroid Build Coastguard Worker        if groupName is None:
322*35238bceSAndroid Build Coastguard Worker            return None
323*35238bceSAndroid Build Coastguard Worker        return groups[groupName]
324*35238bceSAndroid Build Coastguard Worker
325*35238bceSAndroid Build Coastguard Worker    def parseParam(eParam):
326*35238bceSAndroid Build Coastguard Worker        return makeObject(
327*35238bceSAndroid Build Coastguard Worker            Param, eParam,
328*35238bceSAndroid Build Coastguard Worker            name=eParam.get('name') or eParam.findtext('name'),
329*35238bceSAndroid Build Coastguard Worker            declaration=''.join(eParam.xpath('.//text()')).strip(),
330*35238bceSAndroid Build Coastguard Worker            type=''.join(eParam.xpath('(.|ptype)/text()')).strip(),
331*35238bceSAndroid Build Coastguard Worker            ptype=extractPtype(eParam),
332*35238bceSAndroid Build Coastguard Worker            group=extractGroup(eParam))
333*35238bceSAndroid Build Coastguard Worker
334*35238bceSAndroid Build Coastguard Worker    def createCommand(commandName):
335*35238bceSAndroid Build Coastguard Worker        info('Add command %s', commandName)
336*35238bceSAndroid Build Coastguard Worker        eCmd = registry.commands[commandName]
337*35238bceSAndroid Build Coastguard Worker        eProto = eCmd.find('proto')
338*35238bceSAndroid Build Coastguard Worker        return makeObject(
339*35238bceSAndroid Build Coastguard Worker            Command, eCmd,
340*35238bceSAndroid Build Coastguard Worker            name=eCmd.findtext('proto/name'),
341*35238bceSAndroid Build Coastguard Worker            declaration=''.join(eProto.xpath('.//text()')).strip(),
342*35238bceSAndroid Build Coastguard Worker            type=''.join(eProto.xpath('(.|ptype)/text()')).strip(),
343*35238bceSAndroid Build Coastguard Worker            ptype=extractPtype(eProto),
344*35238bceSAndroid Build Coastguard Worker            group=extractGroup(eProto),
345*35238bceSAndroid Build Coastguard Worker            alias=extractAlias(eCmd),
346*35238bceSAndroid Build Coastguard Worker            params=NameIndex(list(map(parseParam, eCmd.findall('param')))))
347*35238bceSAndroid Build Coastguard Worker
348*35238bceSAndroid Build Coastguard Worker    def createGroup(name):
349*35238bceSAndroid Build Coastguard Worker        info('Add group %s', name)
350*35238bceSAndroid Build Coastguard Worker        try:
351*35238bceSAndroid Build Coastguard Worker            eGroup = registry.groups[name]
352*35238bceSAndroid Build Coastguard Worker        except KeyError:
353*35238bceSAndroid Build Coastguard Worker            return Group(name=name)
354*35238bceSAndroid Build Coastguard Worker        return makeObject(
355*35238bceSAndroid Build Coastguard Worker            Group, eGroup,
356*35238bceSAndroid Build Coastguard Worker            # Missing enums are often from exotic extensions. Don't create dummy entries,
357*35238bceSAndroid Build Coastguard Worker            # just filter them out.
358*35238bceSAndroid Build Coastguard Worker            enums=NameIndex(enums[name] for name in eGroup.xpath('enum/@name')
359*35238bceSAndroid Build Coastguard Worker                            if name in enums))
360*35238bceSAndroid Build Coastguard Worker
361*35238bceSAndroid Build Coastguard Worker    def sortedIndex(items):
362*35238bceSAndroid Build Coastguard Worker        # Some groups have no location set, due to it is absent in gl.xml file
363*35238bceSAndroid Build Coastguard Worker        # for example glGetFenceivNV uses group FenceNV which is not declared
364*35238bceSAndroid Build Coastguard Worker        #    <command>
365*35238bceSAndroid Build Coastguard Worker        #        <proto>void <name>glGetFenceivNV</name></proto>
366*35238bceSAndroid Build Coastguard Worker        #        <param group="FenceNV"><ptype>GLuint</ptype> <name>fence</name></param>
367*35238bceSAndroid Build Coastguard Worker        # Python 2 ignores it. Avoid sorting to allow Python 3 to continue
368*35238bceSAndroid Build Coastguard Worker
369*35238bceSAndroid Build Coastguard Worker        enableSort=True
370*35238bceSAndroid Build Coastguard Worker        for item in items:
371*35238bceSAndroid Build Coastguard Worker            if item.location is None:
372*35238bceSAndroid Build Coastguard Worker                enableSort=False
373*35238bceSAndroid Build Coastguard Worker                warning("Location not found for %s: %s", type(item).__name__.lower(), item.name)
374*35238bceSAndroid Build Coastguard Worker
375*35238bceSAndroid Build Coastguard Worker        if enableSort:
376*35238bceSAndroid Build Coastguard Worker            sortedItems = sorted(items, key=lambda item: item.location)
377*35238bceSAndroid Build Coastguard Worker        else:
378*35238bceSAndroid Build Coastguard Worker            sortedItems = items
379*35238bceSAndroid Build Coastguard Worker        return NameIndex(sortedItems)
380*35238bceSAndroid Build Coastguard Worker
381*35238bceSAndroid Build Coastguard Worker    groups = NameIndex(createMissing=createGroup, kind="group")
382*35238bceSAndroid Build Coastguard Worker    types = NameIndex(list(map(createType, spec.types)),
383*35238bceSAndroid Build Coastguard Worker                      createMissing=createType, kind="type")
384*35238bceSAndroid Build Coastguard Worker    enums = NameIndex(list(map(createEnum, spec.enums)),
385*35238bceSAndroid Build Coastguard Worker                      createMissing=Enum, kind="enum")
386*35238bceSAndroid Build Coastguard Worker    commands = NameIndex(list(map(createCommand, spec.commands)),
387*35238bceSAndroid Build Coastguard Worker                        createMissing=Command, kind="command")
388*35238bceSAndroid Build Coastguard Worker    versions = sorted(spec.versions)
389*35238bceSAndroid Build Coastguard Worker
390*35238bceSAndroid Build Coastguard Worker    # This is a mess because the registry contains alias chains whose
391*35238bceSAndroid Build Coastguard Worker    # midpoints might not be included in the interface even though
392*35238bceSAndroid Build Coastguard Worker    # endpoints are.
393*35238bceSAndroid Build Coastguard Worker    for command in commands:
394*35238bceSAndroid Build Coastguard Worker        alias = command.alias
395*35238bceSAndroid Build Coastguard Worker        aliasCommand = None
396*35238bceSAndroid Build Coastguard Worker        while alias is not None:
397*35238bceSAndroid Build Coastguard Worker            aliasCommand = registry.commands[alias]
398*35238bceSAndroid Build Coastguard Worker            alias = extractAlias(aliasCommand)
399*35238bceSAndroid Build Coastguard Worker        command.alias = None
400*35238bceSAndroid Build Coastguard Worker        if aliasCommand is not None:
401*35238bceSAndroid Build Coastguard Worker            name = aliasCommand.findtext('proto/name')
402*35238bceSAndroid Build Coastguard Worker            if name in commands:
403*35238bceSAndroid Build Coastguard Worker                command.alias = commands[name]
404*35238bceSAndroid Build Coastguard Worker
405*35238bceSAndroid Build Coastguard Worker    sortedTypes=sortedIndex(types)
406*35238bceSAndroid Build Coastguard Worker    sortedEnums=sortedIndex(enums)
407*35238bceSAndroid Build Coastguard Worker    sortedGroups=sortedIndex(groups)
408*35238bceSAndroid Build Coastguard Worker    sortedCommands=sortedIndex(commands)
409*35238bceSAndroid Build Coastguard Worker
410*35238bceSAndroid Build Coastguard Worker    ifc=Interface(
411*35238bceSAndroid Build Coastguard Worker        types=sortedTypes,
412*35238bceSAndroid Build Coastguard Worker        enums=sortedEnums,
413*35238bceSAndroid Build Coastguard Worker        groups=sortedGroups,
414*35238bceSAndroid Build Coastguard Worker        commands=sortedCommands,
415*35238bceSAndroid Build Coastguard Worker        versions=versions)
416*35238bceSAndroid Build Coastguard Worker
417*35238bceSAndroid Build Coastguard Worker    return ifc
418*35238bceSAndroid Build Coastguard Worker
419*35238bceSAndroid Build Coastguard Worker
420*35238bceSAndroid Build Coastguard Workerdef spec(registry, api, version=None, profile=None, extensionNames=[], protects=[], force=False):
421*35238bceSAndroid Build Coastguard Worker    available = set(protects)
422*35238bceSAndroid Build Coastguard Worker    spec = InterfaceSpec()
423*35238bceSAndroid Build Coastguard Worker
424*35238bceSAndroid Build Coastguard Worker    if version is None or version is False:
425*35238bceSAndroid Build Coastguard Worker        def check(v): return False
426*35238bceSAndroid Build Coastguard Worker    elif version is True:
427*35238bceSAndroid Build Coastguard Worker        def check(v): return True
428*35238bceSAndroid Build Coastguard Worker    else:
429*35238bceSAndroid Build Coastguard Worker        def check(v): return v <= version
430*35238bceSAndroid Build Coastguard Worker
431*35238bceSAndroid Build Coastguard Worker#    BOZA TODO: I suppose adding primitive types will remove a lot of warnings
432*35238bceSAndroid Build Coastguard Worker#    spec.addComponents(registry.types, api, profile)
433*35238bceSAndroid Build Coastguard Worker
434*35238bceSAndroid Build Coastguard Worker    for eFeature in registry.getFeatures(api, check):
435*35238bceSAndroid Build Coastguard Worker        spec.addFeature(eFeature, api, profile, force)
436*35238bceSAndroid Build Coastguard Worker
437*35238bceSAndroid Build Coastguard Worker    for extName in extensionNames:
438*35238bceSAndroid Build Coastguard Worker        eExtension = registry.extensions[extName]
439*35238bceSAndroid Build Coastguard Worker        protect = eExtension.get('protect')
440*35238bceSAndroid Build Coastguard Worker        if protect is not None and protect not in available:
441*35238bceSAndroid Build Coastguard Worker            warnElem(eExtension, "Unavailable dependency %s", protect)
442*35238bceSAndroid Build Coastguard Worker            if not force:
443*35238bceSAndroid Build Coastguard Worker                continue
444*35238bceSAndroid Build Coastguard Worker        spec.addExtension(eExtension, api, profile, force)
445*35238bceSAndroid Build Coastguard Worker        available.add(extName)
446*35238bceSAndroid Build Coastguard Worker
447*35238bceSAndroid Build Coastguard Worker    return spec
448*35238bceSAndroid Build Coastguard Worker
449*35238bceSAndroid Build Coastguard Workerdef interface(registry, api, **kwargs):
450*35238bceSAndroid Build Coastguard Worker    s = spec(registry, api, **kwargs)
451*35238bceSAndroid Build Coastguard Worker    return createInterface(registry, s, api)
452*35238bceSAndroid Build Coastguard Worker
453*35238bceSAndroid Build Coastguard Workerdef parse(path):
454*35238bceSAndroid Build Coastguard Worker    return Registry(etree.parse(path))
455