gles-3.0: fix build errors (trivial)
[piglit.git] / registry / gl.py
blobe87bf5ca553553a52c18b47f8407adcdee1319ce
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 print_function
29 import os.path
30 import re
31 import sys
32 import functools
33 from copy import copy, deepcopy
35 import six
37 # Export 'debug' so other Piglit modules can easily enable it.
38 debug = False
41 def _log_debug(msg):
42 if debug:
43 print('debug: {0}: {1}'.format(__name__, msg), file=sys.stderr)
46 # Prefer the external module 'lxml.etree' (it uses libxml2) over Python's
47 # builtin 'xml.etree.ElementTree'. It's faster.
48 try:
49 import lxml.etree as etree
50 _log_debug('etree is lxml.etree')
51 except ImportError:
52 import xml.etree.cElementTree as etree
53 _log_debug('etree is xml.etree.cElementTree')
56 def parse():
57 """Parse gl.xml and return a Registry object."""
58 filename = os.path.join(os.path.dirname(__file__), 'gl.xml')
59 xml_registry = etree.parse(filename).getroot()
60 _repair_xml(xml_registry)
61 return Registry(xml_registry)
64 def _repair_xml(xml_registry):
65 fixes = set((
66 'GL_ALL_ATTRIB_BITS',
67 'glOcclusionQueryEventMaskAMD',
68 'gles2_GL_ACTIVE_PROGRAM_EXT',
71 remove_queue = []
73 def defer_removal(parent, child):
74 remove_queue.append((parent, child))
76 for enums in xml_registry.iterfind('./enums'):
77 if ('GL_ALL_ATTRIB_BITS' in fixes and
78 enums.get('group') == 'AttribMask'):
79 # The XML defines GL_ALL_ATTRIB_BITS incorrectly with all bits
80 # set (0xFFFFFFFF). From the GL_ARB_multisample spec, v5:
82 # In order to avoid incompatibility with GL implementations
83 # that do not support SGIS_multisample, ALL_ATTRIB_BITS
84 # does not include MULTISAMPLE_BIT_ARB.
86 enum = enums.find("./enum[@name='GL_ALL_ATTRIB_BITS']")
87 enum.set('value', '0x000FFFFF')
89 fixes.remove('GL_ALL_ATTRIB_BITS')
90 continue
92 if ('glOcclusionQueryEventMaskAMD' in fixes and
93 enums.get('namespace') == 'OcclusionQueryEventMaskAMD'):
94 # This tag's attributes are totally broken.
95 enums.set('namespace', 'GL')
96 enums.set('group', 'OcclusionQueryEventMaskAMD')
97 enums.set('type', 'bitmask')
99 fixes.remove('glOcclusionQueryEventMaskAMD')
100 continue
102 if ('gles2_GL_ACTIVE_PROGRAM_EXT' in fixes and
103 enums.get('vendor') is not None and
104 enums.get('vendor') == 'ARB' and
105 enums.get('start') is not None and
106 enums.get('start') <= '0x8259' and
107 enums.get('end') is not None and
108 enums.get('end') >= '0x8259'):
109 # GL_ACTIVE_PROGRAM_EXT has different numerical values in GL
110 # (0x8B8D) and in GLES (0x8259). Remove the GLES value to avoid
111 # redefinition collisions.
112 bad_enum = enums.find(("./enum"
113 "[@value='0x8259']"
114 "[@name='GL_ACTIVE_PROGRAM_EXT']"
115 "[@api='gles2']"))
116 defer_removal(enums, bad_enum)
118 fixes.remove('gles2_GL_ACTIVE_PROGRAM_EXT')
119 continue
121 for (parent, child) in remove_queue:
122 parent.remove(child)
124 if len(fixes) > 0:
125 raise Exception('failed to apply some xml repairs: ' +
126 ', '.join(map(repr, fixes)))
129 class OrderedKeyedSet(object):
130 """A set with keyed elements that preserves order of element insertion.
132 Why waste words? Let's document the class with example code.
134 Example:
135 Cheese = namedtuple('Cheese', ('name', 'flavor'))
136 cheeses = OrderedKeyedSet(key='name')
137 cheeses.add(Cheese(name='cheddar', flavor='good'))
138 cheeses.add(Cheese(name='gouda', flavor='smells like feet'))
139 cheeses.add(Cheese(name='romano', flavor='awesome'))
141 # Elements are retrievable by key.
142 assert cheeses['gouda'].flavor == 'smells like feet'
144 # On key collision, the old element is removed.
145 cheeses.add(Cheese(name='gouda', flavor='ok i guess'))
146 assert cheeses['gouda'].flavor == 'ok i guess'
148 # The set preserves order of insertion. Replacement does not alter
149 # order.
150 assert list(cheeses)[2].name == 'romano'
152 # The set is iterable.
153 for cheese in cheeses:
154 print(cheese.name)
156 # Yet another example...
157 Bread = namedtuple('Bread', ('name', 'smell'))
158 breads = OrderedKeyedSet(key='name')
159 breads.add(Bread(name='como', smell='subtle')
160 breads.add(Bread(name='sourdough', smell='pleasant'))
162 # The set supports some common set operations, such as union.
163 breads_and_cheeses = breads | cheeses
164 assert len(breads_and_cheeses) == len(breads) + len(cheeses)
167 def __init__(self, key, elems=()):
168 """Create a new set with the given key.
170 The given 'key' defines how to calculate each element's key value. If
171 'key' is a string, then each key value is defined to be
172 `getattr(elem, key)`. If 'key' is a function, then the key value is
173 `key(elem)`.
176 # A linked list contains the set's items. Each list node is a 4-tuple
177 # [prev, next, key, value]. The root node is permanent.
178 root = []
179 root[:] = [root, root, None, None]
180 self.__list_root = root
182 # For quick retrieval, we map each key to its node. That is, each map
183 # pair has form {key: [prev, next, key, value])}.
184 self.__map = dict()
186 if isinstance(key, str):
187 self.__key_func = lambda elem: getattr(elem, key)
188 else:
189 self.__key_func = key
191 for e in elems:
192 self.add(e)
194 def __or__(self, other):
195 """Same as `union`."""
196 return self.union(other)
198 def __contains__(self, key):
199 return key in self.__map
201 def __copy__(self):
202 return OrderedKeyedSet(key=deepcopy(self.__key_func),
203 elems=iter(self))
205 def __getitem__(self, key):
206 return self.__map[key][3]
208 def __iter__(self):
209 return self.itervalues()
211 def __len__(self):
212 return len(self.__map)
214 def __repr__(self):
215 templ = '{self.__class__.__name__}({self.name!r})'
216 return templ.format(self=self)
218 def add(self, value):
219 key = self.__key_func(value)
220 node = self.__map.get(key, None)
221 if node is not None:
222 node[3] = value
223 else:
224 root = self.__list_root
225 old_tail = root[0]
226 new_tail = [old_tail, root, key, value]
227 new_tail[0][1] = new_tail
228 new_tail[1][0] = new_tail
229 self.__map[key] = new_tail
231 def clear(self):
232 self.__map.clear()
233 root = self.__list_root
234 root[:] = [root, root, None, None]
236 def extend(self, elems):
237 for e in elems:
238 self.add(e)
240 def get(self, key, default):
241 node = self.__map.get(key, None)
242 if node is not None:
243 return node[3]
244 else:
245 return default
247 def iteritems(self):
248 root = self.__list_root
249 node = root[1]
250 while node is not root:
251 yield (node[2], node[3])
252 node = node[1]
254 def iterkeys(self):
255 return (i[0] for i in self.iteritems())
257 def itervalues(self):
258 return (i[1] for i in self.iteritems())
260 def pop(self, key):
261 node = self.__map.pop(key)
262 node[0][1] = node[1]
263 node[1][0] = node[0]
264 return node[3]
266 def sort_by_key(self):
267 sorted_items = sorted(six.iteritems(self.__map))
268 self.clear()
269 for item in sorted_items:
270 self.add(item[1])
272 def sort_by_value(self):
273 sorted_values = sorted(six.itervalues(self.__map))
274 self.clear()
275 for value in sorted_values:
276 self.add(value)
278 def union(self, other):
279 """Return the union of two sets as a new set.
281 In the new set, all elements of the self set precede those of the other
282 set. The order of elements in the new set preserves the order of the
283 original sets.
285 The new set's key function is copied from self. On key collisions, set
286 y has precedence over x.
288 u = copy(self)
289 u.extend(other)
290 return u
293 class ImmutableOrderedKeyedSet(OrderedKeyedSet):
295 def __init__(self, key, elems):
296 self.__is_frozen = False
297 OrderedKeyedSet.__init__(self, key=key, elems=elems)
298 self.__is_frozen = True
300 def add(self, value):
301 if self.__is_frozen:
302 raise ImmutableError
303 else:
304 OrderedKeyedSet.add(self, value)
306 def pop(self, key):
307 raise ImmutableError
309 def clear(self):
310 raise ImmutableError
313 class ImmutableError(Exception):
314 pass
317 # Values that may appear in the XML attributes 'api' and 'supported'.
318 VALID_APIS = frozenset(('gl', 'glcore', 'gles1', 'gles2'))
321 class Registry(object):
322 """The toplevel <registry> element.
324 Attributes:
325 features: An OrderedKeyedSet that contains a Feature for each <feature>
326 subelement.
328 extensions: An OrderedKeyedSet that contains an Extension for each
329 <extension> subelement.
331 commands: An OrderedKeyedSet that contains a Command for each <command>
332 subelement.
334 command_alias_map: A CommandAliasMap that contains a CommandAliasSet
335 for each equivalence class of commands.
337 enum_groups: An OrderedKeyedSet that contains an EnumGroup for each
338 <enums> subelement.
340 enums: An OrderedKeyedSet that contains an Enum for each <enum>
341 subelement.
343 vendor_namespaces: A collection of all vendor prefixes and suffixes,
344 such as "ARB", "EXT", "CHROMIUM", and "NV".
347 def __init__(self, xml_registry):
348 """Parse the <registry> element."""
350 assert xml_registry.tag == 'registry'
352 self.command_alias_map = CommandAliasMap()
353 self.commands = OrderedKeyedSet(key='name')
354 self.enum_groups = []
355 self.enums = OrderedKeyedSet(key='name')
356 self.extensions = OrderedKeyedSet(key='name')
357 self.features = OrderedKeyedSet(key='name')
358 self.vendor_namespaces = set()
360 for xml_command in xml_registry.iterfind('./commands/command'):
361 command = Command(xml_command)
362 self.commands.add(command)
363 self.command_alias_map.add(command)
365 for xml_enums in xml_registry.iterfind('./enums'):
366 enum_group = EnumGroup(xml_enums)
367 self.enum_groups.append(enum_group)
368 for enum in enum_group.enums:
369 self.enums.add(enum)
371 for xml_feature in xml_registry.iterfind('./feature'):
372 feature = Feature(xml_feature, command_map=self.commands,
373 enum_map=self.enums)
374 self.features.add(feature)
376 for xml_ext in xml_registry.iterfind('./extensions/extension'):
377 ext = Extension(xml_ext, command_map=self.commands,
378 enum_map=self.enums)
379 self.extensions.add(ext)
380 self.vendor_namespaces.add(ext.vendor_namespace)
382 self.vendor_namespaces.remove(None)
385 @functools.total_ordering
386 class Feature(object):
387 """A <feature> XML element.
389 Attributes:
390 name: The XML element's 'name' attribute.
392 api: The XML element's 'api' attribute.
394 version_str: The XML element's 'number' attribute. For example, "3.1".
396 version_float: float(version_str)
398 version_int: int(10 * version_float)
400 requirements: A collection of Requirement for each Command and Enum
401 this Feature requires.
404 def __init__(self, xml_feature, command_map, enum_map):
405 """Parse a <feature> element."""
407 # Example <feature> element:
409 # <feature api="gles2" name="GL_ES_VERSION_3_1" number="3.1">
410 # <!-- arrays_of_arrays features -->
411 # <require/>
412 # <!-- compute_shader features -->
413 # <require>
414 # <command name="glDispatchCompute"/>
415 # <command name="glDispatchComputeIndirect"/>
416 # <enum name="GL_COMPUTE_SHADER"/>
417 # <enum name="GL_MAX_COMPUTE_UNIFORM_BLOCKS"/>
418 # ...
419 # </require>
420 # <!-- draw_indirect features -->
421 # <require>
422 # <command name="glDrawArraysIndirect"/>
423 # <command name="glDrawElementsIndirect"/>
424 # <enum name="GL_DRAW_INDIRECT_BUFFER"/>
425 # <enum name="GL_DRAW_INDIRECT_BUFFER_BINDING"/>
426 # </require>
427 # ...
428 # </feature>
430 assert xml_feature.tag == 'feature'
432 # Parse the <feature> tag's attributes.
433 self.name = xml_feature.get('name')
434 self.api = xml_feature.get('api')
435 self.is_gles = self.name.startswith('GL_ES')
437 self.version_str = xml_feature.get('number')
438 self.version_float = float(self.version_str)
439 self.version_int = int(10 * self.version_float)
441 self.__parse_requirements(xml_feature, command_map, enum_map)
443 assert self.api in VALID_APIS
444 assert len(self.requirements) > 0
446 def __eq__(self, other):
447 if self is other:
448 return True
449 elif isinstance(other, Extension):
450 return False
451 elif self.is_gles != other.is_gles:
452 return False
453 return self.name == other.name
455 def __lt__(self, other):
456 if isinstance(other, Extension):
457 return True
458 # Desktop GL before GLES
459 elif self.is_gles != other.is_gles:
460 if self.is_gles:
461 return False
462 else:
463 return True
464 return self.name < other.name
466 def __repr__(self):
467 templ = '{self.__class__.__name__}({self.name!r})'
468 return templ.format(self=self)
470 def __parse_requirements(self, xml_feature, command_map, enum_map):
471 """For each <command> and <enum> under a <require>, create
472 a Requirement that links this Feature to a Command or Enum.
474 self.requirements = set()
476 def link(x):
477 req = Requirement(provider=self, provided=x,
478 apis=frozenset((self.api,)))
479 self.requirements.add(req)
480 x.requirements.add(req)
482 for xml_cmd in xml_feature.iterfind('./require/command'):
483 cmd = command_map[xml_cmd.get('name')]
484 link(cmd)
485 for xml_enum in xml_feature.iterfind('./require/enum'):
486 enum = enum_map[xml_enum.get('name')]
487 link(enum)
490 @functools.total_ordering
491 class Extension(object):
492 """An <extension> XML element.
494 Attributes:
495 name: The XML element's 'name' attribute.
497 supported_apis: The set of api strings in the XML element's 'supported'
498 attribute. For example, set('gl', 'glcore').
500 vendor_namespace: For example, "AMD". May be None.
502 requirements: A collection of Requirement for each Command and Enum
503 this Extension requires.
506 __VENDOR_REGEX = re.compile(r'^GL_(?P<vendor_namespace>[A-Z]+)_')
507 RATIFIED_NAMESPACES = ('KHR', 'ARB', 'OES')
509 def __init__(self, xml_extension, command_map, enum_map):
510 """Parse an <extension> element."""
512 # Example <extension> element:
513 # <extension name="GL_ARB_ES2_compatibility" supported="gl|glcore">
514 # <require>
515 # <enum name="GL_FIXED"/>
516 # <enum name="GL_IMPLEMENTATION_COLOR_READ_TYPE"/>
517 # ...
518 # <command name="glReleaseShaderCompiler"/>
519 # <command name="glShaderBinary"/>
520 # ...
521 # </require>
522 # </extension>
524 assert xml_extension.tag == 'extension'
526 self.name = xml_extension.get('name')
528 self.vendor_namespace = None
529 match = Extension.__VENDOR_REGEX.match(self.name)
530 if match is not None:
531 groups = match.groupdict()
532 self.vendor_namespace = groups.get('vendor_namespace', None)
534 self.supported_apis = xml_extension.get('supported').split('|')
535 self.supported_apis = frozenset(self.supported_apis)
536 assert self.supported_apis <= VALID_APIS
538 self.__parse_requirements(xml_extension, command_map, enum_map)
540 def __eq__(self, other):
541 if self is other:
542 return True
543 elif isinstance(other, Feature):
544 return False
545 elif self.is_ratified != other.is_ratified:
546 return False
547 elif self.vendor_namespace == 'EXT' != other.vendor_namespace == 'EXT':
548 return False
549 return self.name == other.name
551 def __lt__(self, other):
552 if isinstance(other, Feature):
553 return False
554 elif self.is_ratified != other.is_ratified:
555 # sort ratified before unratified
556 if self.is_ratified:
557 return True
558 else:
559 return False
560 elif (other.vendor_namespace == 'EXT') != \
561 (self.vendor_namespace == 'EXT'):
562 # Sort EXT before others
563 if self.vendor_namespace == 'EXT':
564 return True
565 else:
566 return False
567 return self.name < other.name
569 def __repr__(self):
570 templ = '{self.__class__.__name__}(name={self.name!r})'
571 return templ.format(self=self)
573 @property
574 def is_ratified(self):
575 """True if the vendor namespace is one that traditionally requires
576 ratification by Khronos.
578 return self.vendor_namespace in self.RATIFIED_NAMESPACES
580 def __parse_requirements(self, xml_extension, command_map, enum_map):
581 """For each <command> and <enum> under a <require>, create
582 a Requirement that links this Extension to a Command or Enum.
584 self.requirements = set()
586 def link(xml_require, x):
587 api = xml_require.get('api', None)
588 if api is not None:
589 assert api in self.supported_apis
590 apis = frozenset((api,))
591 else:
592 apis = frozenset(self.supported_apis)
594 req = Requirement(provider=self, provided=x, apis=apis)
595 self.requirements.add(req)
596 x.requirements.add(req)
598 for xml_req in xml_extension.iterfind('./require'):
599 for xml_cmd in xml_req.iterfind('./command'):
600 cmd = command_map[xml_cmd.get('name')]
601 link(xml_req, cmd)
602 for xml_enum in xml_req.iterfind('./enum'):
603 enum = enum_map[xml_enum.get('name')]
604 link(xml_req, enum)
607 @functools.total_ordering
608 class Requirement(object):
609 """A <require> XML element, which links a provider (Feature or Extension)
610 to a provided (Command or Enum) for a set of apis.
613 def __init__(self, provider, provided, apis):
614 self.provider = provider
615 self.provided = provided
616 self.apis = frozenset(apis)
618 def choose_if(condition, obj):
619 if condition:
620 return obj
621 else:
622 return None
624 self.has_feature = isinstance(provider, Feature)
625 self.has_extension = isinstance(provider, Extension)
626 self.has_command = isinstance(provided, Command)
627 self.has_enum = isinstance(provided, Enum)
629 self.feature = choose_if(self.has_feature, self.provider)
630 self.extension = choose_if(self.has_extension, self.provider)
631 self.command = choose_if(self.has_command, self.provided)
632 self.enum = choose_if(self.has_enum, self.provided)
634 assert self.has_feature + self.has_extension == 1
635 assert self.has_command + self.has_enum == 1
636 assert self.apis <= VALID_APIS
638 _log_debug('created {0}'.format(self))
640 def __hash__(self):
641 return hash('__Requirement_class_{}_{}_'.format(
642 self.provider, self.provided))
644 def __eq__(self, other):
645 if self.provider != other.provider:
646 return False
647 elif self.provided != other.provided:
648 return False
649 return True
651 def __lt__(self, other):
652 if self.provider < other.provider:
653 return True
654 elif self.provided < other.provided:
655 return True
656 return False
658 def __repr__(self):
659 templ = ('{self.__class__.__name__}'
660 '(provider={self.provider.name!r},'
661 ' provided={self.provided.name!r},'
662 ' apis={api_tuple})')
663 return templ.format(self=self, api_tuple=tuple(self.apis))
666 class CommandParam(object):
667 """A <param> XML element at path command/param.
669 Attributes:
670 name
671 c_type
672 array_suffix
675 __PARAM_NAME_FIXES = {'near': 'hither', 'far': 'yon'}
677 def __init__(self, xml_param, log=None):
678 """Parse a <param> element."""
680 # Example <param> elements:
682 # <param>const <ptype>GLchar</ptype> *<name>name</name></param>
683 # <param len="1"><ptype>GLsizei</ptype> *<name>length</name></param>
684 # <param len="bufSize">
685 # <ptype>GLint</ptype> *<name>values</name>
686 # </param>
687 # <param><ptype>GLenum</ptype> <name>shadertype</name></param>
688 # <param group="sync">
689 # <ptype>GLsync</ptype> <name>sync</name>
690 # </param>
691 # <param><ptype>GLuint</ptype> <name>baseAndCount</name>[2]</param>
693 assert xml_param.tag == 'param'
695 self.name = xml_param.find('./name').text
697 # Rename the parameter if its name is a reserved keyword in MSVC.
698 self.name = self.__PARAM_NAME_FIXES.get(self.name, self.name)
700 # Parse the C type.
701 c_type_text = list(xml_param.itertext())
703 # Could be <name> or <array_suffix>
704 c_type_text_end = c_type_text.pop(-1)
706 # We popped off <array_suffix>
707 if c_type_text_end.startswith('['):
708 # This is an array variable.
709 self.array_suffix = c_type_text_end
710 c_type_text.pop(-1) # Pop off the next one (<name>)
711 else:
712 self.array_suffix = ''
713 c_type_text = (t.strip() for t in c_type_text)
714 self.c_type = ' '.join(c_type_text).strip()
716 _log_debug('parsed {0}'.format(self))
718 def __repr__(self):
719 templ = ('{self.__class__.__name__}'
720 '(name={self.name!r}, type={self.c_type!r}, '
721 'suffix={self.array_suffix!r})')
722 return templ.format(self=self)
725 @functools.total_ordering
726 class Command(object):
727 """A <command> XML element.
729 Attributes:
730 name: The XML element's 'name' attribute, which is also the function
731 name.
733 c_return_type: For example, "void *".
735 alias: The XML element's 'alias' element. May be None.
737 param_list: List of that contains a CommandParam for each <param>
738 subelement.
740 requirements: A collection of each Requirement that exposes this
741 Command.
744 def __init__(self, xml_command):
745 """Parse a <command> element."""
747 # Example <command> element:
749 # <command>
750 # <proto>void <name>glTexSubImage2D</name></proto>
751 # <param group="TextureTarget">
752 # <ptype>GLenum</ptype> <name>target</name>
753 # </param>
754 # <param group="CheckedInt32">
755 # <ptype>GLint</ptype> <name>level</name>
756 # </param>
757 # <param group="CheckedInt32">
758 # <ptype>GLint</ptype> <name>xoffset</name>
759 # </param>
760 # <param group="CheckedInt32">
761 # <ptype>GLint</ptype> <name>yoffset</name>
762 # </param>
763 # <param><ptype>GLsizei</ptype> <name>width</name></param>
764 # <param><ptype>GLsizei</ptype> <name>height</name></param>
765 # <param group="PixelFormat">
766 # <ptype>GLenum</ptype> <name>format</name>
767 # </param>
768 # <param group="PixelType">
769 # <ptype>GLenum</ptype> <name>type</name>
770 # </param>
771 # <param len="COMPSIZE(format,type,width,height)">const void *
772 # <name>pixels</name>
773 # </param>
774 # <glx type="render" opcode="4100"/>
775 # <glx type="render" opcode="332" name="glTexSubImage2DPBO"
776 # comment="PBO protocol"/>
777 # </command>
780 assert xml_command.tag == 'command'
781 xml_proto = xml_command.find('./proto')
782 self.name = xml_proto.find('./name').text
783 _log_debug('start parsing Command(name={0!r})'.format(self.name))
785 self.requirements = set()
786 self.__vendor_namespace = None
788 # Parse the return type from the <proto> element.
790 # Example of a difficult <proto> element:
791 # <proto group="String">const <ptype>GLubyte</ptype> *
792 # <name>glGetStringi</name>
793 # </proto>
794 c_return_type_text = list(xml_proto.itertext())
795 # Pop off the text from the <name> subelement.
796 c_return_type_text.pop(-1)
797 c_return_type_text = (t.strip() for t in c_return_type_text)
798 self.c_return_type = ' '.join(c_return_type_text).strip()
800 # Parse alias info, if any.
801 xml_alias = xml_command.find('./alias')
802 if xml_alias is None:
803 self.alias = None
804 else:
805 self.alias = xml_alias.get('name')
807 self.param_list = [
808 CommandParam(xml_param)
809 for xml_param in xml_command.iterfind('./param')
812 _log_debug(('parsed {self.__class__.__name__}('
813 'name={self.name!r}, '
814 'alias={self.alias!r}, '
815 'prototype={self.c_prototype!r})').format(self=self))
817 def __hash__(self):
818 return hash('__Command_class_{}_'.format(self.name))
820 def __eq__(self, other):
821 return self.name == other.name
823 def __lt__(self, other):
824 return self.name < other.name
826 def __repr__(self):
827 templ = '{self.__class__.__name__}({self.name!r})'
828 return templ.format(self=self)
830 @property
831 def vendor_namespace(self):
832 if self.__vendor_namespace is None:
833 for req in self.requirements:
834 ext = req.extension
835 if ext is None:
836 continue
838 if ext.vendor_namespace is None:
839 continue
841 if self.name.endswith('_' + ext.vendor_namespace):
842 self.__vendor_namespace = ext.vendor_namespace
844 return self.__vendor_namespace
846 @property
847 def c_prototype(self):
848 """For example, "void glAccum(GLenum o, GLfloat value)"."""
849 return '{self.c_return_type} {self.name}({self.c_named_param_list})'\
850 .format(self=self)
852 @property
853 def c_funcptr_typedef(self):
854 """For example, "PFNGLACCUMROC" for glAccum."""
855 return 'PFN{0}PROC'.format(self.name).upper()
857 @property
858 def c_named_param_list(self):
859 """For example, "GLenum op, GLfloat value" for glAccum."""
860 return ', '.join(
861 '{p.c_type} {p.name}{p.array_suffix}'.format(p=param)
862 for param in self.param_list
865 @property
866 def c_unnamed_param_list(self):
867 """For example, "GLenum, GLfloat" for glAccum."""
868 return ', '.join(
869 '{param.c_type}{param.array_suffix}'.format(param=param)
870 for param in self.param_list
873 @property
874 def c_untyped_param_list(self):
875 """For example, "op, value" for glAccum."""
876 return ', '.join(
877 param.name
878 for param in self.param_list
882 @functools.total_ordering
883 class CommandAliasSet(ImmutableOrderedKeyedSet):
885 def __init__(self, commands):
886 ImmutableOrderedKeyedSet.__init__(self, key='name',
887 elems=sorted(commands))
888 self.__primary_command = None
889 self.__requirements = None
891 def __eq__(self, other):
892 return self.name == other.name
894 def __lt__(self, other):
895 return self.name < other.name
897 def __repr__(self):
898 templ = '{self.__class__.__name__}({self.name!r})'
899 return templ.format(self=self)
901 def __hash__(self):
902 return hash(repr(self))
904 @property
905 def name(self):
906 return self.primary_command.name
908 @property
909 def primary_command(self):
910 """The set's first command when sorted by name."""
911 for command in self:
912 return command
914 @property
915 def requirements(self):
916 """A sorted iterator over each Requirement that exposes this
917 CommandAliasSet.
919 if self.__requirements is None:
920 self.__requirements = sorted(
922 for command in self
923 for req in command.requirements
925 _log_debug('{0} sorted requirements: {1}'.format(
926 self, self.__requirements))
928 return iter(self.__requirements)
931 class CommandAliasMap(object):
933 def __init__(self):
934 self.__map = dict()
935 self.__sorted_unique_values = None
937 def __getitem__(self, command_name):
938 return self.__map[command_name]
940 def __iter__(self):
941 """A sorted iterator over the map's unique CommandAliasSet values."""
942 if self.__sorted_unique_values is None:
943 self.__sorted_unique_values = \
944 sorted(set(six.itervalues(self.__map)))
946 return iter(self.__sorted_unique_values)
948 def get(self, command_name, default):
949 return self.__map.get(command_name, default)
951 def add(self, command):
952 assert isinstance(command, Command)
953 name = command.name
954 _log_debug('adding command {0!r} to CommandAliasMap'.format(name))
956 name_set = self.get(name, None)
957 assert self.__is_set_mapping_complete(name_set)
959 alias_set = self.get(command.alias, None)
960 assert self.__is_set_mapping_complete(alias_set)
962 if name_set is alias_set and name_set is not None:
963 return
965 # After modifying the contained alias sets, the mapping will no longer
966 # be sorted.
967 self.__sorted_unique_values = None
969 new_set_elems = set((command,))
970 if name_set is not None:
971 new_set_elems.update(name_set)
972 if alias_set is not None:
973 new_set_elems.update(alias_set)
975 new_set = CommandAliasSet(new_set_elems)
976 for other_command in new_set:
977 self.__map[other_command.name] = new_set
978 if other_command.alias is not None:
979 self.__map[other_command.alias] = new_set
981 def __is_set_mapping_complete(self, alias_set):
982 if alias_set is None:
983 return True
985 for command in alias_set:
986 if self[command.name] is not alias_set:
987 return False
988 if command.alias is None:
989 continue
990 if self[command.alias] is not alias_set:
991 return False
993 return True
996 class EnumGroup(object):
997 """An <enums> element at path registry/enums.
999 Attributes:
1000 name: The XML element's 'group' attribute. If the XML does not define
1001 'group', then this class invents one.
1003 type: The XML element's 'type' attribute. If the XML does not define
1004 'type', then this class invents one.
1006 start, end: The XML element's 'start' and 'end' attributes. Each may be
1007 None.
1009 enums: An OrderedKeyedSet of Enum that contains each <enum> subelement
1010 in this <enums>.
1013 # Each EnumGroup belongs to exactly one member of EnumGroup.TYPES.
1015 # Some members in EnumGroup.TYPES are invented and not present in gl.xml.
1016 # The only enum type defined explicitly in gl.xml is "bitmask", which
1017 # occurs as <enums type="bitmask">. However, in gl.xml each block of
1018 # non-bitmask enums is introduced by a comment that describes the block's
1019 # "type", even if the <enums> tag lacks a 'type' attribute. (Thanks,
1020 # Khronos, for encoding data in XML comments rather than the XML itself).
1021 # EnumGroup.TYPES lists such implicit comment-only types, with invented
1022 # names, alongside the types explicitly defined by <enums type=>.
1023 TYPES = (
1024 # Type 'default_namespace' is self-explanatory. It indicates the large
1025 # set of enums from 0x0000 to 0xffff that includes, for example,
1026 # GL_POINTS and GL_TEXTURE_2D.
1027 'default_namespace',
1029 # Type 'bitmask' is self-explanatory.
1030 'bitmask',
1032 # Type 'small_index' indicates a small namespace of non-bitmask enums.
1033 # As of Khronos revision 26792, 'small_index' groups generally contain
1034 # small numbers used for indexed access.
1035 'small_index',
1037 # Type 'special' is used only for the group named "SpecialNumbers". The
1038 # group contains enums such as GL_FALSE, GL_ZERO, and GL_INVALID_INDEX.
1039 'special',
1042 def __init__(self, xml_enums):
1043 """Parse an <enums> element."""
1045 # Example of a bitmask group:
1047 # <enums namespace="GL" group="SyncObjectMask" type="bitmask">
1048 # <enum value="0x00000001"
1049 # name="GL_SYNC_FLUSH_COMMANDS_BIT"/>
1050 # <enum value="0x00000001"
1051 # name="GL_SYNC_FLUSH_COMMANDS_BIT_APPLE"/>
1052 # </enums>
1054 # Example of a group that resides in OpenGL's default enum namespace:
1056 # <enums namespace="GL" start="0x0000" end="0x7FFF" vendor="ARB"
1057 # comment="...">
1058 # <enum value="0x0000" name="GL_POINTS"/>
1059 # <enum value="0x0001" name="GL_LINES"/>
1060 # <enum value="0x0002" name="GL_LINE_LOOP"/>
1061 # ...
1062 # </enums>
1064 # Example of a non-bitmask group that resides outside OpenGL's default
1065 # enum namespace:
1067 # <enums namespace="GL" group="PathRenderingTokenNV" vendor="NV">
1068 # <enum value="0x00" name="GL_CLOSE_PATH_NV"/>
1069 # <enum value="0x02" name="GL_MOVE_TO_NV"/>
1070 # <enum value="0x03" name="GL_RELATIVE_MOVE_TO_NV"/>
1071 # ...
1072 # </enums>
1075 self.name = xml_enums.get('group', None)
1076 _log_debug('start parsing {0}'.format(self))
1078 self.type = xml_enums.get('type', None)
1079 self.start = xml_enums.get('start', None)
1080 self.end = xml_enums.get('end', None)
1081 self.enums = []
1083 self.__invent_name_and_type()
1084 assert self.name is not None
1085 assert self.type in self.TYPES
1087 _log_debug('start parsing <enum> subelements of {0}'.format(self))
1088 self.enums = OrderedKeyedSet(key='name')
1089 for xml_enum in xml_enums.iterfind('./enum'):
1090 self.enums.add(Enum(self, xml_enum))
1091 _log_debug('parsed {0}'.format(self))
1093 def __repr__(self):
1094 templ = '{self.__class__.__name__}({self.name!r})'
1095 return templ.format(self=self)
1097 def __invent_name_and_type(self):
1098 """If the XML didn't define a name or type, invent one."""
1099 if self.name is None:
1100 assert self.type is None
1101 assert self.start is not None
1102 assert self.end is not None
1103 self.name = 'range_{self.start}_{self.end}'.format(self=self)
1104 self.type = 'default_namespace'
1105 elif self.type is None:
1106 self.type = 'small_index'
1107 elif self.name == 'SpecialNumbers':
1108 assert self.type is None
1109 self.type = 'special'
1112 @functools.total_ordering
1113 class Enum(object):
1114 """An <enum> XML element.
1116 Attributes:
1117 name, api: The XML element's 'name' and 'api' attributes.
1119 str_value, c_num_literal: Equivalent attributes. The XML element's
1120 'value' attribute.
1122 num_value: The long integer for str_value.
1124 requirements: A collection of each Requirement that exposes this Enum.
1127 def __init__(self, enum_group, xml_enum):
1128 """Parse an <enum> tag located at path registry/enums/enum."""
1130 # Example <enum> element:
1131 # <enum value="0x0000" name="GL_POINTS"/>
1133 assert isinstance(enum_group, EnumGroup)
1134 assert xml_enum.tag == 'enum'
1136 self.requirements = set()
1137 self.__vendor_namespace = None
1139 self.group = enum_group
1140 self.name = xml_enum.get('name')
1141 self.api = xml_enum.get('api')
1142 self.str_value = xml_enum.get('value')
1143 self.c_num_literal = self.str_value
1145 if '0x' in self.str_value.lower():
1146 base = 16
1147 else:
1148 base = 10
1150 if six.PY2:
1151 # long is undefined in python3, and we are aware of that
1152 # pylint: disable=undefined-variable
1153 self.num_value = long(self.str_value, base)
1154 else:
1155 assert six.PY3
1156 self.num_value = int(self.str_value, base)
1158 _log_debug('parsed {0}'.format(self))
1160 def __repr__(self):
1161 templ = ('{self.__class__.__name__}'
1162 '(name={self.name!r},'
1163 ' value={self.str_value!r})')
1164 return templ.format(self=self)
1166 def __eq__(self, other):
1167 if self.num_value != other.num_value:
1168 return False
1169 elif (self.vendor_namespace is None) != \
1170 (other.vendor_namespace is None):
1171 return False
1172 elif (self.vendor_namespace in Extension.RATIFIED_NAMESPACES) != \
1173 (other.vendor_namespace in Extension.RATIFIED_NAMESPACES):
1174 return False
1175 elif (self.vendor_namespace == 'EXT') != \
1176 (other.vendor_namespace == 'EXT'):
1177 return False
1178 elif self.name != other.name:
1179 return False
1180 return self.api == other.api
1182 def __lt__(self, other): # pylint: disable=too-many-return-statements
1183 """Less than.
1185 Sort by numerical value, then vendor_namspace (ratified first, then
1186 EXT), then by full name, and finally by api.
1188 This sort order ensures that names provided by core specifications
1189 precede those provided by ratified extensions, which proceed those
1190 provided by unratified extensions.
1192 For example: GL_RED < GL_RED_EXT < GL_RED_INTEL
1195 if self.num_value != other.num_value:
1196 if self.num_value < other.num_value:
1197 return True
1198 return False
1200 x = self.vendor_namespace is None
1201 y = other.vendor_namespace is None
1202 if x != y:
1203 if x and not y:
1204 return True
1205 return False
1207 x = self.vendor_namespace in Extension.RATIFIED_NAMESPACES
1208 y = other.vendor_namespace in Extension.RATIFIED_NAMESPACES
1209 if x != y:
1210 if x and not y:
1211 return True
1212 return False
1214 x = self.vendor_namespace == 'EXT'
1215 y = other.vendor_namespace == 'EXT'
1216 if x != y:
1217 if x and not y:
1218 return True
1219 return False
1221 if self.name != other.name:
1222 if self.name < other.name:
1223 return True
1224 return False
1226 return self.api < other.api
1228 @property
1229 def vendor_namespace(self):
1230 if self.__vendor_namespace is None:
1231 for req in self.requirements:
1232 ext = req.extension
1233 if ext is None:
1234 continue
1236 if ext.vendor_namespace is None:
1237 continue
1239 if self.name.endswith('_' + ext.vendor_namespace):
1240 self.__vendor_namespace = ext.vendor_namespace
1242 return self.__vendor_namespace