util: link with WAYLAND_LIBRARIES when building for Wayland
[piglit.git] / registry / gl.py
blobf994f94d5255f18c30c6ba04af1fcd2c5e3857f2
1 # Copyright 2014 Intel Corporation
3 # Permission is hereby granted, free of charge, to any person obtaining a
4 # copy of this software and associated documentation files (the "Software"),
5 # to deal in the Software without restriction, including without limitation
6 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 # and/or sell copies of the Software, and to permit persons to whom the
8 # Software is furnished to do so, subject to the following conditions:
10 # The above copyright notice and this permission notice (including the next
11 # paragraph) shall be included in all copies or substantial portions of the
12 # Software.
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 # IN THE SOFTWARE.
22 """
23 Parse gl.xml into Python objects.
24 """
26 from __future__ import (
27 absolute_import, division, print_function, unicode_literals
30 import os.path
31 import re
32 import sys
33 import functools
34 from copy import copy, deepcopy
36 import six
38 # Export 'debug' so other Piglit modules can easily enable it.
39 debug = False
42 def _log_debug(msg):
43 if debug:
44 print('debug: {0}: {1}'.format(__name__, msg), file=sys.stderr)
47 # Prefer the external module 'lxml.etree' (it uses libxml2) over Python's
48 # builtin 'xml.etree.ElementTree'. It's faster.
49 try:
50 import lxml.etree as etree
51 _log_debug('etree is lxml.etree')
52 except ImportError:
53 import xml.etree.cElementTree as etree
54 _log_debug('etree is xml.etree.cElementTree')
57 def parse():
58 """Parse gl.xml and return a Registry object."""
59 filename = os.path.join(os.path.dirname(__file__), 'gl.xml')
60 xml_registry = etree.parse(filename).getroot()
61 _repair_xml(xml_registry)
62 return Registry(xml_registry)
65 def _repair_xml(xml_registry):
66 fixes = set((
67 'GL_ALL_ATTRIB_BITS',
68 'glOcclusionQueryEventMaskAMD',
69 'gles2_GL_ACTIVE_PROGRAM_EXT',
72 remove_queue = []
74 def defer_removal(parent, child):
75 remove_queue.append((parent, child))
77 for enums in xml_registry.iterfind('./enums'):
78 if ('GL_ALL_ATTRIB_BITS' in fixes and
79 enums.get('group') == 'AttribMask'):
80 # The XML defines GL_ALL_ATTRIB_BITS incorrectly with all bits
81 # set (0xFFFFFFFF). From the GL_ARB_multisample spec, v5:
83 # In order to avoid incompatibility with GL implementations
84 # that do not support SGIS_multisample, ALL_ATTRIB_BITS
85 # does not include MULTISAMPLE_BIT_ARB.
87 enum = enums.find("./enum[@name='GL_ALL_ATTRIB_BITS']")
88 enum.set('value', '0x000FFFFF')
90 fixes.remove('GL_ALL_ATTRIB_BITS')
91 continue
93 if ('glOcclusionQueryEventMaskAMD' in fixes and
94 enums.get('namespace') == 'OcclusionQueryEventMaskAMD'):
95 # This tag's attributes are totally broken.
96 enums.set('namespace', 'GL')
97 enums.set('group', 'OcclusionQueryEventMaskAMD')
98 enums.set('type', 'bitmask')
100 fixes.remove('glOcclusionQueryEventMaskAMD')
101 continue
103 if ('gles2_GL_ACTIVE_PROGRAM_EXT' in fixes and
104 enums.get('vendor') is not None and
105 enums.get('vendor') == 'ARB' and
106 enums.get('start') is not None and
107 enums.get('start') <= '0x8259' and
108 enums.get('end') is not None and
109 enums.get('end') >= '0x8259'):
110 # GL_ACTIVE_PROGRAM_EXT has different numerical values in GL
111 # (0x8B8D) and in GLES (0x8259). Remove the GLES value to avoid
112 # redefinition collisions.
113 bad_enum = enums.find(("./enum"
114 "[@value='0x8259']"
115 "[@name='GL_ACTIVE_PROGRAM_EXT']"
116 "[@api='gles2']"))
117 defer_removal(enums, bad_enum)
119 fixes.remove('gles2_GL_ACTIVE_PROGRAM_EXT')
120 continue
122 for (parent, child) in remove_queue:
123 parent.remove(child)
125 if len(fixes) > 0:
126 raise Exception('failed to apply some xml repairs: ' +
127 ', '.join(map(repr, fixes)))
130 class OrderedKeyedSet(object):
131 """A set with keyed elements that preserves order of element insertion.
133 Why waste words? Let's document the class with example code.
135 Example:
136 Cheese = namedtuple('Cheese', ('name', 'flavor'))
137 cheeses = OrderedKeyedSet(key='name')
138 cheeses.add(Cheese(name='cheddar', flavor='good'))
139 cheeses.add(Cheese(name='gouda', flavor='smells like feet'))
140 cheeses.add(Cheese(name='romano', flavor='awesome'))
142 # Elements are retrievable by key.
143 assert cheeses['gouda'].flavor == 'smells like feet'
145 # On key collision, the old element is removed.
146 cheeses.add(Cheese(name='gouda', flavor='ok i guess'))
147 assert cheeses['gouda'].flavor == 'ok i guess'
149 # The set preserves order of insertion. Replacement does not alter
150 # order.
151 assert list(cheeses)[2].name == 'romano'
153 # The set is iterable.
154 for cheese in cheeses:
155 print(cheese.name)
157 # Yet another example...
158 Bread = namedtuple('Bread', ('name', 'smell'))
159 breads = OrderedKeyedSet(key='name')
160 breads.add(Bread(name='como', smell='subtle')
161 breads.add(Bread(name='sourdough', smell='pleasant'))
163 # The set supports some common set operations, such as union.
164 breads_and_cheeses = breads | cheeses
165 assert len(breads_and_cheeses) == len(breads) + len(cheeses)
168 def __init__(self, key, elems=()):
169 """Create a new set with the given key.
171 The given 'key' defines how to calculate each element's key value. If
172 'key' is a string, then each key value is defined to be
173 `getattr(elem, key)`. If 'key' is a function, then the key value is
174 `key(elem)`.
177 # A linked list contains the set's items. Each list node is a 4-tuple
178 # [prev, next, key, value]. The root node is permanent.
179 root = []
180 root[:] = [root, root, None, None]
181 self.__list_root = root
183 # For quick retrieval, we map each key to its node. That is, each map
184 # pair has form {key: [prev, next, key, value])}.
185 self.__map = dict()
187 if isinstance(key, six.text_type):
188 self.__key_func = lambda elem: getattr(elem, key)
189 else:
190 self.__key_func = key
192 for e in elems:
193 self.add(e)
195 def __or__(self, other):
196 """Same as `union`."""
197 return self.union(other)
199 def __contains__(self, key):
200 return key in self.__map
202 def __copy__(self):
203 return OrderedKeyedSet(key=deepcopy(self.__key_func),
204 elems=iter(self))
206 def __getitem__(self, key):
207 return self.__map[key][3]
209 def __iter__(self):
210 return self.itervalues()
212 def __len__(self):
213 return len(self.__map)
215 def __repr__(self):
216 templ = '{self.__class__.__name__}({self.name!r})'
217 return templ.format(self=self)
219 def add(self, value):
220 key = self.__key_func(value)
221 node = self.__map.get(key, None)
222 if node is not None:
223 node[3] = value
224 else:
225 root = self.__list_root
226 old_tail = root[0]
227 new_tail = [old_tail, root, key, value]
228 new_tail[0][1] = new_tail
229 new_tail[1][0] = new_tail
230 self.__map[key] = new_tail
232 def clear(self):
233 self.__map.clear()
234 root = self.__list_root
235 root[:] = [root, root, None, None]
237 def extend(self, elems):
238 for e in elems:
239 self.add(e)
241 def get(self, key, default):
242 node = self.__map.get(key, None)
243 if node is not None:
244 return node[3]
245 else:
246 return default
248 def iteritems(self):
249 root = self.__list_root
250 node = root[1]
251 while node is not root:
252 yield (node[2], node[3])
253 node = node[1]
255 def iterkeys(self):
256 return (i[0] for i in self.iteritems())
258 def itervalues(self):
259 return (i[1] for i in self.iteritems())
261 def pop(self, key):
262 node = self.__map.pop(key)
263 node[0][1] = node[1]
264 node[1][0] = node[0]
265 return node[3]
267 def sort_by_key(self):
268 sorted_items = sorted(six.iteritems(self.__map))
269 self.clear()
270 for item in sorted_items:
271 self.add(item[1])
273 def sort_by_value(self):
274 sorted_values = sorted(six.itervalues(self.__map))
275 self.clear()
276 for value in sorted_values:
277 self.add(value)
279 def union(self, other):
280 """Return the union of two sets as a new set.
282 In the new set, all elements of the self set precede those of the other
283 set. The order of elements in the new set preserves the order of the
284 original sets.
286 The new set's key function is copied from self. On key collisions, set
287 y has precedence over x.
289 u = copy(self)
290 u.extend(other)
291 return u
294 class ImmutableOrderedKeyedSet(OrderedKeyedSet):
296 def __init__(self, key, elems):
297 self.__is_frozen = False
298 OrderedKeyedSet.__init__(self, key=key, elems=elems)
299 self.__is_frozen = True
301 def add(self, value):
302 if self.__is_frozen:
303 raise ImmutableError
304 else:
305 OrderedKeyedSet.add(self, value)
307 def pop(self, key):
308 raise ImmutableError
310 def clear(self):
311 raise ImmutableError
314 class ImmutableError(Exception):
315 pass
318 # Values that may appear in the XML attributes 'api' and 'supported'.
319 VALID_APIS = frozenset(('gl', 'glcore', 'gles1', 'gles2', 'glsc2'))
322 class Registry(object):
323 """The toplevel <registry> element.
325 Attributes:
326 features: An OrderedKeyedSet that contains a Feature for each <feature>
327 subelement.
329 extensions: An OrderedKeyedSet that contains an Extension for each
330 <extension> subelement.
332 commands: An OrderedKeyedSet that contains a Command for each <command>
333 subelement.
335 command_alias_map: A CommandAliasMap that contains a CommandAliasSet
336 for each equivalence class of commands.
338 enum_groups: An OrderedKeyedSet that contains an EnumGroup for each
339 <enums> subelement.
341 enums: An OrderedKeyedSet that contains an Enum for each <enum>
342 subelement.
344 vendor_namespaces: A collection of all vendor prefixes and suffixes,
345 such as "ARB", "EXT", "CHROMIUM", and "NV".
348 def __init__(self, xml_registry):
349 """Parse the <registry> element."""
351 assert xml_registry.tag == 'registry'
353 self.command_alias_map = CommandAliasMap()
354 self.commands = OrderedKeyedSet(key='name')
355 self.enum_groups = []
356 self.enums = OrderedKeyedSet(key='name')
357 self.extensions = OrderedKeyedSet(key='name')
358 self.features = OrderedKeyedSet(key='name')
359 self.vendor_namespaces = set()
361 for xml_command in xml_registry.iterfind('./commands/command'):
362 command = Command(xml_command)
363 self.commands.add(command)
364 self.command_alias_map.add(command)
366 for xml_enums in xml_registry.iterfind('./enums'):
367 enum_group = EnumGroup(xml_enums)
368 self.enum_groups.append(enum_group)
369 for enum in enum_group.enums:
370 self.enums.add(enum)
372 for xml_feature in xml_registry.iterfind('./feature'):
373 feature = Feature(xml_feature, command_map=self.commands,
374 enum_map=self.enums)
375 self.features.add(feature)
377 for xml_ext in xml_registry.iterfind('./extensions/extension'):
378 ext = Extension(xml_ext, command_map=self.commands,
379 enum_map=self.enums)
380 self.extensions.add(ext)
381 self.vendor_namespaces.add(ext.vendor_namespace)
383 self.vendor_namespaces.remove(None)
386 @functools.total_ordering
387 class Feature(object):
388 """A <feature> XML element.
390 Attributes:
391 name: The XML element's 'name' attribute.
393 api: The XML element's 'api' attribute.
395 version_str: The XML element's 'number' attribute. For example, "3.1".
397 version_float: float(version_str)
399 version_int: int(10 * version_float)
401 requirements: A collection of Requirement for each Command and Enum
402 this Feature requires.
405 def __init__(self, xml_feature, command_map, enum_map):
406 """Parse a <feature> element."""
408 # Example <feature> element:
410 # <feature api="gles2" name="GL_ES_VERSION_3_1" number="3.1">
411 # <!-- arrays_of_arrays features -->
412 # <require/>
413 # <!-- compute_shader features -->
414 # <require>
415 # <command name="glDispatchCompute"/>
416 # <command name="glDispatchComputeIndirect"/>
417 # <enum name="GL_COMPUTE_SHADER"/>
418 # <enum name="GL_MAX_COMPUTE_UNIFORM_BLOCKS"/>
419 # ...
420 # </require>
421 # <!-- draw_indirect features -->
422 # <require>
423 # <command name="glDrawArraysIndirect"/>
424 # <command name="glDrawElementsIndirect"/>
425 # <enum name="GL_DRAW_INDIRECT_BUFFER"/>
426 # <enum name="GL_DRAW_INDIRECT_BUFFER_BINDING"/>
427 # </require>
428 # ...
429 # </feature>
431 assert xml_feature.tag == 'feature'
433 # Parse the <feature> tag's attributes.
434 self.name = xml_feature.get('name')
435 self.api = xml_feature.get('api')
436 self.is_gles = self.name.startswith('GL_ES')
438 self.version_str = xml_feature.get('number')
439 self.version_float = float(self.version_str)
440 self.version_int = int(10 * self.version_float)
442 self.__parse_requirements(xml_feature, command_map, enum_map)
444 assert self.api in VALID_APIS
445 assert len(self.requirements) > 0
447 def __eq__(self, other):
448 if self is other:
449 return True
450 elif isinstance(other, Extension):
451 return False
452 elif self.is_gles != other.is_gles:
453 return False
454 return self.name == other.name
456 def __lt__(self, other):
457 if isinstance(other, Extension):
458 return True
459 # Desktop GL before GLES
460 elif self.is_gles != other.is_gles:
461 if self.is_gles:
462 return False
463 else:
464 return True
465 return self.name < other.name
467 def __repr__(self):
468 templ = '{self.__class__.__name__}({self.name!r})'
469 return templ.format(self=self)
471 def __parse_requirements(self, xml_feature, command_map, enum_map):
472 """For each <command> and <enum> under a <require>, create
473 a Requirement that links this Feature to a Command or Enum.
475 self.requirements = set()
477 def link(x):
478 req = Requirement(provider=self, provided=x,
479 apis=frozenset((self.api,)))
480 self.requirements.add(req)
481 x.requirements.add(req)
483 for xml_cmd in xml_feature.iterfind('./require/command'):
484 cmd = command_map[xml_cmd.get('name')]
485 link(cmd)
486 for xml_enum in xml_feature.iterfind('./require/enum'):
487 enum = enum_map[xml_enum.get('name')]
488 link(enum)
491 @functools.total_ordering
492 class Extension(object):
493 """An <extension> XML element.
495 Attributes:
496 name: The XML element's 'name' attribute.
498 supported_apis: The set of api strings in the XML element's 'supported'
499 attribute. For example, set('gl', 'glcore').
501 vendor_namespace: For example, "AMD". May be None.
503 requirements: A collection of Requirement for each Command and Enum
504 this Extension requires.
507 __VENDOR_REGEX = re.compile(r'^GL_(?P<vendor_namespace>[A-Z]+)_')
508 RATIFIED_NAMESPACES = ('KHR', 'ARB', 'OES')
510 def __init__(self, xml_extension, command_map, enum_map):
511 """Parse an <extension> element."""
513 # Example <extension> element:
514 # <extension name="GL_ARB_ES2_compatibility" supported="gl|glcore">
515 # <require>
516 # <enum name="GL_FIXED"/>
517 # <enum name="GL_IMPLEMENTATION_COLOR_READ_TYPE"/>
518 # ...
519 # <command name="glReleaseShaderCompiler"/>
520 # <command name="glShaderBinary"/>
521 # ...
522 # </require>
523 # </extension>
525 assert xml_extension.tag == 'extension'
527 self.name = xml_extension.get('name')
529 self.vendor_namespace = None
530 match = Extension.__VENDOR_REGEX.match(self.name)
531 if match is not None:
532 groups = match.groupdict()
533 self.vendor_namespace = groups.get('vendor_namespace', None)
535 self.supported_apis = xml_extension.get('supported').split('|')
536 self.supported_apis = frozenset(self.supported_apis)
537 assert self.supported_apis <= VALID_APIS
539 self.__parse_requirements(xml_extension, command_map, enum_map)
541 def __eq__(self, other):
542 if self is other:
543 return True
544 elif isinstance(other, Feature):
545 return False
546 elif self.is_ratified != other.is_ratified:
547 return False
548 elif self.vendor_namespace == 'EXT' != other.vendor_namespace == 'EXT':
549 return False
550 return self.name == other.name
552 def __lt__(self, other):
553 if isinstance(other, Feature):
554 return False
555 elif self.is_ratified != other.is_ratified:
556 # sort ratified before unratified
557 if self.is_ratified:
558 return True
559 else:
560 return False
561 elif (other.vendor_namespace == 'EXT') != \
562 (self.vendor_namespace == 'EXT'):
563 # Sort EXT before others
564 if self.vendor_namespace == 'EXT':
565 return True
566 else:
567 return False
568 return self.name < other.name
570 def __repr__(self):
571 templ = '{self.__class__.__name__}(name={self.name!r})'
572 return templ.format(self=self)
574 @property
575 def is_ratified(self):
576 """True if the vendor namespace is one that traditionally requires
577 ratification by Khronos.
579 return self.vendor_namespace in self.RATIFIED_NAMESPACES
581 def __parse_requirements(self, xml_extension, command_map, enum_map):
582 """For each <command> and <enum> under a <require>, create
583 a Requirement that links this Extension to a Command or Enum.
585 self.requirements = set()
587 def link(xml_require, x):
588 api = xml_require.get('api', None)
589 if api is not None:
590 assert api in self.supported_apis
591 apis = frozenset((api,))
592 else:
593 apis = frozenset(self.supported_apis)
595 req = Requirement(provider=self, provided=x, apis=apis)
596 self.requirements.add(req)
597 x.requirements.add(req)
599 for xml_req in xml_extension.iterfind('./require'):
600 for xml_cmd in xml_req.iterfind('./command'):
601 cmd = command_map[xml_cmd.get('name')]
602 link(xml_req, cmd)
603 for xml_enum in xml_req.iterfind('./enum'):
604 enum = enum_map[xml_enum.get('name')]
605 link(xml_req, enum)
608 @functools.total_ordering
609 class Requirement(object):
610 """A <require> XML element, which links a provider (Feature or Extension)
611 to a provided (Command or Enum) for a set of apis.
614 def __init__(self, provider, provided, apis):
615 self.provider = provider
616 self.provided = provided
617 self.apis = frozenset(apis)
619 def choose_if(condition, obj):
620 if condition:
621 return obj
622 else:
623 return None
625 self.has_feature = isinstance(provider, Feature)
626 self.has_extension = isinstance(provider, Extension)
627 self.has_command = isinstance(provided, Command)
628 self.has_enum = isinstance(provided, Enum)
630 self.feature = choose_if(self.has_feature, self.provider)
631 self.extension = choose_if(self.has_extension, self.provider)
632 self.command = choose_if(self.has_command, self.provided)
633 self.enum = choose_if(self.has_enum, self.provided)
635 assert self.has_feature + self.has_extension == 1
636 assert self.has_command + self.has_enum == 1
637 assert self.apis <= VALID_APIS
639 _log_debug('created {0}'.format(self))
641 def __hash__(self):
642 return hash('__Requirement_class_{}_{}_'.format(
643 self.provider, self.provided))
645 def __eq__(self, other):
646 if self.provider != other.provider:
647 return False
648 elif self.provided != other.provided:
649 return False
650 return True
652 def __lt__(self, other):
653 if self.provider < other.provider:
654 return True
655 elif self.provided < other.provided:
656 return True
657 return False
659 def __repr__(self):
660 templ = ('{self.__class__.__name__}'
661 '(provider={self.provider.name!r},'
662 ' provided={self.provided.name!r},'
663 ' apis={api_tuple})')
664 return templ.format(self=self, api_tuple=tuple(self.apis))
667 class CommandParam(object):
668 """A <param> XML element at path command/param.
670 Attributes:
671 name
672 c_type
673 array_suffix
676 __PARAM_NAME_FIXES = {'near': 'hither', 'far': 'yon'}
678 def __init__(self, xml_param, log=None):
679 """Parse a <param> element."""
681 # Example <param> elements:
683 # <param>const <ptype>GLchar</ptype> *<name>name</name></param>
684 # <param len="1"><ptype>GLsizei</ptype> *<name>length</name></param>
685 # <param len="bufSize">
686 # <ptype>GLint</ptype> *<name>values</name>
687 # </param>
688 # <param><ptype>GLenum</ptype> <name>shadertype</name></param>
689 # <param group="sync">
690 # <ptype>GLsync</ptype> <name>sync</name>
691 # </param>
692 # <param><ptype>GLuint</ptype> <name>baseAndCount</name>[2]</param>
694 assert xml_param.tag == 'param'
696 self.name = xml_param.find('./name').text
698 # Rename the parameter if its name is a reserved keyword in MSVC.
699 self.name = self.__PARAM_NAME_FIXES.get(self.name, self.name)
701 # Parse the C type.
702 c_type_text = list(xml_param.itertext())
704 # Could be <name> or <array_suffix>
705 c_type_text_end = c_type_text.pop(-1)
707 # We popped off <array_suffix>
708 if c_type_text_end.startswith('['):
709 # This is an array variable.
710 self.array_suffix = c_type_text_end
711 c_type_text.pop(-1) # Pop off the next one (<name>)
712 else:
713 self.array_suffix = ''
714 c_type_text = (t.strip() for t in c_type_text)
715 self.c_type = ' '.join(c_type_text).strip()
717 _log_debug('parsed {0}'.format(self))
719 def __repr__(self):
720 templ = ('{self.__class__.__name__}'
721 '(name={self.name!r}, type={self.c_type!r}, '
722 'suffix={self.array_suffix!r})')
723 return templ.format(self=self)
726 @functools.total_ordering
727 class Command(object):
728 """A <command> XML element.
730 Attributes:
731 name: The XML element's 'name' attribute, which is also the function
732 name.
734 c_return_type: For example, "void *".
736 alias: The XML element's 'alias' element. May be None.
738 param_list: List of that contains a CommandParam for each <param>
739 subelement.
741 requirements: A collection of each Requirement that exposes this
742 Command.
745 def __init__(self, xml_command):
746 """Parse a <command> element."""
748 # Example <command> element:
750 # <command>
751 # <proto>void <name>glTexSubImage2D</name></proto>
752 # <param group="TextureTarget">
753 # <ptype>GLenum</ptype> <name>target</name>
754 # </param>
755 # <param group="CheckedInt32">
756 # <ptype>GLint</ptype> <name>level</name>
757 # </param>
758 # <param group="CheckedInt32">
759 # <ptype>GLint</ptype> <name>xoffset</name>
760 # </param>
761 # <param group="CheckedInt32">
762 # <ptype>GLint</ptype> <name>yoffset</name>
763 # </param>
764 # <param><ptype>GLsizei</ptype> <name>width</name></param>
765 # <param><ptype>GLsizei</ptype> <name>height</name></param>
766 # <param group="PixelFormat">
767 # <ptype>GLenum</ptype> <name>format</name>
768 # </param>
769 # <param group="PixelType">
770 # <ptype>GLenum</ptype> <name>type</name>
771 # </param>
772 # <param len="COMPSIZE(format,type,width,height)">const void *
773 # <name>pixels</name>
774 # </param>
775 # <glx type="render" opcode="4100"/>
776 # <glx type="render" opcode="332" name="glTexSubImage2DPBO"
777 # comment="PBO protocol"/>
778 # </command>
781 assert xml_command.tag == 'command'
782 xml_proto = xml_command.find('./proto')
783 self.name = xml_proto.find('./name').text
784 _log_debug('start parsing Command(name={0!r})'.format(self.name))
786 self.requirements = set()
787 self.__vendor_namespace = None
789 # Parse the return type from the <proto> element.
791 # Example of a difficult <proto> element:
792 # <proto group="String">const <ptype>GLubyte</ptype> *
793 # <name>glGetStringi</name>
794 # </proto>
795 c_return_type_text = list(xml_proto.itertext())
796 # Pop off the text from the <name> subelement.
797 c_return_type_text.pop(-1)
798 c_return_type_text = (t.strip() for t in c_return_type_text)
799 self.c_return_type = ' '.join(c_return_type_text).strip()
801 # Parse alias info, if any.
802 xml_alias = xml_command.find('./alias')
803 if xml_alias is None:
804 self.alias = None
805 else:
806 self.alias = xml_alias.get('name')
808 self.param_list = [
809 CommandParam(xml_param)
810 for xml_param in xml_command.iterfind('./param')
813 _log_debug(('parsed {self.__class__.__name__}('
814 'name={self.name!r}, '
815 'alias={self.alias!r}, '
816 'prototype={self.c_prototype!r})').format(self=self))
818 def __hash__(self):
819 return hash('__Command_class_{}_'.format(self.name))
821 def __eq__(self, other):
822 return self.name == other.name
824 def __lt__(self, other):
825 return self.name < other.name
827 def __repr__(self):
828 templ = '{self.__class__.__name__}({self.name!r})'
829 return templ.format(self=self)
831 @property
832 def vendor_namespace(self):
833 if self.__vendor_namespace is None:
834 for req in self.requirements:
835 ext = req.extension
836 if ext is None:
837 continue
839 if ext.vendor_namespace is None:
840 continue
842 if self.name.endswith('_' + ext.vendor_namespace):
843 self.__vendor_namespace = ext.vendor_namespace
845 return self.__vendor_namespace
847 @property
848 def c_prototype(self):
849 """For example, "void glAccum(GLenum o, GLfloat value)"."""
850 return '{self.c_return_type} {self.name}({self.c_named_param_list})'\
851 .format(self=self)
853 @property
854 def c_funcptr_typedef(self):
855 """For example, "PFNGLACCUMROC" for glAccum."""
856 return 'PFN{0}PROC'.format(self.name).upper()
858 @property
859 def c_named_param_list(self):
860 """For example, "GLenum op, GLfloat value" for glAccum."""
861 return ', '.join(
862 '{p.c_type} {p.name}{p.array_suffix}'.format(p=param)
863 for param in self.param_list
866 @property
867 def c_unnamed_param_list(self):
868 """For example, "GLenum, GLfloat" for glAccum."""
869 return ', '.join(
870 '{param.c_type}{param.array_suffix}'.format(param=param)
871 for param in self.param_list
874 @property
875 def c_untyped_param_list(self):
876 """For example, "op, value" for glAccum."""
877 return ', '.join(
878 param.name
879 for param in self.param_list
883 @functools.total_ordering
884 class CommandAliasSet(ImmutableOrderedKeyedSet):
886 def __init__(self, commands):
887 ImmutableOrderedKeyedSet.__init__(self, key='name',
888 elems=sorted(commands))
889 self.__primary_command = None
890 self.__requirements = None
892 def __eq__(self, other):
893 return self.name == other.name
895 def __lt__(self, other):
896 return self.name < other.name
898 def __repr__(self):
899 templ = '{self.__class__.__name__}({self.name!r})'
900 return templ.format(self=self)
902 def __hash__(self):
903 return hash(repr(self))
905 @property
906 def name(self):
907 return self.primary_command.name
909 @property
910 def primary_command(self):
911 """The set's first command when sorted by name."""
912 for command in self:
913 return command
915 @property
916 def requirements(self):
917 """A sorted iterator over each Requirement that exposes this
918 CommandAliasSet.
920 if self.__requirements is None:
921 self.__requirements = sorted(
923 for command in self
924 for req in command.requirements
926 _log_debug('{0} sorted requirements: {1}'.format(
927 self, self.__requirements))
929 return iter(self.__requirements)
932 class CommandAliasMap(object):
934 def __init__(self):
935 self.__map = dict()
936 self.__sorted_unique_values = None
938 def __getitem__(self, command_name):
939 return self.__map[command_name]
941 def __iter__(self):
942 """A sorted iterator over the map's unique CommandAliasSet values."""
943 if self.__sorted_unique_values is None:
944 self.__sorted_unique_values = \
945 sorted(set(six.itervalues(self.__map)))
947 return iter(self.__sorted_unique_values)
949 def get(self, command_name, default):
950 return self.__map.get(command_name, default)
952 def add(self, command):
953 assert isinstance(command, Command)
954 name = command.name
955 _log_debug('adding command {0!r} to CommandAliasMap'.format(name))
957 name_set = self.get(name, None)
958 assert self.__is_set_mapping_complete(name_set)
960 alias_set = self.get(command.alias, None)
961 assert self.__is_set_mapping_complete(alias_set)
963 if name_set is alias_set and name_set is not None:
964 return
966 # After modifying the contained alias sets, the mapping will no longer
967 # be sorted.
968 self.__sorted_unique_values = None
970 new_set_elems = set((command,))
971 if name_set is not None:
972 new_set_elems.update(name_set)
973 if alias_set is not None:
974 new_set_elems.update(alias_set)
976 new_set = CommandAliasSet(new_set_elems)
977 for other_command in new_set:
978 self.__map[other_command.name] = new_set
979 if other_command.alias is not None:
980 self.__map[other_command.alias] = new_set
982 def __is_set_mapping_complete(self, alias_set):
983 if alias_set is None:
984 return True
986 for command in alias_set:
987 if self[command.name] is not alias_set:
988 return False
989 if command.alias is None:
990 continue
991 if self[command.alias] is not alias_set:
992 return False
994 return True
997 class EnumGroup(object):
998 """An <enums> element at path registry/enums.
1000 Attributes:
1001 name: The XML element's 'group' attribute. If the XML does not define
1002 'group', then this class invents one.
1004 type: The XML element's 'type' attribute. If the XML does not define
1005 'type', then this class invents one.
1007 start, end: The XML element's 'start' and 'end' attributes. Each may be
1008 None.
1010 enums: An OrderedKeyedSet of Enum that contains each <enum> subelement
1011 in this <enums>.
1014 # Each EnumGroup belongs to exactly one member of EnumGroup.TYPES.
1016 # Some members in EnumGroup.TYPES are invented and not present in gl.xml.
1017 # The only enum type defined explicitly in gl.xml is "bitmask", which
1018 # occurs as <enums type="bitmask">. However, in gl.xml each block of
1019 # non-bitmask enums is introduced by a comment that describes the block's
1020 # "type", even if the <enums> tag lacks a 'type' attribute. (Thanks,
1021 # Khronos, for encoding data in XML comments rather than the XML itself).
1022 # EnumGroup.TYPES lists such implicit comment-only types, with invented
1023 # names, alongside the types explicitly defined by <enums type=>.
1024 TYPES = (
1025 # Type 'default_namespace' is self-explanatory. It indicates the large
1026 # set of enums from 0x0000 to 0xffff that includes, for example,
1027 # GL_POINTS and GL_TEXTURE_2D.
1028 'default_namespace',
1030 # Type 'bitmask' is self-explanatory.
1031 'bitmask',
1033 # Type 'small_index' indicates a small namespace of non-bitmask enums.
1034 # As of Khronos revision 26792, 'small_index' groups generally contain
1035 # small numbers used for indexed access.
1036 'small_index',
1038 # Type 'special' is used only for the group named "SpecialNumbers". The
1039 # group contains enums such as GL_FALSE, GL_ZERO, and GL_INVALID_INDEX.
1040 'special',
1043 def __init__(self, xml_enums):
1044 """Parse an <enums> element."""
1046 # Example of a bitmask group:
1048 # <enums namespace="GL" group="SyncObjectMask" type="bitmask">
1049 # <enum value="0x00000001"
1050 # name="GL_SYNC_FLUSH_COMMANDS_BIT"/>
1051 # <enum value="0x00000001"
1052 # name="GL_SYNC_FLUSH_COMMANDS_BIT_APPLE"/>
1053 # </enums>
1055 # Example of a group that resides in OpenGL's default enum namespace:
1057 # <enums namespace="GL" start="0x0000" end="0x7FFF" vendor="ARB"
1058 # comment="...">
1059 # <enum value="0x0000" name="GL_POINTS"/>
1060 # <enum value="0x0001" name="GL_LINES"/>
1061 # <enum value="0x0002" name="GL_LINE_LOOP"/>
1062 # ...
1063 # </enums>
1065 # Example of a non-bitmask group that resides outside OpenGL's default
1066 # enum namespace:
1068 # <enums namespace="GL" group="PathRenderingTokenNV" vendor="NV">
1069 # <enum value="0x00" name="GL_CLOSE_PATH_NV"/>
1070 # <enum value="0x02" name="GL_MOVE_TO_NV"/>
1071 # <enum value="0x03" name="GL_RELATIVE_MOVE_TO_NV"/>
1072 # ...
1073 # </enums>
1076 self.name = xml_enums.get('group', None)
1077 _log_debug('start parsing {0}'.format(self))
1079 self.type = xml_enums.get('type', None)
1080 self.start = xml_enums.get('start', None)
1081 self.end = xml_enums.get('end', None)
1082 self.enums = []
1084 self.__invent_name_and_type()
1085 assert self.name is not None
1086 assert self.type in self.TYPES
1088 _log_debug('start parsing <enum> subelements of {0}'.format(self))
1089 self.enums = OrderedKeyedSet(key='name')
1090 for xml_enum in xml_enums.iterfind('./enum'):
1091 self.enums.add(Enum(self, xml_enum))
1092 _log_debug('parsed {0}'.format(self))
1094 def __repr__(self):
1095 templ = '{self.__class__.__name__}({self.name!r})'
1096 return templ.format(self=self)
1098 def __invent_name_and_type(self):
1099 """If the XML didn't define a name or type, invent one."""
1100 if self.name is None:
1101 assert self.type is None
1102 assert self.start is not None
1103 assert self.end is not None
1104 self.name = 'range_{self.start}_{self.end}'.format(self=self)
1105 self.type = 'default_namespace'
1106 elif self.type is None:
1107 self.type = 'small_index'
1108 elif self.name == 'SpecialNumbers':
1109 assert self.type is None
1110 self.type = 'special'
1113 @functools.total_ordering
1114 class Enum(object):
1115 """An <enum> XML element.
1117 Attributes:
1118 name, api: The XML element's 'name' and 'api' attributes.
1120 str_value, c_num_literal: Equivalent attributes. The XML element's
1121 'value' attribute.
1123 num_value: The long integer for str_value.
1125 requirements: A collection of each Requirement that exposes this Enum.
1128 def __init__(self, enum_group, xml_enum):
1129 """Parse an <enum> tag located at path registry/enums/enum."""
1131 # Example <enum> element:
1132 # <enum value="0x0000" name="GL_POINTS"/>
1134 assert isinstance(enum_group, EnumGroup)
1135 assert xml_enum.tag == 'enum'
1137 self.requirements = set()
1138 self.__vendor_namespace = None
1140 self.group = enum_group
1141 self.name = xml_enum.get('name')
1142 self.api = xml_enum.get('api')
1143 self.str_value = xml_enum.get('value')
1144 self.c_num_literal = self.str_value
1146 if '0x' in self.str_value.lower():
1147 base = 16
1148 else:
1149 base = 10
1151 if six.PY2:
1152 # long is undefined in python3, and we are aware of that
1153 # pylint: disable=undefined-variable
1154 self.num_value = long(self.str_value, base)
1155 else:
1156 assert six.PY3
1157 self.num_value = int(self.str_value, base)
1159 _log_debug('parsed {0}'.format(self))
1161 def __repr__(self):
1162 templ = ('{self.__class__.__name__}'
1163 '(name={self.name!r},'
1164 ' value={self.str_value!r})')
1165 return templ.format(self=self)
1167 def __eq__(self, other):
1168 if self.num_value != other.num_value:
1169 return False
1170 elif (self.vendor_namespace is None) != \
1171 (other.vendor_namespace is None):
1172 return False
1173 elif (self.vendor_namespace in Extension.RATIFIED_NAMESPACES) != \
1174 (other.vendor_namespace in Extension.RATIFIED_NAMESPACES):
1175 return False
1176 elif (self.vendor_namespace == 'EXT') != \
1177 (other.vendor_namespace == 'EXT'):
1178 return False
1179 elif self.name != other.name:
1180 return False
1181 return self.api == other.api
1183 def __lt__(self, other): # pylint: disable=too-many-return-statements
1184 """Less than.
1186 Sort by numerical value, then vendor_namspace (ratified first, then
1187 EXT), then by full name, and finally by api.
1189 This sort order ensures that names provided by core specifications
1190 precede those provided by ratified extensions, which proceed those
1191 provided by unratified extensions.
1193 For example: GL_RED < GL_RED_EXT < GL_RED_INTEL
1196 if self.num_value != other.num_value:
1197 if self.num_value < other.num_value:
1198 return True
1199 return False
1201 x = self.vendor_namespace is None
1202 y = other.vendor_namespace is None
1203 if x != y:
1204 if x and not y:
1205 return True
1206 return False
1208 x = self.vendor_namespace in Extension.RATIFIED_NAMESPACES
1209 y = other.vendor_namespace in Extension.RATIFIED_NAMESPACES
1210 if x != y:
1211 if x and not y:
1212 return True
1213 return False
1215 x = self.vendor_namespace == 'EXT'
1216 y = other.vendor_namespace == 'EXT'
1217 if x != y:
1218 if x and not y:
1219 return True
1220 return False
1222 if self.name != other.name:
1223 if self.name < other.name:
1224 return True
1225 return False
1227 return self.api < other.api
1229 @property
1230 def vendor_namespace(self):
1231 if self.__vendor_namespace is None:
1232 for req in self.requirements:
1233 ext = req.extension
1234 if ext is None:
1235 continue
1237 if ext.vendor_namespace is None:
1238 continue
1240 if self.name.endswith('_' + ext.vendor_namespace):
1241 self.__vendor_namespace = ext.vendor_namespace
1243 return self.__vendor_namespace