17ec681f3Smrg#!/usr/bin/env python3
2a8bb7a65Smaya
3a8bb7a65Smaya# (C) Copyright 2015, NVIDIA CORPORATION.
4a8bb7a65Smaya# All Rights Reserved.
5a8bb7a65Smaya#
6a8bb7a65Smaya# Permission is hereby granted, free of charge, to any person obtaining a
7a8bb7a65Smaya# copy of this software and associated documentation files (the "Software"),
8a8bb7a65Smaya# to deal in the Software without restriction, including without limitation
9a8bb7a65Smaya# on the rights to use, copy, modify, merge, publish, distribute, sub
10a8bb7a65Smaya# license, and/or sell copies of the Software, and to permit persons to whom
11a8bb7a65Smaya# the Software is furnished to do so, subject to the following conditions:
12a8bb7a65Smaya#
13a8bb7a65Smaya# The above copyright notice and this permission notice (including the next
14a8bb7a65Smaya# paragraph) shall be included in all copies or substantial portions of the
15a8bb7a65Smaya# Software.
16a8bb7a65Smaya#
17a8bb7a65Smaya# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18a8bb7a65Smaya# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19a8bb7a65Smaya# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
20a8bb7a65Smaya# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21a8bb7a65Smaya# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22a8bb7a65Smaya# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23a8bb7a65Smaya# IN THE SOFTWARE.
24a8bb7a65Smaya#
25a8bb7a65Smaya# Authors:
26a8bb7a65Smaya#    Kyle Brenneman <kbrenneman@nvidia.com>
27a8bb7a65Smaya
28a8bb7a65Smayaimport collections
29a8bb7a65Smayaimport re
30a8bb7a65Smayaimport sys
317ec681f3Smrgimport xml.etree.ElementTree as etree
32a8bb7a65Smaya
33a8bb7a65Smayaimport os
34a8bb7a65SmayaGLAPI = os.path.join(os.path.dirname(__file__), "..", "glapi", "gen")
35a8bb7a65Smayasys.path.insert(0, GLAPI)
36a8bb7a65Smayaimport static_data
37a8bb7a65Smaya
38a8bb7a65SmayaMAPI_TABLE_NUM_DYNAMIC = 4096
39a8bb7a65Smaya
40a8bb7a65Smaya_LIBRARY_FEATURE_NAMES = {
41a8bb7a65Smaya    # libGL and libGLdiapatch both include every function.
42a8bb7a65Smaya    "gl" : None,
43a8bb7a65Smaya    "gldispatch" : None,
44a8bb7a65Smaya    "opengl" : frozenset(( "GL_VERSION_1_0", "GL_VERSION_1_1",
45a8bb7a65Smaya        "GL_VERSION_1_2", "GL_VERSION_1_3", "GL_VERSION_1_4", "GL_VERSION_1_5",
46a8bb7a65Smaya        "GL_VERSION_2_0", "GL_VERSION_2_1", "GL_VERSION_3_0", "GL_VERSION_3_1",
47a8bb7a65Smaya        "GL_VERSION_3_2", "GL_VERSION_3_3", "GL_VERSION_4_0", "GL_VERSION_4_1",
48a8bb7a65Smaya        "GL_VERSION_4_2", "GL_VERSION_4_3", "GL_VERSION_4_4", "GL_VERSION_4_5",
49a8bb7a65Smaya    )),
50a8bb7a65Smaya    "glesv1" : frozenset(("GL_VERSION_ES_CM_1_0", "GL_OES_point_size_array")),
51a8bb7a65Smaya    "glesv2" : frozenset(("GL_ES_VERSION_2_0", "GL_ES_VERSION_3_0",
52a8bb7a65Smaya            "GL_ES_VERSION_3_1", "GL_ES_VERSION_3_2",
53a8bb7a65Smaya    )),
54a8bb7a65Smaya}
55a8bb7a65Smaya
56a8bb7a65Smayadef getFunctions(xmlFiles):
57a8bb7a65Smaya    """
58a8bb7a65Smaya    Reads an XML file and returns all of the functions defined in it.
59a8bb7a65Smaya
60a8bb7a65Smaya    xmlFile should be the path to Khronos's gl.xml file. The return value is a
61a8bb7a65Smaya    sequence of FunctionDesc objects, ordered by slot number.
62a8bb7a65Smaya    """
63a8bb7a65Smaya    roots = [ etree.parse(xmlFile).getroot() for xmlFile in xmlFiles ]
64a8bb7a65Smaya    return getFunctionsFromRoots(roots)
65a8bb7a65Smaya
66a8bb7a65Smayadef getFunctionsFromRoots(roots):
67a8bb7a65Smaya    functions = {}
68a8bb7a65Smaya    for root in roots:
69a8bb7a65Smaya        for func in _getFunctionList(root):
70a8bb7a65Smaya            functions[func.name] = func
71a8bb7a65Smaya    functions = functions.values()
72a8bb7a65Smaya
73a8bb7a65Smaya    # Sort the function list by name.
74a8bb7a65Smaya    functions = sorted(functions, key=lambda f: f.name)
75a8bb7a65Smaya
76a8bb7a65Smaya    # Lookup for fixed offset/slot functions and use it if available.
77a8bb7a65Smaya    # Assign a slot number to each function. This isn't strictly necessary,
78a8bb7a65Smaya    # since you can just look at the index in the list, but it makes it easier
79a8bb7a65Smaya    # to include the slot when formatting output.
80a8bb7a65Smaya
81a8bb7a65Smaya    next_slot = 0
82a8bb7a65Smaya    for i in range(len(functions)):
83a8bb7a65Smaya        name = functions[i].name[2:]
84a8bb7a65Smaya
85a8bb7a65Smaya        if name in static_data.offsets:
86a8bb7a65Smaya            functions[i] = functions[i]._replace(slot=static_data.offsets[name])
87a8bb7a65Smaya        elif not name.endswith("ARB") and name + "ARB" in static_data.offsets:
88a8bb7a65Smaya            functions[i] = functions[i]._replace(slot=static_data.offsets[name + "ARB"])
89a8bb7a65Smaya        elif not name.endswith("EXT") and name + "EXT" in static_data.offsets:
90a8bb7a65Smaya            functions[i] = functions[i]._replace(slot=static_data.offsets[name + "EXT"])
91a8bb7a65Smaya        else:
92a8bb7a65Smaya            functions[i] = functions[i]._replace(slot=next_slot)
93a8bb7a65Smaya            next_slot += 1
94a8bb7a65Smaya
95a8bb7a65Smaya    return functions
96a8bb7a65Smaya
97a8bb7a65Smayadef getExportNamesFromRoots(target, roots):
98a8bb7a65Smaya    """
99a8bb7a65Smaya    Goes through the <feature> tags from gl.xml and returns a set of OpenGL
100a8bb7a65Smaya    functions that a library should export.
101a8bb7a65Smaya
102a8bb7a65Smaya    target should be one of "gl", "gldispatch", "opengl", "glesv1", or
103a8bb7a65Smaya    "glesv2".
104a8bb7a65Smaya    """
105a8bb7a65Smaya    featureNames = _LIBRARY_FEATURE_NAMES[target]
106a8bb7a65Smaya    if featureNames is None:
107a8bb7a65Smaya        return set(func.name for func in getFunctionsFromRoots(roots))
108a8bb7a65Smaya
109a8bb7a65Smaya    names = set()
110a8bb7a65Smaya    for root in roots:
111a8bb7a65Smaya        features = []
112a8bb7a65Smaya        for featElem in root.findall("feature"):
113a8bb7a65Smaya            if featElem.get("name") in featureNames:
114a8bb7a65Smaya                features.append(featElem)
115a8bb7a65Smaya        for featElem in root.findall("extensions/extension"):
116a8bb7a65Smaya            if featElem.get("name") in featureNames:
117a8bb7a65Smaya                features.append(featElem)
118a8bb7a65Smaya        for featElem in features:
119a8bb7a65Smaya            for commandElem in featElem.findall("require/command"):
120a8bb7a65Smaya                names.add(commandElem.get("name"))
121a8bb7a65Smaya    return names
122a8bb7a65Smaya
123a8bb7a65Smayaclass FunctionArg(collections.namedtuple("FunctionArg", "type name")):
124a8bb7a65Smaya    @property
125a8bb7a65Smaya    def dec(self):
126a8bb7a65Smaya        """
127a8bb7a65Smaya        Returns a "TYPE NAME" string, suitable for a function prototype.
128a8bb7a65Smaya        """
129a8bb7a65Smaya        rv = str(self.type)
130a8bb7a65Smaya        if not rv.endswith("*"):
131a8bb7a65Smaya            rv += " "
132a8bb7a65Smaya        rv += self.name
133a8bb7a65Smaya        return rv
134a8bb7a65Smaya
135a8bb7a65Smayaclass FunctionDesc(collections.namedtuple("FunctionDesc", "name rt args slot")):
136a8bb7a65Smaya    def hasReturn(self):
137a8bb7a65Smaya        """
138a8bb7a65Smaya        Returns true if the function returns a value.
139a8bb7a65Smaya        """
140a8bb7a65Smaya        return (self.rt != "void")
141a8bb7a65Smaya
142a8bb7a65Smaya    @property
143a8bb7a65Smaya    def decArgs(self):
144a8bb7a65Smaya        """
145a8bb7a65Smaya        Returns a string with the types and names of the arguments, as you
146a8bb7a65Smaya        would use in a function declaration.
147a8bb7a65Smaya        """
148a8bb7a65Smaya        if not self.args:
149a8bb7a65Smaya            return "void"
150a8bb7a65Smaya        else:
151a8bb7a65Smaya            return ", ".join(arg.dec for arg in self.args)
152a8bb7a65Smaya
153a8bb7a65Smaya    @property
154a8bb7a65Smaya    def callArgs(self):
155a8bb7a65Smaya        """
156a8bb7a65Smaya        Returns a string with the names of the arguments, as you would use in a
157a8bb7a65Smaya        function call.
158a8bb7a65Smaya        """
159a8bb7a65Smaya        return ", ".join(arg.name for arg in self.args)
160a8bb7a65Smaya
161a8bb7a65Smaya    @property
162a8bb7a65Smaya    def basename(self):
163a8bb7a65Smaya        assert self.name.startswith("gl")
164a8bb7a65Smaya        return self.name[2:]
165a8bb7a65Smaya
166a8bb7a65Smayadef _getFunctionList(root):
167a8bb7a65Smaya    for elem in root.findall("commands/command"):
168a8bb7a65Smaya        yield _parseCommandElem(elem)
169a8bb7a65Smaya
170a8bb7a65Smayadef _parseCommandElem(elem):
171a8bb7a65Smaya    protoElem = elem.find("proto")
172a8bb7a65Smaya    (rt, name) = _parseProtoElem(protoElem)
173a8bb7a65Smaya
174a8bb7a65Smaya    args = []
175a8bb7a65Smaya    for ch in elem.findall("param"):
176a8bb7a65Smaya        # <param> tags have the same format as a <proto> tag.
177a8bb7a65Smaya        args.append(FunctionArg(*_parseProtoElem(ch)))
178a8bb7a65Smaya    func = FunctionDesc(name, rt, tuple(args), slot=None)
179a8bb7a65Smaya
180a8bb7a65Smaya    return func
181a8bb7a65Smaya
182a8bb7a65Smayadef _parseProtoElem(elem):
183a8bb7a65Smaya    # If I just remove the tags and string the text together, I'll get valid C code.
184a8bb7a65Smaya    text = _flattenText(elem)
185a8bb7a65Smaya    text = text.strip()
186a8bb7a65Smaya    m = re.match(r"^(.+)\b(\w+)(?:\s*\[\s*(\d*)\s*\])?$", text, re.S)
187a8bb7a65Smaya    if m:
188a8bb7a65Smaya        typename = _fixupTypeName(m.group(1))
189a8bb7a65Smaya        name = m.group(2)
190a8bb7a65Smaya        if m.group(3):
191a8bb7a65Smaya            # HACK: glPathGlyphIndexRangeNV defines an argument like this:
192a8bb7a65Smaya            # GLuint baseAndCount[2]
193a8bb7a65Smaya            # Convert it to a pointer and hope for the best.
194a8bb7a65Smaya            typename += "*"
195a8bb7a65Smaya        return (typename, name)
196a8bb7a65Smaya    else:
197a8bb7a65Smaya        raise ValueError("Can't parse element %r -> %r" % (elem, text))
198a8bb7a65Smaya
199a8bb7a65Smayadef _flattenText(elem):
200a8bb7a65Smaya    """
201a8bb7a65Smaya    Returns the text in an element and all child elements, with the tags
202a8bb7a65Smaya    removed.
203a8bb7a65Smaya    """
204a8bb7a65Smaya    text = ""
205a8bb7a65Smaya    if elem.text is not None:
206a8bb7a65Smaya        text = elem.text
207a8bb7a65Smaya    for ch in elem:
208a8bb7a65Smaya        text += _flattenText(ch)
209a8bb7a65Smaya        if ch.tail is not None:
210a8bb7a65Smaya            text += ch.tail
211a8bb7a65Smaya    return text
212a8bb7a65Smaya
213a8bb7a65Smayadef _fixupTypeName(typeName):
214a8bb7a65Smaya    """
215a8bb7a65Smaya    Converts a typename into a more consistent format.
216a8bb7a65Smaya    """
217a8bb7a65Smaya
218a8bb7a65Smaya    rv = typeName.strip()
219a8bb7a65Smaya
220a8bb7a65Smaya    # Replace "GLvoid" with just plain "void".
221a8bb7a65Smaya    rv = re.sub(r"\bGLvoid\b", "void", rv)
222a8bb7a65Smaya
223a8bb7a65Smaya    # Remove the vendor suffixes from types that have a suffix-less version.
224a8bb7a65Smaya    rv = re.sub(r"\b(GLhalf|GLintptr|GLsizeiptr|GLint64|GLuint64)(?:ARB|EXT|NV|ATI)\b", r"\1", rv)
225a8bb7a65Smaya
226a8bb7a65Smaya    rv = re.sub(r"\bGLDEBUGPROCKHR\b", "GLDEBUGPROC", rv)
227a8bb7a65Smaya
228a8bb7a65Smaya    # Clear out any leading and trailing whitespace.
229a8bb7a65Smaya    rv = rv.strip()
230a8bb7a65Smaya
231a8bb7a65Smaya    # Remove any whitespace before a '*'
232a8bb7a65Smaya    rv = re.sub(r"\s+\*", r"*", rv)
233a8bb7a65Smaya
234a8bb7a65Smaya    # Change "foo*" to "foo *"
235a8bb7a65Smaya    rv = re.sub(r"([^\*])\*", r"\1 *", rv)
236a8bb7a65Smaya
237a8bb7a65Smaya    # Condense all whitespace into a single space.
238a8bb7a65Smaya    rv = re.sub(r"\s+", " ", rv)
239a8bb7a65Smaya
240a8bb7a65Smaya    return rv
241a8bb7a65Smaya
242