]> git.tdb.fi Git - libs/gl.git/blob - scripts/extgen.py
c8f87c3bf79278bb98e5e1342912f66570752cab
[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 target_ext = sys.argv[1]
27 out_base = None
28 if target_ext.endswith(".glext"):
29         fn = target_ext
30         target_ext = None
31         core_version = None
32         secondary = []
33         for line in open(fn):
34                 parts = line.split()
35                 if parts[0]=="extension":
36                         target_ext = parts[1]
37                 elif parts[0]=="core_version":
38                         core_version = 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         core_version = None
46         if secondary and secondary[0][0].isdigit():
47                 core_version = secondary.pop(0)
48
49 ext_type = target_ext.split('_')[0]
50 backport_ext = None
51
52 if core_version:
53         core_version = map(int, core_version.split('.'))
54
55 if not out_base:
56         out_base = target_ext.lower()
57
58 ### XML file parsing ###
59
60 class Thing:
61         FUNCTION = 1
62         ENUM = 2
63
64         def __init__(self, name, kind):
65                 self.name = name
66                 self.kind = kind
67                 self.version = None
68                 self.extension = None
69                 self.aliases = []
70                 self.source = None
71
72 class Function(Thing):
73         def __init__(self, name):
74                 Thing.__init__(self, name, Thing.FUNCTION)
75                 self.return_type = "void"
76                 self.params = []
77                 self.typedef = None
78
79 class Enum(Thing):
80         def __init__(self, name):
81                 Thing.__init__(self, name, Thing.ENUM)
82                 self.value = 0
83
84 things = {}
85
86 def get_nested_elements(elem, path):
87         childElements = [c for c in elem.childNodes if c.nodeType==xml.dom.Node.ELEMENT_NODE]
88         if '/' in path:
89                 head, tail = path.split('/', 1)
90                 result = []
91                 for c in childElements:
92                         if c.tagName==head:
93                                 result += get_nested_elements(c, tail)
94                 return result
95         else:
96                 return [c for c in childElements if c.tagName==path]
97
98 def get_first_child(elem, tag):
99         for c in elem.childNodes:
100                 if c.nodeType==xml.dom.Node.ELEMENT_NODE and c.tagName==tag:
101                         return c
102         return None
103
104 def get_text_contents(node):
105         result = ""
106         for c in node.childNodes:
107                 if c.nodeType==xml.dom.Node.TEXT_NODE or c.nodeType==xml.dom.Node.CDATA_SECTION_NODE:
108                         result += c.data
109                 else:
110                         result += get_text_contents(c)
111         return result
112
113 def parse_command(cmd):
114         proto = get_first_child(cmd, "proto")
115         name = get_text_contents(get_first_child(proto, "name"))
116         func = things.get(name)
117         if not func:
118                 func = Function(name)
119                 things[name] = func
120
121         aliases = get_nested_elements(cmd, "alias")
122         func.aliases = [a.getAttribute("name") for a in aliases]
123
124         ptype = get_first_child(proto, "ptype")
125         if ptype:
126                 func.return_type = get_text_contents(ptype)
127         else:
128                 for c in proto.childNodes:
129                         if c.nodeType==xml.dom.Node.TEXT_NODE and c.data.strip():
130                                 func.return_type = c.data.strip()
131                                 break
132
133         params = get_nested_elements(cmd, "param")
134         func.params = map(get_text_contents, params)
135
136 def parse_enum(en):
137         name = en.getAttribute("name")
138         enum = things.get(name)
139         if not enum:
140                 enum = Enum(name)
141                 things[name] = enum
142
143         enum.value = int(en.getAttribute("value"), 16)
144
145 def parse_feature(feat):
146         api = feat.getAttribute("api")
147         if api=="gl":
148                 version = feat.getAttribute("number")
149                 if version:
150                         version = map(int, version.split('.'))
151                 else:
152                         version = None
153
154                 commands = get_nested_elements(feat, "require/command")
155                 enums = get_nested_elements(feat, "require/enum")
156                 for t in itertools.chain(commands, enums):
157                         name = t.getAttribute("name")
158                         thing = things.get(name)
159                         if thing:
160                                 thing.version = version
161
162                 if feat.getAttribute("name")=="MSPGL_REMOVE":
163                         commands = get_nested_elements(feat, "remove/command")
164                         enums = get_nested_elements(feat, "remove/enum")
165                         for t in itertools.chain(commands, enums):
166                                 name = t.getAttribute("name")
167                                 if name in things:
168                                         del things[name]
169
170 def parse_extension(ext):
171         ext_name = ext.getAttribute("name")
172         if ext_name.startswith("GL_"):
173                 ext_name = ext_name[3:]
174
175         supported = ext.getAttribute("supported").split('|')
176         if "gl" in supported:
177                 commands = get_nested_elements(ext, "require/command")
178                 enums = get_nested_elements(ext, "require/enum")
179                 for t in itertools.chain(commands, enums):
180                         name = t.getAttribute("name")
181                         thing = things.get(name)
182                         if thing:
183                                 thing.extension = ext_name
184
185 def parse_file(fn):
186         doc = xml.dom.minidom.parse(fn)
187         root = doc.documentElement
188
189         commands = get_nested_elements(root, "commands/command")
190         for cmd in commands:
191                 parse_command(cmd)
192
193         enums = get_nested_elements(root, "enums/enum")
194         for en in enums:
195                 parse_enum(en)
196
197         features = get_nested_elements(root, "feature")
198         for feat in features:
199                 parse_feature(feat)
200
201         extensions = get_nested_elements(root, "extensions/extension")
202         for ext in extensions:
203                 parse_extension(ext)
204
205 parse_file("gl.xml")
206 parse_file("gl.fixes.xml")
207
208 ### Additional processing ###
209
210 # Find aliases for enums
211 enums = [t for t in things.itervalues() if t.kind==Thing.ENUM]
212 core_enums_by_value = dict((e.value, e) for e in enums if e.version)
213
214 for e in enums:
215         if e.kind==t.ENUM and e.extension:
216                 ce = core_enums_by_value.get(e.value)
217                 if ce and ce!=e:
218                         e.aliases.append(ce.name)
219
220 # Create references from core things to their extension counterparts
221 for t in things.itervalues():
222         if t.extension==target_ext or t.extension in secondary:
223                 for a in t.aliases:
224                         alias = things.get(a)
225                         if alias:
226                                 alias.source = t
227
228 def is_relevant(t):
229         # Unpromoted extension things are relevant
230         if t.extension==target_ext and not t.aliases:
231                 return True
232
233         # Core things promoted from the extension are also relevant
234         if t.source:
235                 e = t.source
236                 if e.extension==target_ext or e.extension in secondary:
237                         return True
238
239         return False
240
241 funcs = [t for t in things.itervalues() if t.kind==Thing.FUNCTION and is_relevant(t)]
242 funcs.sort(key=(lambda f: f.name))
243 enums = filter(is_relevant, enums)
244 enums.sort(key=(lambda e: e.value))
245
246 for t in itertools.chain(funcs, enums):
247         if not core_version:
248                 core_version = t.version
249
250         # Things in backport extensions don't acquire an extension suffix
251         if t.extension and not t.name.endswith(ext_type):
252                 backport_ext = t.extension
253
254 for f in funcs:
255         if f.source:
256                 # Typedefs for early core functions are not available in all
257                 # implementations
258                 f.typedef = "PFN%sPROC"%f.source.name.upper()
259         else:
260                 f.typedef = "PFN%sPROC"%f.name.upper()
261
262 ### Output ###
263
264 out = file(out_base+".h", "w")
265 out.write("#ifndef MSP_GL_%s_\n"%target_ext.upper())
266 out.write("#define MSP_GL_%s_\n"%target_ext.upper())
267
268 out.write("""
269 #include <msp/gl/extension.h>
270 #include <msp/gl/gl.h>
271
272 namespace Msp {
273 namespace GL {
274
275 """)
276
277 if funcs or enums:
278         if funcs:
279                 out.write("#if defined(__APPLE__) || !defined(GL_%s)\n"%target_ext)
280                 for f in funcs:
281                         out.write("typedef %s (*%s)(%s);\n"%(f.return_type, f.typedef, ", ".join(f.params)))
282                 out.write("#endif\n\n")
283
284         if enums:
285                 if core_version:
286                         out.write("#ifndef GL_VERSION_%s\n"%"_".join(map(str, core_version)))
287                 else:
288                         out.write("#ifndef GL_%s\n"%target_ext)
289                 for e in enums:
290                         out.write("#define %s 0x%04X\n"%(e.name, e.value))
291                 out.write("#endif\n\n")
292
293         for f in funcs:
294                 out.write("extern %s %s;\n"%(f.typedef, f.name))
295
296 out.write("extern Extension %s;\n"%target_ext)
297
298 out.write("""
299 } // namespace GL
300 } // namespace Msp
301
302 #endif
303 """)
304
305 out = file(out_base+".cpp", "w")
306 out.write("#include \"%s.h\"\n"%target_ext.lower())
307
308 if funcs:
309         out.write("""
310 #ifdef __APPLE__
311 #define GET_PROC_ADDRESS(x) ::x
312 #else
313 #define GET_PROC_ADDRESS(x) get_proc_address(#x)
314 #endif
315 """)
316 out.write("""
317 namespace Msp {
318 namespace GL {
319
320 """)
321
322 for f in funcs:
323         out.write("%s %s = 0;\n"%(f.typedef, f.name))
324
325 out.write("\nExtension::SupportLevel init_%s()\n{\n"%target_ext.lower())
326 out.write("#ifdef GL_%s\n"%target_ext)
327 if core_version:
328         out.write("\tif(is_version_at_least(%d, %d)"%tuple(core_version))
329         if backport_ext:
330                 out.write(" || is_supported(\"GL_%s\")"%backport_ext)
331         out.write(")\n\t{\n")
332         if funcs:
333                 for f in funcs:
334                         out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, f.typedef, f.name))
335         out.write("\t\treturn Extension::CORE;\n")
336         out.write("\t}\n")
337 if target_ext!=backport_ext:
338         out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(target_ext))
339         if funcs:
340                 for f in funcs:
341                         n = f.name
342                         if f.source:
343                                 n = f.source.name
344                         out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, f.typedef, n))
345         out.write("\t\treturn Extension::EXTENSION;\n")
346         out.write("\t}\n")
347 out.write("#endif\n")
348 out.write("\treturn Extension::UNSUPPORTED;\n")
349 out.write("}\n")
350
351 out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(target_ext, target_ext, target_ext.lower()))
352
353 out.write("""
354 } // namespace GL
355 } // namespace Msp
356 """)