cmake: remove this 12 years old workaround taking too many lines
[piglit.git] / registry / gl.py
bloba671884103c52998fce8ee47a878f9204a397119
1 # coding=utf-8
2 # Copyright 2014 Intel Corporation
4 # Permission is hereby granted, free of charge, to any person obtaining a
5 # copy of this software and associated documentation files (the "Software"),
6 # to deal in the Software without restriction, including without limitation
7 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 # and/or sell copies of the Software, and to permit persons to whom the
9 # Software is furnished to do so, subject to the following conditions:
11 # The above copyright notice and this permission notice (including the next
12 # paragraph) shall be included in all copies or substantial portions of the
13 # Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 # IN THE SOFTWARE.
23 """
24 Parse gl.xml into Python objects.
25 """
27 import os.path
28 import re
29 import sys
30 import functools
31 from copy import copy, deepcopy
33 # Export 'debug' so other Piglit modules can easily enable it.
34 debug = False
37 def _log_debug(msg):
38 if debug:
39 print('debug: {0}: {1}'.format(__name__, msg), file=sys.stderr)
42 # Prefer the external module 'lxml.etree' (it uses libxml2) over Python's
43 # builtin 'xml.etree.ElementTree'. It's faster.
44 try:
45 import lxml.etree as etree
46 _log_debug('etree is lxml.etree')
47 except ImportError:
48 import xml.etree.ElementTree as etree
49 _log_debug('etree is xml.etree.ElementTree')
52 def parse():
53 """Parse gl.xml and return a Registry object."""
54 filename = os.path.join(os.path.dirname(__file__), 'gl.xml')
55 xml_registry = etree.parse(filename).getroot()
56 _repair_xml(xml_registry)
57 return Registry(xml_registry)
60 def _repair_xml(xml_registry):
61 fixes = set((
62 'GL_ALL_ATTRIB_BITS',
63 'gles2_GL_ACTIVE_PROGRAM_EXT',
66 remove_queue = []
68 def defer_removal(parent, child):
69 remove_queue.append((parent, child))
71 for enums in xml_registry.iterfind('./enums'):
72 if ('GL_ALL_ATTRIB_BITS' in fixes and
73 enums.get('group') == 'AttribMask'):
74 # The XML defines GL_ALL_ATTRIB_BITS incorrectly with all bits
75 # set (0xFFFFFFFF). From the GL_ARB_multisample spec, v5:
77 # In order to avoid incompatibility with GL implementations
78 # that do not support SGIS_multisample, ALL_ATTRIB_BITS
79 # does not include MULTISAMPLE_BIT_ARB.
81 enum = enums.find("./enum[@name='GL_ALL_ATTRIB_BITS']")
82 enum.set('value', '0x000FFFFF')
84 fixes.remove('GL_ALL_ATTRIB_BITS')
85 continue
87 if ('gles2_GL_ACTIVE_PROGRAM_EXT' in fixes and
88 enums.get('vendor') is not None and
89 enums.get('vendor') == 'ARB' and
90 enums.get('start') is not None and
91 enums.get('start') <= '0x8259' and
92 enums.get('end') is not None and
93 enums.get('end') >= '0x8259'):
94 # GL_ACTIVE_PROGRAM_EXT has different numerical values in GL
95 # (0x8B8D) and in GLES (0x8259). Remove the GLES value to avoid
96 # redefinition collisions.
97 bad_enum = enums.find(("./enum"
98 "[@value='0x8259']"
99 "[@name='GL_ACTIVE_PROGRAM_EXT']"
100 "[@api='gles2']"))
101 defer_removal(enums, bad_enum)
103 fixes.remove('gles2_GL_ACTIVE_PROGRAM_EXT')
104 continue
106 for (parent, child) in remove_queue:
107 parent.remove(child)
109 if len(fixes) > 0:
110 raise Exception('failed to apply some xml repairs: ' +
111 ', '.join(map(repr, fixes)))
114 class OrderedKeyedSet(object):
115 """A set with keyed elements that preserves order of element insertion.
117 Why waste words? Let's document the class with example code.
119 Example:
120 Cheese = namedtuple('Cheese', ('name', 'flavor'))
121 cheeses = OrderedKeyedSet(key='name')
122 cheeses.add(Cheese(name='cheddar', flavor='good'))
123 cheeses.add(Cheese(name='gouda', flavor='smells like feet'))
124 cheeses.add(Cheese(name='romano', flavor='awesome'))
126 # Elements are retrievable by key.
127 assert cheeses['gouda'].flavor == 'smells like feet'
129 # On key collision, the old element is removed.
130 cheeses.add(Cheese(name='gouda', flavor='ok i guess'))
131 assert cheeses['gouda'].flavor == 'ok i guess'
133 # The set preserves order of insertion. Replacement does not alter
134 # order.
135 assert list(cheeses)[2].name == 'romano'
137 # The set is iterable.
138 for cheese in cheeses:
139 print(cheese.name)
141 # Yet another example...
142 Bread = namedtuple('Bread', ('name', 'smell'))
143 breads = OrderedKeyedSet(key='name')
144 breads.add(Bread(name='como', smell='subtle')
145 breads.add(Bread(name='sourdough', smell='pleasant'))
147 # The set supports some common set operations, such as union.
148 breads_and_cheeses = breads | cheeses
149 assert len(breads_and_cheeses) == len(breads) + len(cheeses)
152 def __init__(self, key, elems=()):
153 """Create a new set with the given key.
155 The given 'key' defines how to calculate each element's key value. If
156 'key' is a string, then each key value is defined to be
157 `getattr(elem, key)`. If 'key' is a function, then the key value is
158 `key(elem)`.
161 # A linked list contains the set's items. Each list node is a 4-tuple
162 # [prev, next, key, value]. The root node is permanent.
163 root = []
164 root[:] = [root, root, None, None]
165 self.__list_root = root
167 # For quick retrieval, we map each key to its node. That is, each map
168 # pair has form {key: [prev, next, key, value])}.
169 self.__map = dict()
171 if isinstance(key, str):
172 self.__key_func = lambda elem: getattr(elem, key)
173 else:
174 self.__key_func = key
176 for e in elems:
177 self.add(e)
179 def __or__(self, other):
180 """Same as `union`."""
181 return self.union(other)
183 def __contains__(self, key):
184 return key in self.__map
186 def __copy__(self):
187 return OrderedKeyedSet(key=deepcopy(self.__key_func),
188 elems=iter(self))
190 def __getitem__(self, key):
191 return self.__map[key][3]
193 def __iter__(self):
194 return self.itervalues()
196 def __len__(self):
197 return len(self.__map)
199 def __repr__(self):
200 templ = '{self.__class__.__name__}({self.name!r})'
201 return templ.format(self=self)
203 def add(self, value):
204 key = self.__key_func(value)
205 node = self.__map.get(key, None)
206 if node is not None:
207 node[3] = value
208 else:
209 root = self.__list_root
210 old_tail = root[0]
211 new_tail = [old_tail, root, key, value]
212 new_tail[0][1] = new_tail
213 new_tail[1][0] = new_tail
214 self.__map[key] = new_tail
216 def clear(self):
217 self.__map.clear()
218 root = self.__list_root
219 root[:] = [root, root, None, None]
221 def extend(self, elems):
222 for e in elems:
223 self.add(e)
225 def get(self, key, default):
226 node = self.__map.get(key, None)
227 if node is not None:
228 return node[3]
229 else:
230 return default
232 def iteritems(self):
233 root = self.__list_root
234 node = root[1]
235 while node is not root:
236 yield (node[2], node[3])
237 node = node[1]
239 def iterkeys(self):
240 return (i[0] for i in self.iteritems())
242 def itervalues(self):
243 return (i[1] for i in self.iteritems())
245 def pop(self, key):
246 node = self.__map.pop(key)
247 node[0][1] = node[1]
248 node[1][0] = node[0]
249 return node[3]
251 def sort_by_key(self):
252 sorted_items = sorted(self.__map.items())
253 self.clear()
254 for item in sorted_items:
255 self.add(item[1])
257 def sort_by_value(self):
258 sorted_values = sorted(self.__map.values())
259 self.clear()
260 for value in sorted_values:
261 self.add(value)
263 def union(self, other):
264 """Return the union of two sets as a new set.
266 In the new set, all elements of the self set precede those of the other
267 set. The order of elements in the new set preserves the order of the
268 original sets.
270 The new set's key function is copied from self. On key collisions, set
271 y has precedence over x.
273 u = copy(self)
274 u.extend(other)
275 return u
278 class ImmutableOrderedKeyedSet(OrderedKeyedSet):
280 def __init__(self, key, elems):
281 self.__is_frozen = False
282 OrderedKeyedSet.__init__(self, key=key, elems=elems)
283 self.__is_frozen = True
285 def add(self, value):
286 if self.__is_frozen:
287 raise ImmutableError
288 else:
289 OrderedKeyedSet.add(self, value)
291 def pop(self, key):
292 raise ImmutableError
294 def clear(self):
295 raise ImmutableError
298 class ImmutableError(Exception):
299 pass
302 # Values that may appear in the XML attributes 'api' and 'supported'.
303 VALID_APIS = frozenset(('gl', 'glcore', 'gles1', 'gles2', 'glsc2', 'disabled'))
306 class Registry(object):
307 """The toplevel <registry> element.
309 Attributes:
310 features: An OrderedKeyedSet that contains a Feature for each <feature>
311 subelement.
313 extensions: An OrderedKeyedSet that contains an Extension for each
314 <extension> subelement.
316 commands: An OrderedKeyedSet that contains a Command for each <command>
317 subelement.
319 command_alias_map: A CommandAliasMap that contains a CommandAliasSet
320 for each equivalence class of commands.
322 enum_groups: An OrderedKeyedSet that contains an EnumGroup for each
323 <enums> subelement.
325 enums: An OrderedKeyedSet that contains an Enum for each <enum>
326 subelement.
328 vendor_namespaces: A collection of all vendor prefixes and suffixes,
329 such as "ARB", "EXT", "CHROMIUM", and "NV".
332 def __init__(self, xml_registry):
333 """Parse the <registry> element."""
335 assert xml_registry.tag == 'registry'
337 self.command_alias_map = CommandAliasMap()
338 self.commands = OrderedKeyedSet(key='name')
339 self.enum_groups = []
340 self.enums = OrderedKeyedSet(key='name')
341 self.extensions = OrderedKeyedSet(key='name')
342 self.features = OrderedKeyedSet(key='name')
343 self.vendor_namespaces = set()
345 for xml_command in xml_registry.iterfind('./commands/command'):
346 command = Command(xml_command)
347 self.commands.add(command)
348 self.command_alias_map.add(command)
350 for xml_enums in xml_registry.iterfind('./enums'):
351 enum_group = EnumGroup(xml_enums)
352 self.enum_groups.append(enum_group)
353 for enum in enum_group.enums:
354 self.enums.add(enum)
356 for xml_feature in xml_registry.iterfind('./feature'):
357 feature = Feature(xml_feature, command_map=self.commands,
358 enum_map=self.enums)
359 self.features.add(feature)
361 for xml_ext in xml_registry.iterfind('./extensions/extension'):
362 ext = Extension(xml_ext, command_map=self.commands,
363 enum_map=self.enums)
364 self.extensions.add(ext)
365 self.vendor_namespaces.add(ext.vendor_namespace)
367 self.vendor_namespaces.remove(None)
370 @functools.total_ordering
371 class Feature(object):
372 """A <feature> XML element.
374 Attributes:
375 name: The XML element's 'name' attribute.
377 api: The XML element's 'api' attribute.
379 version_str: The XML element's 'number' attribute. For example, "3.1".
381 version_float: float(version_str)
383 version_int: int(10 * version_float)
385 requirements: A collection of Requirement for each Command and Enum
386 this Feature requires.
389 def __init__(self, xml_feature, command_map, enum_map):
390 """Parse a <feature> element."""
392 # Example <feature> element:
394 # <feature api="gles2" name="GL_ES_VERSION_3_1" number="3.1">
395 # <!-- arrays_of_arrays features -->
396 # <require/>
397 # <!-- compute_shader features -->
398 # <require>
399 # <command name="glDispatchCompute"/>
400 # <command name="glDispatchComputeIndirect"/>
401 # <enum name="GL_COMPUTE_SHADER"/>
402 # <enum name="GL_MAX_COMPUTE_UNIFORM_BLOCKS"/>
403 # ...
404 # </require>
405 # <!-- draw_indirect features -->
406 # <require>
407 # <command name="glDrawArraysIndirect"/>
408 # <command name="glDrawElementsIndirect"/>
409 # <enum name="GL_DRAW_INDIRECT_BUFFER"/>
410 # <enum name="GL_DRAW_INDIRECT_BUFFER_BINDING"/>
411 # </require>
412 # ...
413 # </feature>
415 assert xml_feature.tag == 'feature'
417 # Parse the <feature> tag's attributes.
418 self.name = xml_feature.get('name')
419 self.api = xml_feature.get('api')
420 self.is_gles = self.name.startswith('GL_ES')
422 self.version_str = xml_feature.get('number')
423 self.version_float = float(self.version_str)
424 self.version_int = int(10 * self.version_float)
426 self.__parse_requirements(xml_feature, command_map, enum_map)
428 assert self.api in VALID_APIS
429 assert len(self.requirements) > 0
431 def __eq__(self, other):
432 if self is other:
433 return True
434 elif isinstance(other, Extension):
435 return False
436 elif self.is_gles != other.is_gles:
437 return False
438 return self.name == other.name
440 def __lt__(self, other):
441 if isinstance(other, Extension):
442 return True
443 # Desktop GL before GLES
444 elif self.is_gles != other.is_gles:
445 if self.is_gles:
446 return False
447 else:
448 return True
449 return self.name < other.name
451 def __repr__(self):
452 templ = '{self.__class__.__name__}({self.name!r})'
453 return templ.format(self=self)
455 def __parse_requirements(self, xml_feature, command_map, enum_map):
456 """For each <command> and <enum> under a <require>, create
457 a Requirement that links this Feature to a Command or Enum.
459 self.requirements = set()
461 def link(x):
462 req = Requirement(provider=self, provided=x,
463 apis=frozenset((self.api,)))
464 self.requirements.add(req)
465 x.requirements.add(req)
467 for xml_cmd in xml_feature.iterfind('./require/command'):
468 cmd = command_map[xml_cmd.get('name')]
469 link(cmd)
470 for xml_enum in xml_feature.iterfind('./require/enum'):
471 enum = enum_map[xml_enum.get('name')]
472 link(enum)
475 @functools.total_ordering
476 class Extension(object):
477 """An <extension> XML element.
479 Attributes:
480 name: The XML element's 'name' attribute.
482 supported_apis: The set of api strings in the XML element's 'supported'
483 attribute. For example, set('gl', 'glcore').
485 vendor_namespace: For example, "AMD". May be None.
487 requirements: A collection of Requirement for each Command and Enum
488 this Extension requires.
491 __VENDOR_REGEX = re.compile(r'^GL_(?P<vendor_namespace>[A-Z]+)_')
492 RATIFIED_NAMESPACES = ('KHR', 'ARB', 'OES')
494 def __init__(self, xml_extension, command_map, enum_map):
495 """Parse an <extension> element."""
497 # Example <extension> element:
498 # <extension name="GL_ARB_ES2_compatibility" supported="gl|glcore">
499 # <require>
500 # <enum name="GL_FIXED"/>
501 # <enum name="GL_IMPLEMENTATION_COLOR_READ_TYPE"/>
502 # ...
503 # <command name="glReleaseShaderCompiler"/>
504 # <command name="glShaderBinary"/>
505 # ...
506 # </require>
507 # </extension>
509 assert xml_extension.tag == 'extension'
511 self.name = xml_extension.get('name')
513 self.vendor_namespace = None
514 match = Extension.__VENDOR_REGEX.match(self.name)
515 if match is not None:
516 groups = match.groupdict()
517 self.vendor_namespace = groups.get('vendor_namespace', None)
519 self.supported_apis = xml_extension.get('supported').split('|')
520 self.supported_apis = frozenset(self.supported_apis)
521 assert self.supported_apis <= VALID_APIS
523 self.__parse_requirements(xml_extension, command_map, enum_map)
525 def __eq__(self, other):
526 if self is other:
527 return True
528 elif isinstance(other, Feature):
529 return False
530 elif self.is_ratified != other.is_ratified:
531 return False
532 elif self.vendor_namespace == 'EXT' != other.vendor_namespace == 'EXT':
533 return False
534 return self.name == other.name
536 def __lt__(self, other):
537 if isinstance(other, Feature):
538 return False
539 elif self.is_ratified != other.is_ratified:
540 # sort ratified before unratified
541 if self.is_ratified:
542 return True
543 else:
544 return False
545 elif (other.vendor_namespace == 'EXT') != \
546 (self.vendor_namespace == 'EXT'):
547 # Sort EXT before others
548 if self.vendor_namespace == 'EXT':
549 return True
550 else:
551 return False
552 return self.name < other.name
554 def __repr__(self):
555 templ = '{self.__class__.__name__}(name={self.name!r})'
556 return templ.format(self=self)
558 @property
559 def is_ratified(self):
560 """True if the vendor namespace is one that traditionally requires
561 ratification by Khronos.
563 return self.vendor_namespace in self.RATIFIED_NAMESPACES
565 def __parse_requirements(self, xml_extension, command_map, enum_map):
566 """For each <command> and <enum> under a <require>, create
567 a Requirement that links this Extension to a Command or Enum.
569 self.requirements = set()
571 def link(xml_require, x):
572 api = xml_require.get('api', None)
573 if api is not None:
574 assert api in self.supported_apis
575 apis = frozenset((api,))
576 else:
577 apis = frozenset(self.supported_apis)
579 req = Requirement(provider=self, provided=x, apis=apis)
580 self.requirements.add(req)
581 x.requirements.add(req)
583 for xml_req in xml_extension.iterfind('./require'):
584 for xml_cmd in xml_req.iterfind('./command'):
585 cmd = command_map[xml_cmd.get('name')]
586 link(xml_req, cmd)
587 for xml_enum in xml_req.iterfind('./enum'):
588 enum = enum_map[xml_enum.get('name')]
589 link(xml_req, enum)
592 @functools.total_ordering
593 class Requirement(object):
594 """A <require> XML element, which links a provider (Feature or Extension)
595 to a provided (Command or Enum) for a set of apis.
598 def __init__(self, provider, provided, apis):
599 self.provider = provider
600 self.provided = provided
601 self.apis = frozenset(apis)
603 def choose_if(condition, obj):
604 if condition:
605 return obj
606 else:
607 return None
609 self.has_feature = isinstance(provider, Feature)
610 self.has_extension = isinstance(provider, Extension)
611 self.has_command = isinstance(provided, Command)
612 self.has_enum = isinstance(provided, Enum)
614 self.feature = choose_if(self.has_feature, self.provider)
615 self.extension = choose_if(self.has_extension, self.provider)
616 self.command = choose_if(self.has_command, self.provided)
617 self.enum = choose_if(self.has_enum, self.provided)
619 assert self.has_feature + self.has_extension == 1
620 assert self.has_command + self.has_enum == 1
621 assert self.apis <= VALID_APIS
623 _log_debug('created {0}'.format(self))
625 def __hash__(self):
626 return hash('__Requirement_class_{}_{}_'.format(
627 self.provider, self.provided))
629 def __eq__(self, other):
630 if self.provider != other.provider:
631 return False
632 elif self.provided != other.provided:
633 return False
634 return True
636 def __lt__(self, other):
637 if self.provider < other.provider:
638 return True
639 elif self.provided < other.provided:
640 return True
641 return False
643 def __repr__(self):
644 templ = ('{self.__class__.__name__}'
645 '(provider={self.provider.name!r},'
646 ' provided={self.provided.name!r},'
647 ' apis={api_tuple})')
648 return templ.format(self=self, api_tuple=tuple(self.apis))
651 class CommandParam(object):
652 """A <param> XML element at path command/param.
654 Attributes:
655 name
656 c_type
657 array_suffix
660 __PARAM_NAME_FIXES = {'near': 'hither', 'far': 'yon'}
662 def __init__(self, xml_param, log=None):
663 """Parse a <param> element."""
665 # Example <param> elements:
667 # <param>const <ptype>GLchar</ptype> *<name>name</name></param>
668 # <param len="1"><ptype>GLsizei</ptype> *<name>length</name></param>
669 # <param len="bufSize">
670 # <ptype>GLint</ptype> *<name>values</name>
671 # </param>
672 # <param><ptype>GLenum</ptype> <name>shadertype</name></param>
673 # <param group="sync">
674 # <ptype>GLsync</ptype> <name>sync</name>
675 # </param>
676 # <param><ptype>GLuint</ptype> <name>baseAndCount</name>[2]</param>
678 assert xml_param.tag == 'param'
680 self.name = xml_param.find('./name').text
682 # Rename the parameter if its name is a reserved keyword in MSVC.
683 self.name = self.__PARAM_NAME_FIXES.get(self.name, self.name)
685 # Parse the C type.
686 c_type_text = list(xml_param.itertext())
688 # Could be <name> or <array_suffix>
689 c_type_text_end = c_type_text.pop(-1)
691 # We popped off <array_suffix>
692 if c_type_text_end.startswith('['):
693 # This is an array variable.
694 self.array_suffix = c_type_text_end
695 c_type_text.pop(-1) # Pop off the next one (<name>)
696 else:
697 self.array_suffix = ''
698 c_type_text = (t.strip() for t in c_type_text)
699 self.c_type = ' '.join(c_type_text).strip()
701 _log_debug('parsed {0}'.format(self))
703 def __repr__(self):
704 templ = ('{self.__class__.__name__}'
705 '(name={self.name!r}, type={self.c_type!r}, '
706 'suffix={self.array_suffix!r})')
707 return templ.format(self=self)
710 @functools.total_ordering
711 class Command(object):
712 """A <command> XML element.
714 Attributes:
715 name: The XML element's 'name' attribute, which is also the function
716 name.
718 c_return_type: For example, "void *".
720 alias: The XML element's 'alias' element. May be None.
722 param_list: List of that contains a CommandParam for each <param>
723 subelement.
725 requirements: A collection of each Requirement that exposes this
726 Command.
729 def __init__(self, xml_command):
730 """Parse a <command> element."""
732 # Example <command> element:
734 # <command>
735 # <proto>void <name>glTexSubImage2D</name></proto>
736 # <param group="TextureTarget">
737 # <ptype>GLenum</ptype> <name>target</name>
738 # </param>
739 # <param group="CheckedInt32">
740 # <ptype>GLint</ptype> <name>level</name>
741 # </param>
742 # <param group="CheckedInt32">
743 # <ptype>GLint</ptype> <name>xoffset</name>
744 # </param>
745 # <param group="CheckedInt32">
746 # <ptype>GLint</ptype> <name>yoffset</name>
747 # </param>
748 # <param><ptype>GLsizei</ptype> <name>width</name></param>
749 # <param><ptype>GLsizei</ptype> <name>height</name></param>
750 # <param group="PixelFormat">
751 # <ptype>GLenum</ptype> <name>format</name>
752 # </param>
753 # <param group="PixelType">
754 # <ptype>GLenum</ptype> <name>type</name>
755 # </param>
756 # <param len="COMPSIZE(format,type,width,height)">const void *
757 # <name>pixels</name>
758 # </param>
759 # <glx type="render" opcode="4100"/>
760 # <glx type="render" opcode="332" name="glTexSubImage2DPBO"
761 # comment="PBO protocol"/>
762 # </command>
765 assert xml_command.tag == 'command'
766 xml_proto = xml_command.find('./proto')
767 self.name = xml_proto.find('./name').text
768 _log_debug('start parsing Command(name={0!r})'.format(self.name))
770 self.requirements = set()
771 self.__vendor_namespace = None
773 # Parse the return type from the <proto> element.
775 # Example of a difficult <proto> element:
776 # <proto group="String">const <ptype>GLubyte</ptype> *
777 # <name>glGetStringi</name>
778 # </proto>
779 c_return_type_text = list(xml_proto.itertext())
780 # Pop off the text from the <name> subelement.
781 c_return_type_text.pop(-1)
782 c_return_type_text = (t.strip() for t in c_return_type_text)
783 self.c_return_type = ' '.join(c_return_type_text).strip()
785 # Parse alias info, if any.
786 xml_alias = xml_command.find('./alias')
787 if xml_alias is None:
788 self.alias = None
789 else:
790 self.alias = xml_alias.get('name')
792 self.param_list = [
793 CommandParam(xml_param)
794 for xml_param in xml_command.iterfind('./param')
797 _log_debug(('parsed {self.__class__.__name__}('
798 'name={self.name!r}, '
799 'alias={self.alias!r}, '
800 'prototype={self.c_prototype!r})').format(self=self))
802 def __hash__(self):
803 return hash('__Command_class_{}_'.format(self.name))
805 def __eq__(self, other):
806 return self.name == other.name
808 def __lt__(self, other):
809 return self.name < other.name
811 def __repr__(self):
812 templ = '{self.__class__.__name__}({self.name!r})'
813 return templ.format(self=self)
815 @property
816 def vendor_namespace(self):
817 if self.__vendor_namespace is None:
818 for req in self.requirements:
819 ext = req.extension
820 if ext is None:
821 continue
823 if ext.vendor_namespace is None:
824 continue
826 if self.name.endswith('_' + ext.vendor_namespace):
827 self.__vendor_namespace = ext.vendor_namespace
829 return self.__vendor_namespace
831 @property
832 def c_prototype(self):
833 """For example, "void glAccum(GLenum o, GLfloat value)"."""
834 return '{self.c_return_type} {self.name}({self.c_named_param_list})'\
835 .format(self=self)
837 @property
838 def c_funcptr_typedef(self):
839 """For example, "PFNGLACCUMROC" for glAccum."""
840 return 'PFN{0}PROC'.format(self.name).upper()
842 @property
843 def c_named_param_list(self):
844 """For example, "GLenum op, GLfloat value" for glAccum."""
845 return ', '.join(
846 '{p.c_type} {p.name}{p.array_suffix}'.format(p=param)
847 for param in self.param_list
850 @property
851 def c_unnamed_param_list(self):
852 """For example, "GLenum, GLfloat" for glAccum."""
853 return ', '.join(
854 '{param.c_type}{param.array_suffix}'.format(param=param)
855 for param in self.param_list
858 @property
859 def c_untyped_param_list(self):
860 """For example, "op, value" for glAccum."""
861 return ', '.join(
862 param.name
863 for param in self.param_list
867 @functools.total_ordering
868 class CommandAliasSet(ImmutableOrderedKeyedSet):
870 def __init__(self, commands):
871 ImmutableOrderedKeyedSet.__init__(self, key='name',
872 elems=sorted(commands))
873 self.__primary_command = None
874 self.__requirements = None
876 def __eq__(self, other):
877 return self.name == other.name
879 def __lt__(self, other):
880 return self.name < other.name
882 def __repr__(self):
883 templ = '{self.__class__.__name__}({self.name!r})'
884 return templ.format(self=self)
886 def __hash__(self):
887 return hash(repr(self))
889 @property
890 def name(self):
891 return self.primary_command.name
893 @property
894 def primary_command(self):
895 """The set's first command when sorted by name."""
896 for command in self:
897 return command
899 @property
900 def requirements(self):
901 """A sorted iterator over each Requirement that exposes this
902 CommandAliasSet.
904 if self.__requirements is None:
905 self.__requirements = sorted(
907 for command in self
908 for req in command.requirements
910 _log_debug('{0} sorted requirements: {1}'.format(
911 self, self.__requirements))
913 return iter(self.__requirements)
916 class CommandAliasMap(object):
918 def __init__(self):
919 self.__map = dict()
920 self.__sorted_unique_values = None
922 def __getitem__(self, command_name):
923 return self.__map[command_name]
925 def __iter__(self):
926 """A sorted iterator over the map's unique CommandAliasSet values."""
927 if self.__sorted_unique_values is None:
928 self.__sorted_unique_values = \
929 sorted(set(self.__map.values()))
931 return iter(self.__sorted_unique_values)
933 def get(self, command_name, default):
934 return self.__map.get(command_name, default)
936 def add(self, command):
937 assert isinstance(command, Command)
938 name = command.name
939 _log_debug('adding command {0!r} to CommandAliasMap'.format(name))
941 name_set = self.get(name, None)
942 assert self.__is_set_mapping_complete(name_set)
944 alias_set = self.get(command.alias, None)
945 assert self.__is_set_mapping_complete(alias_set)
947 if name_set is alias_set and name_set is not None:
948 return
950 # After modifying the contained alias sets, the mapping will no longer
951 # be sorted.
952 self.__sorted_unique_values = None
954 new_set_elems = set((command,))
955 if name_set is not None:
956 new_set_elems.update(name_set)
957 if alias_set is not None:
958 new_set_elems.update(alias_set)
960 new_set = CommandAliasSet(new_set_elems)
961 for other_command in new_set:
962 self.__map[other_command.name] = new_set
963 if other_command.alias is not None:
964 self.__map[other_command.alias] = new_set
966 def __is_set_mapping_complete(self, alias_set):
967 if alias_set is None:
968 return True
970 for command in alias_set:
971 if self[command.name] is not alias_set:
972 return False
973 if command.alias is None:
974 continue
975 if self[command.alias] is not alias_set:
976 return False
978 return True
981 class EnumGroup(object):
982 """An <enums> element at path registry/enums.
984 Attributes:
985 name: The XML element's 'group' attribute. If the XML does not define
986 'group', then this class invents one.
988 type: The XML element's 'type' attribute. If the XML does not define
989 'type', then this class invents one.
991 start, end: The XML element's 'start' and 'end' attributes. Each may be
992 None.
994 enums: An OrderedKeyedSet of Enum that contains each <enum> subelement
995 in this <enums>.
998 # Each EnumGroup belongs to exactly one member of EnumGroup.TYPES.
1000 # Some members in EnumGroup.TYPES are invented and not present in gl.xml.
1001 # The only enum type defined explicitly in gl.xml is "bitmask", which
1002 # occurs as <enums type="bitmask">. However, in gl.xml each block of
1003 # non-bitmask enums is introduced by a comment that describes the block's
1004 # "type", even if the <enums> tag lacks a 'type' attribute. (Thanks,
1005 # Khronos, for encoding data in XML comments rather than the XML itself).
1006 # EnumGroup.TYPES lists such implicit comment-only types, with invented
1007 # names, alongside the types explicitly defined by <enums type=>.
1008 TYPES = (
1009 # Type 'default_namespace' is self-explanatory. It indicates the large
1010 # set of enums from 0x0000 to 0xffff that includes, for example,
1011 # GL_POINTS and GL_TEXTURE_2D.
1012 'default_namespace',
1014 # Type 'bitmask' is self-explanatory.
1015 'bitmask',
1017 # Type 'small_index' indicates a small namespace of non-bitmask enums.
1018 # As of Khronos revision 26792, 'small_index' groups generally contain
1019 # small numbers used for indexed access.
1020 'small_index',
1022 # Type 'special' is used only for the group named "SpecialNumbers". The
1023 # group contains enums such as GL_FALSE, GL_ZERO, and GL_INVALID_INDEX.
1024 'special',
1027 def __init__(self, xml_enums):
1028 """Parse an <enums> element."""
1030 # Example of a bitmask group:
1032 # <enums namespace="GL" group="SyncObjectMask" type="bitmask">
1033 # <enum value="0x00000001"
1034 # name="GL_SYNC_FLUSH_COMMANDS_BIT"/>
1035 # <enum value="0x00000001"
1036 # name="GL_SYNC_FLUSH_COMMANDS_BIT_APPLE"/>
1037 # </enums>
1039 # Example of a group that resides in OpenGL's default enum namespace:
1041 # <enums namespace="GL" start="0x0000" end="0x7FFF" vendor="ARB"
1042 # comment="...">
1043 # <enum value="0x0000" name="GL_POINTS"/>
1044 # <enum value="0x0001" name="GL_LINES"/>
1045 # <enum value="0x0002" name="GL_LINE_LOOP"/>
1046 # ...
1047 # </enums>
1049 # Example of a non-bitmask group that resides outside OpenGL's default
1050 # enum namespace:
1052 # <enums namespace="GL" group="PathRenderingTokenNV" vendor="NV">
1053 # <enum value="0x00" name="GL_CLOSE_PATH_NV"/>
1054 # <enum value="0x02" name="GL_MOVE_TO_NV"/>
1055 # <enum value="0x03" name="GL_RELATIVE_MOVE_TO_NV"/>
1056 # ...
1057 # </enums>
1060 self.name = xml_enums.get('group', None)
1061 _log_debug('start parsing {0}'.format(self))
1063 self.type = xml_enums.get('type', None)
1064 self.start = xml_enums.get('start', None)
1065 self.end = xml_enums.get('end', None)
1066 self.enums = []
1068 self.__invent_name_and_type()
1069 assert self.name is not None
1070 assert self.type in self.TYPES
1072 _log_debug('start parsing <enum> subelements of {0}'.format(self))
1073 self.enums = OrderedKeyedSet(key='name')
1074 for xml_enum in xml_enums.iterfind('./enum'):
1075 self.enums.add(Enum(self, xml_enum))
1076 _log_debug('parsed {0}'.format(self))
1078 def __repr__(self):
1079 templ = '{self.__class__.__name__}({self.name!r})'
1080 return templ.format(self=self)
1082 def __invent_name_and_type(self):
1083 """If the XML didn't define a name or type, invent one."""
1084 if self.name is None:
1085 assert self.type is None
1086 assert self.start is not None
1087 assert self.end is not None
1088 self.name = 'range_{self.start}_{self.end}'.format(self=self)
1089 self.type = 'default_namespace'
1090 elif self.type is None:
1091 self.type = 'small_index'
1092 elif self.name == 'SpecialNumbers':
1093 assert self.type is None
1094 self.type = 'special'
1097 @functools.total_ordering
1098 class Enum(object):
1099 """An <enum> XML element.
1101 Attributes:
1102 name, api: The XML element's 'name' and 'api' attributes.
1104 str_value, c_num_literal: Equivalent attributes. The XML element's
1105 'value' attribute.
1107 num_value: The long integer for str_value.
1109 requirements: A collection of each Requirement that exposes this Enum.
1112 def __init__(self, enum_group, xml_enum):
1113 """Parse an <enum> tag located at path registry/enums/enum."""
1115 # Example <enum> element:
1116 # <enum value="0x0000" name="GL_POINTS"/>
1118 assert isinstance(enum_group, EnumGroup)
1119 assert xml_enum.tag == 'enum'
1121 self.requirements = set()
1122 self.__vendor_namespace = None
1124 self.group = enum_group
1125 self.name = xml_enum.get('name')
1126 self.api = xml_enum.get('api')
1127 self.str_value = xml_enum.get('value')
1128 self.c_num_literal = self.str_value
1130 if '0x' in self.str_value.lower():
1131 base = 16
1132 else:
1133 base = 10
1135 self.num_value = int(self.str_value, base)
1137 _log_debug('parsed {0}'.format(self))
1139 def __repr__(self):
1140 templ = ('{self.__class__.__name__}'
1141 '(name={self.name!r},'
1142 ' value={self.str_value!r})')
1143 return templ.format(self=self)
1145 def __eq__(self, other):
1146 if self.num_value != other.num_value:
1147 return False
1148 elif (self.vendor_namespace is None) != \
1149 (other.vendor_namespace is None):
1150 return False
1151 elif (self.vendor_namespace in Extension.RATIFIED_NAMESPACES) != \
1152 (other.vendor_namespace in Extension.RATIFIED_NAMESPACES):
1153 return False
1154 elif (self.vendor_namespace == 'EXT') != \
1155 (other.vendor_namespace == 'EXT'):
1156 return False
1157 elif self.name != other.name:
1158 return False
1159 return self.api == other.api
1161 def __lt__(self, other): # pylint: disable=too-many-return-statements
1162 """Less than.
1164 Sort by numerical value, then vendor_namspace (ratified first, then
1165 EXT), then by full name, and finally by api.
1167 This sort order ensures that names provided by core specifications
1168 precede those provided by ratified extensions, which proceed those
1169 provided by unratified extensions.
1171 For example: GL_RED < GL_RED_EXT < GL_RED_INTEL
1174 if self.num_value != other.num_value:
1175 if self.num_value < other.num_value:
1176 return True
1177 return False
1179 x = self.vendor_namespace is None
1180 y = other.vendor_namespace is None
1181 if x != y:
1182 if x and not y:
1183 return True
1184 return False
1186 x = self.vendor_namespace in Extension.RATIFIED_NAMESPACES
1187 y = other.vendor_namespace in Extension.RATIFIED_NAMESPACES
1188 if x != y:
1189 if x and not y:
1190 return True
1191 return False
1193 x = self.vendor_namespace == 'EXT'
1194 y = other.vendor_namespace == 'EXT'
1195 if x != y:
1196 if x and not y:
1197 return True
1198 return False
1200 if self.name != other.name:
1201 if self.name < other.name:
1202 return True
1203 return False
1205 return self.api < other.api
1207 @property
1208 def vendor_namespace(self):
1209 if self.__vendor_namespace is None:
1210 for req in self.requirements:
1211 ext = req.extension
1212 if ext is None:
1213 continue
1215 if ext.vendor_namespace is None:
1216 continue
1218 if self.name.endswith('_' + ext.vendor_namespace):
1219 self.__vendor_namespace = ext.vendor_namespace
1221 return self.__vendor_namespace