3 # (C) Copyright 2015, NVIDIA CORPORATION.
6 # Permission is hereby granted, free of charge, to any person obtaining a
7 # copy of this software and associated documentation files (the "Software"),
8 # to deal in the Software without restriction, including without limitation
9 # on the rights to use, copy, modify, merge, publish, distribute, sub
10 # license, and/or sell copies of the Software, and to permit persons to whom
11 # the Software is furnished to do so, subject to the following conditions:
13 # The above copyright notice and this permission notice (including the next
14 # paragraph) shall be included in all copies or substantial portions of the
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20 # IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 # Kyle Brenneman <kbrenneman@nvidia.com>
31 import xml
.etree
.cElementTree
as etree
33 MAPI_TABLE_NUM_DYNAMIC
= 4096
35 _LIBRARY_FEATURE_NAMES
= {
36 # libGL and libGLdiapatch both include every function.
39 "opengl" : frozenset(( "GL_VERSION_1_0", "GL_VERSION_1_1",
40 "GL_VERSION_1_2", "GL_VERSION_1_3", "GL_VERSION_1_4", "GL_VERSION_1_5",
41 "GL_VERSION_2_0", "GL_VERSION_2_1", "GL_VERSION_3_0", "GL_VERSION_3_1",
42 "GL_VERSION_3_2", "GL_VERSION_3_3", "GL_VERSION_4_0", "GL_VERSION_4_1",
43 "GL_VERSION_4_2", "GL_VERSION_4_3", "GL_VERSION_4_4", "GL_VERSION_4_5",
45 "glesv1" : frozenset(("GL_VERSION_ES_CM_1_0", "GL_OES_point_size_array")),
46 "glesv2" : frozenset(("GL_ES_VERSION_2_0", "GL_ES_VERSION_3_0",
47 "GL_ES_VERSION_3_1", "GL_ES_VERSION_3_2",
51 def getFunctions(xmlFiles
):
53 Reads an XML file and returns all of the functions defined in it.
55 xmlFile should be the path to Khronos's gl.xml file. The return value is a
56 sequence of FunctionDesc objects, ordered by slot number.
58 roots
= [ etree
.parse(xmlFile
).getroot() for xmlFile
in xmlFiles
]
59 return getFunctionsFromRoots(roots
)
61 def getFunctionsFromRoots(roots
):
64 for func
in _getFunctionList(root
):
65 functions
[func
.name
] = func
66 functions
= functions
.values()
68 # Sort the function list by name.
69 functions
= sorted(functions
, key
=lambda f
: f
.name
)
71 # Assign a slot number to each function. This isn't strictly necessary,
72 # since you can just look at the index in the list, but it makes it easier
73 # to include the slot when formatting output.
74 for i
in range(len(functions
)):
75 functions
[i
] = functions
[i
]._replace
(slot
=i
)
77 # Sort the function list by slot.... to simplify the diff
78 functions
= sorted(functions
, key
=lambda f
: f
.slot
)
82 def getExportNamesFromRoots(target
, roots
):
84 Goes through the <feature> tags from gl.xml and returns a set of OpenGL
85 functions that a library should export.
87 target should be one of "gl", "gldispatch", "opengl", "glesv1", or
90 featureNames
= _LIBRARY_FEATURE_NAMES
[target
]
91 if featureNames
is None:
92 return set(func
.name
for func
in getFunctionsFromRoots(roots
))
97 for featElem
in root
.findall("feature"):
98 if featElem
.get("name") in featureNames
:
99 features
.append(featElem
)
100 for featElem
in root
.findall("extensions/extension"):
101 if featElem
.get("name") in featureNames
:
102 features
.append(featElem
)
103 for featElem
in features
:
104 for commandElem
in featElem
.findall("require/command"):
105 names
.add(commandElem
.get("name"))
108 class FunctionArg(collections
.namedtuple("FunctionArg", "type name")):
112 Returns a "TYPE NAME" string, suitable for a function prototype.
115 if not rv
.endswith("*"):
120 class FunctionDesc(collections
.namedtuple("FunctionDesc", "name rt args slot")):
123 Returns true if the function returns a value.
125 return (self
.rt
!= "void")
130 Returns a string with the types and names of the arguments, as you
131 would use in a function declaration.
136 return ", ".join(arg
.dec
for arg
in self
.args
)
141 Returns a string with the names of the arguments, as you would use in a
144 return ", ".join(arg
.name
for arg
in self
.args
)
148 assert self
.name
.startswith("gl")
151 def _getFunctionList(root
):
152 for elem
in root
.findall("commands/command"):
153 yield _parseCommandElem(elem
)
155 def _parseCommandElem(elem
):
156 protoElem
= elem
.find("proto")
157 (rt
, name
) = _parseProtoElem(protoElem
)
160 for ch
in elem
.findall("param"):
161 # <param> tags have the same format as a <proto> tag.
162 args
.append(FunctionArg(*_parseProtoElem(ch
)))
163 func
= FunctionDesc(name
, rt
, tuple(args
), slot
=None)
167 def _parseProtoElem(elem
):
168 # If I just remove the tags and string the text together, I'll get valid C code.
169 text
= _flattenText(elem
)
171 m
= re
.match(r
"^(.+)\b(\w+)(?:\s*\[\s*(\d*)\s*\])?$", text
, re
.S
)
173 typename
= _fixupTypeName(m
.group(1))
176 # HACK: glPathGlyphIndexRangeNV defines an argument like this:
177 # GLuint baseAndCount[2]
178 # Convert it to a pointer and hope for the best.
180 return (typename
, name
)
182 raise ValueError("Can't parse element %r -> %r" % (elem
, text
))
184 def _flattenText(elem
):
186 Returns the text in an element and all child elements, with the tags
190 if elem
.text
is not None:
193 text
+= _flattenText(ch
)
194 if ch
.tail
is not None:
198 def _fixupTypeName(typeName
):
200 Converts a typename into a more consistent format.
203 rv
= typeName
.strip()
205 # Replace "GLvoid" with just plain "void".
206 rv
= re
.sub(r
"\bGLvoid\b", "void", rv
)
208 # Remove the vendor suffixes from types that have a suffix-less version.
209 rv
= re
.sub(r
"\b(GLhalf|GLintptr|GLsizeiptr|GLint64|GLuint64)(?:ARB|EXT|NV|ATI)\b", r
"\1", rv
)
211 rv
= re
.sub(r
"\bGLvoid\b", "void", rv
)
213 # Clear out any leading and trailing whitespace.
216 # Remove any whitespace before a '*'
217 rv
= re
.sub(r
"\s+\*", r
"*", rv
)
219 # Change "foo*" to "foo *"
220 rv
= re
.sub(r
"([^\*])\*", r
"\1 *", rv
)
222 # Condense all whitespace into a single space.
223 rv
= re
.sub(r
"\s+", " ", rv
)