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