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