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