]> git.tdb.fi Git - libs/gl.git/blob - scripts/extgen.py
Final touches of OS X support
[libs/gl.git] / scripts / extgen.py
1 #!/usr/bin/python
2
3 import sys
4 import os
5 import xml.dom
6 import xml.dom.minidom
7 import itertools
8
9 ### Command line processing ###
10
11 if len(sys.argv)<2:
12         print """Usage:
13   extgen.py <extension> [<core_version>] [<secondary> ...]
14   extgen.py <extfile> [<outfile>]
15
16 Reads gl.xml and generates files to use <extension>.  Any promoted functions
17 are exposed with their promoted names.  If <secondary> extensions are given,
18 any promoted functions from those are pulled in as well.  <core_version> can
19 be given to override the version where <extension> was promoted to core.
20
21 In the second form, the parameters are read from <extfile>.  If <outfile> is
22 absent, the extension's lowercased name is used.  Anything after the last dot
23 in <outfile> is removed and replaced with cpp and h."""
24         sys.exit(0)
25
26 ext = sys.argv[1]
27 out_base = None
28 if ext.endswith(".glext"):
29         fn = ext
30         ext = None
31         ver = None
32         secondary = []
33         for line in open(fn):
34                 parts = line.split()
35                 if parts[0]=="extension":
36                         ext = parts[1]
37                 elif parts[0]=="core_version":
38                         ver = parts[1]
39                 elif parts[0]=="secondary":
40                         secondary.append(parts[1])
41         if len(sys.argv)>=3:
42                 out_base = os.path.splitext(sys.argv[2])[0]
43 else:
44         secondary = sys.argv[2:]
45         ver = None
46         if secondary and secondary[0][0].isdigit():
47                 ver = secondary.pop(0)
48
49 exttype = ext.split('_')[0]
50 backport_ext = None
51
52 if not out_base:
53         out_base = ext.lower()
54
55 ### XML file parsing ###
56
57 class Thing:
58         FUNCTION = 1
59         ENUM = 2
60
61         def __init__(self, name, kind):
62                 self.name = name
63                 self.kind = kind
64                 self.version = None
65                 self.extension = None
66                 self.aliases = []
67                 self.source = None
68
69 class Function(Thing):
70         def __init__(self, name):
71                 Thing.__init__(self, name, Thing.FUNCTION)
72                 self.typedef = None
73                 self.vectorequiv = None
74
75 class Enum(Thing):
76         def __init__(self, name):
77                 Thing.__init__(self, name, Thing.ENUM)
78                 self.value = 0
79
80 things = {}
81
82 def get_nested_elements(elem, path):
83         if '/' in path:
84                 head, tail = path.split('/', 1)
85                 result = []
86                 for e in elem.getElementsByTagName(head):
87                         result += get_nested_elements(e, tail)
88                 return result
89         else:
90                 return elem.getElementsByTagName(path)
91
92 def get_text_contents(node):
93         result = ""
94         for c in node.childNodes:
95                 if c.nodeType==xml.dom.Node.TEXT_NODE or c.nodeType==xml.dom.Node.CDATA_SECTION_NODE:
96                         result += c.data
97                 else:
98                         result += get_text_contents(c)
99         return result
100
101 def parse_file(fn):
102         doc = xml.dom.minidom.parse(fn)
103         root = doc.documentElement
104         commands = get_nested_elements(root, "commands/command")
105         for cmd in commands:
106                 name = get_text_contents(get_nested_elements(cmd, "proto/name")[0])
107                 func = things.get(name)
108                 if not func:
109                         func = Function(name)
110                         things[name] = func
111
112                 aliases = cmd.getElementsByTagName("alias")
113                 for a in aliases:
114                         func.aliases.append(a.getAttribute("name"))
115
116                 vec = cmd.getElementsByTagName("vecequiv")
117                 if vec:
118                         func.vectorequiv = vec[0].getAttribute("name")
119
120         enums = get_nested_elements(root, "enums/enum")
121         for en in enums:
122                 name = en.getAttribute("name")
123                 enum = things.get(name)
124                 if not enum:
125                         enum = Enum(name)
126                         things[name] = enum
127
128                 enum.value = int(en.getAttribute("value"), 16)
129
130         features = root.getElementsByTagName("feature")
131         for feat in features:
132                 api = feat.getAttribute("api")
133                 if api=="gl":
134                         version = feat.getAttribute("number")
135
136                         commands = get_nested_elements(feat, "require/command")
137                         enums = get_nested_elements(feat, "require/enum")
138                         for t in itertools.chain(commands, enums):
139                                 name = t.getAttribute("name")
140                                 thing = things.get(name)
141                                 if thing:
142                                         thing.version = version
143
144                         if feat.getAttribute("name")=="MSPGL_REMOVE":
145                                 commands = get_nested_elements(feat, "remove/command")
146                                 enums = get_nested_elements(feat, "require/enum")
147                                 for t in itertools.chain(commands, enums):
148                                         name = t.getAttribute("name")
149                                         if name in things:
150                                                 del things[name]
151
152         extensions = get_nested_elements(root, "extensions/extension")
153         for ext in extensions:
154                 ext_name = ext.getAttribute("name")
155                 if ext_name.startswith("GL_"):
156                         ext_name = ext_name[3:]
157                 supported = ext.getAttribute("supported").split('|')
158                 if "gl" in supported:
159                         commands = get_nested_elements(ext, "require/command")
160                         enums = get_nested_elements(ext, "require/enum")
161                         for t in itertools.chain(commands, enums):
162                                 name = t.getAttribute("name")
163                                 thing = things.get(name)
164                                 if thing:
165                                         thing.extension = ext_name
166
167 parse_file("gl.xml")
168 parse_file("gl.fixes.xml")
169
170 ### Additional processing ###
171
172 # Find aliases for enums
173 enums = [t for t in things.itervalues() if t.kind==Thing.ENUM]
174 core_enums_by_value = dict((e.value, e) for e in enums if e.version)
175
176 for e in enums:
177         if e.kind==t.ENUM and e.extension:
178                 ce = core_enums_by_value.get(e.value)
179                 if ce and ce!=e:
180                         e.aliases.append(ce.name)
181
182 # Create references from core things to their extension counterparts
183 for t in things.itervalues():
184         if t.extension==ext or t.extension in secondary:
185                 for a in t.aliases:
186                         alias = things.get(a)
187                         if alias:
188                                 alias.source = t
189
190 def is_relevant(t):
191         # Unpromoted extension things are relevant
192         if t.extension==ext and not t.aliases:
193                 return True
194
195         # Core things promoted from the extension are also relevant
196         if t.source:
197                 e = t.source
198                 if e.extension==ext or e.extension in secondary:
199                         return True
200
201         return False
202
203 funcs = [t for t in things.itervalues() if t.kind==Thing.FUNCTION and is_relevant(t)]
204 funcs.sort(key=(lambda f: f.name))
205 enums = [e for e in enums if is_relevant(e)]
206 enums.sort(key=(lambda e: e.value))
207
208 for t in itertools.chain(funcs, enums):
209         if not ver:
210                 ver = t.version
211
212         # Things in backport extensions don't acquire an extension suffix
213         if t.extension and not t.name.endswith(exttype):
214                 backport_ext = t.extension
215
216 for f in funcs:
217         if f.source:
218                 # Typedefs for early core functions are not available in all
219                 # implementations
220                 f.typedef = "PFN%sPROC"%f.source.name.upper()
221         else:
222                 f.typedef = "PFN%sPROC"%f.name.upper()
223
224 if ver:
225         ver = map(int, ver.split('.'))
226
227 ### Output ###
228
229 out = file(out_base+".h", "w")
230 out.write("#ifndef MSP_GL_%s_\n"%ext.upper())
231 out.write("#define MSP_GL_%s_\n"%ext.upper())
232
233 out.write("""
234 #include <msp/gl/extension.h>
235 #include <msp/gl/gl.h>
236
237 namespace Msp {
238 namespace GL {
239 """)
240
241 if funcs or enums:
242         out.write("\n#ifndef GL_%s\n"%ext)
243         for f in funcs:
244                 out.write("typedef int (*%s)(...);\n"%f.typedef)
245         if funcs and enums:
246                 out.write("\n")
247         for e in enums:
248                 out.write("#define %s 0x%04X\n"%(e.name, e.value))
249         out.write("#endif\n\n")
250
251         # Apple's OpenGL implementation doesn't have a GetProcAddress function; link
252         # directly to the OpenGL library
253         out.write("\n#if !defined(__APPLE__) || !defined(GL_%s)\n"%ext)
254         for f in funcs:
255                 out.write("extern %s %s;\n"%(f.typedef, f.name))
256         out.write("#endif\n")
257
258 out.write("\nextern Extension %s;\n"%ext)
259
260 out.write("""
261 } // namespace GL
262 } // namespace Msp
263
264 #endif
265 """)
266
267 out = file(out_base+".cpp", "w")
268 out.write("#include \"%s.h\"\n"%ext.lower())
269
270 out.write("""
271 namespace Msp {
272 namespace GL {
273 """)
274
275 if funcs:
276         out.write("\n#if !defined(__APPLE__) || !defined(GL_%s)\n"%ext)
277         for f in funcs:
278                 out.write("%s %s = 0;\n"%(f.typedef, f.name))
279         out.write("\n#endif\n")
280
281 out.write("\nExtension::SupportLevel init_%s()\n{\n"%ext.lower())
282 out.write("#ifdef GL_%s\n"%ext)
283 if ver:
284         out.write("\tif(is_version_at_least(%d, %d)"%tuple(ver))
285         if backport_ext:
286                 out.write(" || is_supported(\"GL_%s\")"%backport_ext)
287         out.write(")\n\t{\n")
288         if funcs:
289                 out.write("#ifndef __APPLE__\n")
290                 for f in funcs:
291                         out.write("\t\t%s = reinterpret_cast<%s>(get_proc_address(\"%s\"));\n"%(f.name, f.typedef, f.name))
292                 out.write("#endif\n")
293         out.write("\t\treturn Extension::CORE;\n")
294         out.write("\t}\n")
295 if ext!=backport_ext:
296         out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(ext))
297         if funcs:
298                 out.write("#ifndef __APPLE__\n")
299                 for f in funcs:
300                         n = f.name
301                         if f.source:
302                                 n = f.source.name
303                         out.write("\t\t%s = reinterpret_cast<%s>(get_proc_address(\"%s\"));\n"%(f.name, f.typedef, n))
304                 out.write("#endif\n")
305         out.write("\t\treturn Extension::EXTENSION;\n")
306         out.write("\t}\n")
307 out.write("#endif\n")
308 out.write("\treturn Extension::UNSUPPORTED;\n")
309 out.write("}\n")
310
311 out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(ext, ext, ext.lower()))
312
313 out.write("""
314 } // namespace GL
315 } // namespace Msp
316 """)