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]
37 if target_ext.endswith(".glext"):
44 if parts[0]=="extension":
46 elif parts[0]=="core_version":
47 if parts[1]==target_api:
48 core_version = parts[2]
49 elif parts[0]=="secondary":
50 secondary.append(parts[1])
51 elif parts[0]=="backport":
52 backport_ext = parts[1]
53 elif parts[0]=="ignore":
54 ignore_things.append(parts[1])
56 out_base = os.path.splitext(sys.argv[i+1])[0]
58 secondary = sys.argv[i+1:]
60 if secondary and secondary[0][0].isdigit():
61 core_version = secondary.pop(0)
63 ext_type = target_ext.split('_')[0]
66 core_version = map(int, core_version.split('.'))
69 out_base = target_ext.lower()
71 ### XML file parsing ###
77 def __init__(self, name, kind):
82 self.supported_apis = {}
86 class Function(Thing):
87 def __init__(self, name):
88 Thing.__init__(self, name, Thing.FUNCTION)
89 self.return_type = "void"
94 def __init__(self, name):
95 Thing.__init__(self, name, Thing.ENUM)
97 self.bitmask = (name.endswith("_BIT") or "_BIT_" in name)
100 def __init__(self, name):
102 self.supported_apis = []
103 underscore = name.find('_')
104 self.ext_type = name[0:underscore]
105 self.base_name = name[underscore+1:]
110 def get_nested_elements(elem, path):
111 childElements = [c for c in elem.childNodes if c.nodeType==xml.dom.Node.ELEMENT_NODE]
113 head, tail = path.split('/', 1)
115 for c in childElements:
117 result += get_nested_elements(c, tail)
120 return [c for c in childElements if c.tagName==path]
122 def get_first_child(elem, tag):
123 for c in elem.childNodes:
124 if c.nodeType==xml.dom.Node.ELEMENT_NODE and c.tagName==tag:
128 def get_text_contents(node):
130 for c in node.childNodes:
131 if c.nodeType==xml.dom.Node.TEXT_NODE or c.nodeType==xml.dom.Node.CDATA_SECTION_NODE:
134 result += get_text_contents(c)
137 def parse_command(cmd):
138 proto = get_first_child(cmd, "proto")
139 name = get_text_contents(get_first_child(proto, "name"))
140 func = things.get(name)
142 func = Function(name)
145 aliases = get_nested_elements(cmd, "alias")
146 func.aliases = [a.getAttribute("name") for a in aliases]
148 ptype = get_first_child(proto, "ptype")
150 func.return_type = get_text_contents(ptype)
152 for c in proto.childNodes:
153 if c.nodeType==xml.dom.Node.TEXT_NODE and c.data.strip():
154 func.return_type = c.data.strip()
157 params = get_nested_elements(cmd, "param")
158 func.params = map(get_text_contents, params)
161 name = en.getAttribute("name")
162 enum = things.get(name)
167 enum.value = int(en.getAttribute("value"), 16)
169 def parse_feature(feat):
170 api = feat.getAttribute("api")
171 version = feat.getAttribute("number")
173 version = map(int, version.split('.'))
177 requires = get_nested_elements(feat, "require")
179 commands = get_nested_elements(req, "command")
180 enums = get_nested_elements(req, "enum")
181 for t in itertools.chain(commands, enums):
182 name = t.getAttribute("name")
183 thing = things.get(name)
185 thing.supported_apis.setdefault(api, version)
187 if not api or api==target_api:
188 removes = get_nested_elements(feat, "remove")
190 profile = rem.getAttribute("profile")
191 commands = get_nested_elements(rem, "command")
192 enums = get_nested_elements(rem, "enum")
194 for t in itertools.chain(commands, enums):
195 name = t.getAttribute("name")
196 if profile!="core" and name in things:
199 def parse_extension(ext):
200 ext_name = ext.getAttribute("name")
201 if ext_name.startswith("GL_"):
202 ext_name = ext_name[3:]
204 supported = ext.getAttribute("supported").split('|')
205 if target_api not in supported and ext_name!=target_ext:
208 extension = extensions.get(ext_name)
210 extension = Extension(ext_name)
211 extensions[ext_name] = extension
213 extension.supported_apis = supported
215 requires = get_nested_elements(ext, "require")
217 api = req.getAttribute("api")
221 supported = extension.supported_apis
223 commands = get_nested_elements(req, "command")
224 enums = get_nested_elements(req, "enum")
225 for t in itertools.chain(commands, enums):
226 name = t.getAttribute("name")
227 if name in ignore_things:
230 thing = things.get(name)
232 if thing.extension and extension.name!=target_ext:
233 if thing.extension.ext_type=="ARB" or thing.extension.name==target_ext:
235 if thing.extension.ext_type=="EXT" and extension.ext_type!="ARB":
238 thing.extension = extension
240 thing.supported_apis.setdefault(a, "ext")
243 doc = xml.dom.minidom.parse(fn)
244 root = doc.documentElement
246 commands = get_nested_elements(root, "commands/command")
250 enums = get_nested_elements(root, "enums/enum")
254 features = get_nested_elements(root, "feature")
255 for feat in features:
258 extensions = get_nested_elements(root, "extensions/extension")
259 for ext in extensions:
263 parse_file("gl.fixes.xml")
265 ### Additional processing ###
267 if target_ext in extensions:
268 target_ext = extensions[target_ext]
270 print "Extension %s not found"%target_ext
273 # Find aliases for enums
274 enums = [t for t in things.itervalues() if t.kind==Thing.ENUM]
275 core_enums = [e for e in enums if any(v!="ext" for v in e.supported_apis.itervalues())]
276 core_enums_by_value = dict((e.value, None) for e in core_enums)
278 def get_key_api(things):
279 common_apis = set(target_ext.supported_apis)
281 common_apis.intersection_update(t.supported_apis.keys())
283 return common_apis.pop()
288 if all(v=="ext" for v in e.supported_apis.values()) and e.value in core_enums_by_value:
289 if core_enums_by_value[e.value] is None:
290 candidates = [ce for ce in core_enums if ce.value==e.value]
291 key_api = get_key_api(candidates)
292 core_enums_by_value[e.value] = list(sorted(candidates, key=(lambda x: x.supported_apis.get(key_api, "ext"))))
293 for ce in core_enums_by_value[e.value]:
294 if ce.bitmask==e.bitmask:
295 e.aliases.append(ce.name)
298 # Create references from core things to their extension counterparts
299 for t in things.itervalues():
302 alias = things.get(a)
304 if target_api in t.supported_apis:
305 alias.sources.insert(0, t)
307 alias.sources.append(t)
309 # Find the things we want to include in this extension
311 # Unpromoted extension things are relevant
312 if t.extension and t.extension==target_ext and not t.aliases:
315 # Core things promoted from the extension are also relevant
317 if s.extension==target_ext or s.extension.name in secondary:
322 funcs = [t for t in things.itervalues() if t.kind==Thing.FUNCTION and is_relevant(t)]
323 funcs.sort(key=(lambda f: f.name))
324 enums = filter(is_relevant, enums)
325 enums.sort(key=(lambda e: e.value))
327 # Some final preparations for creating the files
328 backport_ext_candidates = []
329 for t in itertools.chain(funcs, enums):
330 if target_api in t.supported_apis and t.supported_apis[target_api]!="ext":
331 t.version = t.supported_apis[target_api]
333 core_version = t.version
335 # Things in backport extensions don't acquire an extension suffix
336 if t.extension and not t.name.endswith(ext_type) and target_api in t.supported_apis:
337 if t.extension not in backport_ext_candidates:
338 backport_ext_candidates.append(t.extension)
341 if backport_ext=="none":
344 backport_ext = extensions[backport_ext]
346 if backport_ext not in backport_ext_candidates:
347 print "Warning: explicitly specified backport extension %s does not look like a backport extension"
348 elif backport_ext_candidates:
349 if len(backport_ext_candidates)>1:
350 print "Warning: multiple backport extension candidates: %s"%(" ".join(e.name for e in backport_ext_candidates))
352 for e in backport_ext_candidates:
353 if e.base_name==target_ext.base_name:
356 if not backport_ext and len(backport_ext_candidates)==1:
357 print "Warning: potential backport extension has mismatched name: %s"%backport_ext_candidates[0].name
360 f.typedef = "FPtr_%s"%f.name
362 if target_api in target_ext.supported_apis:
363 source_ext = target_ext
366 for t in itertools.chain(funcs, enums):
368 if target_api in s.supported_apis:
369 candidates[s.extension.name] = candidates.get(s.extension.name, 0)+1
371 source_ext = extensions[max(candidates.iteritems(), key=(lambda x: x[1]))[0]]
376 any_supported = False
378 for t in itertools.chain(funcs, enums):
379 if target_api in t.supported_apis:
382 all_supported = False
384 if not any_supported:
385 print "Warning: %s is not supported by the target API"%target_ext.name
386 elif not all_supported:
387 print "Warning: %s is only partially supported by the target API"%target_ext.name
389 label = "Warning: Unsupported tokens: "
390 for t in itertools.chain(funcs, enums):
391 if target_api not in t.supported_apis:
392 if unsupported and len(label)+len(unsupported)+2+len(t.name)>78:
393 print label+unsupported
394 label = " "*len(label)
398 unsupported += t.name
400 print label+unsupported
404 out = file(out_base+".h", "w")
405 out.write("#ifndef MSP_GL_%s_\n"%target_ext.name.upper())
406 out.write("#define MSP_GL_%s_\n"%target_ext.name.upper())
409 #include <msp/gl/extension.h>
410 #include <msp/gl/gl.h>
420 out.write("typedef %s (APIENTRY *%s)(%s);\n"%(f.return_type, f.typedef, ", ".join(f.params)))
425 if target_api=="gles2":
428 enums_by_category = {}
432 cat = api_prefix+"_VERSION_"+"_".join(map(str, e.version))
434 cat = "GL_"+e.extension.name
435 enums_by_category.setdefault(cat, []).append(e)
437 for cat in sorted(enums_by_category.keys()):
439 out.write("#ifndef %s\n"%cat)
440 for e in enums_by_category[cat]:
441 out.write("#define %s 0x%04X\n"%(e.name, e.value))
443 out.write("#endif\n")
447 out.write("extern %s %s;\n"%(f.typedef, f.name))
449 out.write("extern Extension %s;\n"%target_ext.name)
458 out = file(out_base+".cpp", "w")
459 out.write("#include \"%s.h\"\n"%target_ext.name.lower())
464 #define GET_PROC_ADDRESS(x) &::x
466 #define GET_PROC_ADDRESS(x) get_proc_address(#x)
470 #define GET_PROC_ADDRESS_1_1(x) &::x
472 #define GET_PROC_ADDRESS_1_1(x) GET_PROC_ADDRESS(x)
482 out.write("%s %s = 0;\n"%(f.typedef, f.name))
484 out.write("\nExtension::SupportLevel init_%s()\n{\n"%target_ext.name.lower())
486 out.write("\tif(is_version_at_least(%d, %d)"%tuple(core_version))
488 out.write(" || is_supported(\"GL_%s\")"%backport_ext.name)
489 out.write(")\n\t{\n")
491 if target_api in f.supported_apis:
493 if f.version is not None and f.version<=[1, 1]:
495 out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS%s(%s));\n"%(f.name, f.typedef, gpa_suffix, f.name))
496 out.write("\t\treturn Extension::CORE;\n")
498 if source_ext and source_ext!=backport_ext:
499 out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(source_ext.name))
504 if s.name.endswith(source_ext.ext_type):
512 if target_api in src.supported_apis:
513 if not src.name.endswith(source_ext.ext_type):
514 print "Warning: %s does not match extension type %s"%(src.name, source_ext.ext_type)
515 out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, f.typedef, src.name))
516 out.write("\t\treturn Extension::EXTENSION;\n")
518 out.write("\treturn Extension::UNSUPPORTED;\n")
521 out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(target_ext.name, target_ext.name, target_ext.name.lower()))