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