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