9 ### Command line processing ###
13 extgen.py [api] <extension> [<core_version>] [<secondary> ...]
14 extgen.py [api] <extfile> [<outfile>]
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.
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."""
29 if sys.argv[i].startswith("gl"):
30 target_api = sys.argv[i]
33 target_ext = sys.argv[i]
35 if target_ext.endswith(".glext"):
42 if parts[0]=="extension":
44 elif parts[0]=="core_version":
45 if parts[1]==target_api:
46 core_version = parts[2]
47 elif parts[0]=="secondary":
48 secondary.append(parts[1])
50 out_base = os.path.splitext(sys.argv[i+1])[0]
52 secondary = sys.argv[i+1:]
54 if secondary and secondary[0][0].isdigit():
55 core_version = secondary.pop(0)
57 ext_type = target_ext.split('_')[0]
61 core_version = map(int, core_version.split('.'))
64 out_base = target_ext.lower()
66 ### XML file parsing ###
72 def __init__(self, name, kind):
77 self.supported_apis = {}
81 class Function(Thing):
82 def __init__(self, name):
83 Thing.__init__(self, name, Thing.FUNCTION)
84 self.return_type = "void"
89 def __init__(self, name):
90 Thing.__init__(self, name, Thing.ENUM)
92 self.bitmask = (name.endswith("_BIT") or "_BIT_" in name)
95 def __init__(self, name):
97 self.supported_apis = []
98 self.ext_type = name[0:name.find('_')]
103 def get_nested_elements(elem, path):
104 childElements = [c for c in elem.childNodes if c.nodeType==xml.dom.Node.ELEMENT_NODE]
106 head, tail = path.split('/', 1)
108 for c in childElements:
110 result += get_nested_elements(c, tail)
113 return [c for c in childElements if c.tagName==path]
115 def get_first_child(elem, tag):
116 for c in elem.childNodes:
117 if c.nodeType==xml.dom.Node.ELEMENT_NODE and c.tagName==tag:
121 def get_text_contents(node):
123 for c in node.childNodes:
124 if c.nodeType==xml.dom.Node.TEXT_NODE or c.nodeType==xml.dom.Node.CDATA_SECTION_NODE:
127 result += get_text_contents(c)
130 def parse_command(cmd):
131 proto = get_first_child(cmd, "proto")
132 name = get_text_contents(get_first_child(proto, "name"))
133 func = things.get(name)
135 func = Function(name)
138 aliases = get_nested_elements(cmd, "alias")
139 func.aliases = [a.getAttribute("name") for a in aliases]
141 ptype = get_first_child(proto, "ptype")
143 func.return_type = get_text_contents(ptype)
145 for c in proto.childNodes:
146 if c.nodeType==xml.dom.Node.TEXT_NODE and c.data.strip():
147 func.return_type = c.data.strip()
150 params = get_nested_elements(cmd, "param")
151 func.params = map(get_text_contents, params)
154 name = en.getAttribute("name")
155 enum = things.get(name)
160 enum.value = int(en.getAttribute("value"), 16)
162 def parse_feature(feat):
163 api = feat.getAttribute("api")
164 version = feat.getAttribute("number")
166 version = map(int, version.split('.'))
170 requires = get_nested_elements(feat, "require")
172 commands = get_nested_elements(req, "command")
173 enums = get_nested_elements(req, "enum")
174 for t in itertools.chain(commands, enums):
175 name = t.getAttribute("name")
176 thing = things.get(name)
178 thing.supported_apis.setdefault(api, version)
180 if not api or api==target_api:
181 removes = get_nested_elements(feat, "remove")
183 profile = rem.getAttribute("profile")
184 commands = get_nested_elements(rem, "command")
185 enums = get_nested_elements(rem, "enum")
187 for t in itertools.chain(commands, enums):
188 name = t.getAttribute("name")
189 if profile!="core" and name in things:
192 def parse_extension(ext):
193 ext_name = ext.getAttribute("name")
194 if ext_name.startswith("GL_"):
195 ext_name = ext_name[3:]
197 supported = ext.getAttribute("supported").split('|')
198 if target_api not in supported and ext_name!=target_ext:
201 extension = extensions.get(ext_name)
203 extension = Extension(ext_name)
204 extensions[ext_name] = extension
206 extension.supported_apis = supported
208 requires = get_nested_elements(ext, "require")
210 api = req.getAttribute("api")
214 supported = extension.supported_apis
216 commands = get_nested_elements(req, "command")
217 enums = get_nested_elements(req, "enum")
218 for t in itertools.chain(commands, enums):
219 name = t.getAttribute("name")
220 thing = things.get(name)
222 if thing.extension and extension.name!=target_ext:
223 if thing.extension.ext_type=="ARB" or thing.extension.name==target_ext:
225 if thing.extension.ext_type=="EXT" and extension.ext_type!="ARB":
228 thing.extension = extension
230 thing.supported_apis.setdefault(a, "ext")
233 doc = xml.dom.minidom.parse(fn)
234 root = doc.documentElement
236 commands = get_nested_elements(root, "commands/command")
240 enums = get_nested_elements(root, "enums/enum")
244 features = get_nested_elements(root, "feature")
245 for feat in features:
248 extensions = get_nested_elements(root, "extensions/extension")
249 for ext in extensions:
253 parse_file("gl.fixes.xml")
255 ### Additional processing ###
257 if target_ext in extensions:
258 target_ext = extensions[target_ext]
260 print "Extension %s not found"%target_ext
263 # Find aliases for enums
264 enums = [t for t in things.itervalues() if t.kind==Thing.ENUM]
265 core_enums = [e for e in enums if any(v!="ext" for v in e.supported_apis.itervalues())]
266 core_enums_by_value = dict((e.value, None) for e in core_enums)
268 def get_key_api(things):
269 common_apis = set(target_ext.supported_apis)
271 common_apis.intersection_update(t.supported_apis.keys())
273 return common_apis.pop()
278 if all(v=="ext" for v in e.supported_apis.values()) and e.value in core_enums_by_value:
279 if core_enums_by_value[e.value] is None:
280 candidates = [ce for ce in core_enums if ce.value==e.value]
281 key_api = get_key_api(candidates)
282 core_enums_by_value[e.value] = list(sorted(candidates, key=(lambda x: x.supported_apis.get(key_api, "ext"))))
283 for ce in core_enums_by_value[e.value]:
284 if ce.bitmask==e.bitmask:
285 e.aliases.append(ce.name)
288 # Create references from core things to their extension counterparts
289 for t in things.itervalues():
292 alias = things.get(a)
294 if target_api in t.supported_apis:
295 alias.sources.insert(0, t)
297 alias.sources.append(t)
299 # Find the things we want to include in this extension
301 # Unpromoted extension things are relevant
302 if t.extension and t.extension==target_ext and not t.aliases:
305 # Core things promoted from the extension are also relevant
307 if s.extension==target_ext or s.extension.name in secondary:
312 funcs = [t for t in things.itervalues() if t.kind==Thing.FUNCTION and is_relevant(t)]
313 funcs.sort(key=(lambda f: f.name))
314 enums = filter(is_relevant, enums)
315 enums.sort(key=(lambda e: e.value))
317 # Some final preparations for creating the files
318 for t in itertools.chain(funcs, enums):
319 if target_api in t.supported_apis and t.supported_apis[target_api]!="ext":
320 t.version = t.supported_apis[target_api]
322 core_version = t.version
324 # Things in backport extensions don't acquire an extension suffix
325 if t.extension and not t.name.endswith(ext_type) and target_api in t.supported_apis:
326 backport_ext = t.extension
329 f.typedef = "FPtr_%s"%f.name
331 if target_api in target_ext.supported_apis:
332 source_ext = target_ext
335 for t in itertools.chain(funcs, enums):
337 if target_api in s.supported_apis:
338 candidates[s.extension.name] = candidates.get(s.extension.name, 0)+1
340 source_ext = extensions[max(candidates.iteritems(), key=(lambda x: x[1]))[0]]
346 out = file(out_base+".h", "w")
347 out.write("#ifndef MSP_GL_%s_\n"%target_ext.name.upper())
348 out.write("#define MSP_GL_%s_\n"%target_ext.name.upper())
351 #include <msp/gl/extension.h>
352 #include <msp/gl/gl.h>
362 out.write("typedef %s (*%s)(%s);\n"%(f.return_type, f.typedef, ", ".join(f.params)))
367 if target_api=="gles2":
370 enums_by_category = {}
374 cat = api_prefix+"_VERSION_"+"_".join(map(str, e.version))
376 cat = "GL_"+e.extension.name
377 enums_by_category.setdefault(cat, []).append(e)
379 for cat in sorted(enums_by_category.keys()):
381 out.write("#ifndef %s\n"%cat)
382 for e in enums_by_category[cat]:
383 out.write("#define %s 0x%04X\n"%(e.name, e.value))
385 out.write("#endif\n")
389 out.write("extern %s %s;\n"%(f.typedef, f.name))
391 out.write("extern Extension %s;\n"%target_ext.name)
400 out = file(out_base+".cpp", "w")
401 out.write("#include \"%s.h\"\n"%target_ext.name.lower())
406 #define GET_PROC_ADDRESS(x) ::x
408 #define GET_PROC_ADDRESS(x) get_proc_address(#x)
418 out.write("%s %s = 0;\n"%(f.typedef, f.name))
420 out.write("\nExtension::SupportLevel init_%s()\n{\n"%target_ext.name.lower())
422 out.write("\tif(is_version_at_least(%d, %d)"%tuple(core_version))
424 out.write(" || is_supported(\"GL_%s\")"%backport_ext.name)
425 out.write(")\n\t{\n")
427 if f.version or target_api in f.supported_apis:
428 out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, f.typedef, f.name))
429 out.write("\t\treturn Extension::CORE;\n")
431 if source_ext and source_ext!=backport_ext:
432 out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(source_ext.name))
437 if s.version or target_api in s.supported_apis:
438 out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, f.typedef, s.name))
439 out.write("\t\treturn Extension::EXTENSION;\n")
441 out.write("\treturn Extension::UNSUPPORTED;\n")
444 out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(target_ext.name, target_ext.name, target_ext.name.lower()))