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 deprecated_version = None
38 if target_ext.endswith(".glext"):
45 if parts[0]=="extension":
47 elif parts[0]=="core_version":
48 if parts[1]==target_api:
49 core_version = parts[2]
50 elif parts[0]=="deprecated":
51 if parts[1]==target_api:
52 deprecated_version = parts[2]
53 elif parts[0]=="secondary":
54 secondary.append(parts[1])
55 elif parts[0]=="backport":
56 backport_ext = parts[1]
57 elif parts[0]=="ignore":
58 ignore_things.append(parts[1])
60 out_base = os.path.splitext(sys.argv[i+1])[0]
62 secondary = sys.argv[i+1:]
64 if secondary and secondary[0][0].isdigit():
65 core_version = secondary.pop(0)
67 ext_type = target_ext.split('_')[0]
70 core_version = map(int, core_version.split('.'))
72 if deprecated_version:
73 deprecated_version = map(int, deprecated_version.split('.'))
76 out_base = target_ext.lower()
78 ### XML file parsing ###
84 def __init__(self, name, kind):
88 self.deprecated_version = None
90 self.supported_apis = {}
95 class Function(Thing):
96 def __init__(self, name):
97 Thing.__init__(self, name, Thing.FUNCTION)
98 self.return_type = "void"
103 def __init__(self, name):
104 Thing.__init__(self, name, Thing.ENUM)
106 self.bitmask = (name.endswith("_BIT") or "_BIT_" in name)
109 def __init__(self, name):
111 self.supported_apis = []
112 underscore = name.find('_')
113 self.ext_type = name[0:underscore]
114 self.base_name = name[underscore+1:]
119 def get_nested_elements(elem, path):
120 childElements = [c for c in elem.childNodes if c.nodeType==xml.dom.Node.ELEMENT_NODE]
122 head, tail = path.split('/', 1)
124 for c in childElements:
126 result += get_nested_elements(c, tail)
129 return [c for c in childElements if c.tagName==path]
131 def get_first_child(elem, tag):
132 for c in elem.childNodes:
133 if c.nodeType==xml.dom.Node.ELEMENT_NODE and c.tagName==tag:
137 def get_text_contents(node):
139 for c in node.childNodes:
140 if c.nodeType==xml.dom.Node.TEXT_NODE or c.nodeType==xml.dom.Node.CDATA_SECTION_NODE:
143 result += get_text_contents(c)
146 def parse_command(cmd):
147 proto = get_first_child(cmd, "proto")
148 name = get_text_contents(get_first_child(proto, "name"))
149 func = things.get(name)
151 func = Function(name)
154 aliases = get_nested_elements(cmd, "alias")
155 func.aliases = [a.getAttribute("name") for a in aliases]
157 ptype = get_first_child(proto, "ptype")
159 func.return_type = get_text_contents(ptype)
161 for c in proto.childNodes:
162 if c.nodeType==xml.dom.Node.TEXT_NODE and c.data.strip():
163 func.return_type = c.data.strip()
166 params = get_nested_elements(cmd, "param")
167 func.params = map(get_text_contents, params)
170 name = en.getAttribute("name")
171 enum = things.get(name)
176 enum.value = int(en.getAttribute("value"), 16)
178 def parse_feature(feat):
179 api = feat.getAttribute("api")
180 version = feat.getAttribute("number")
182 version = map(int, version.split('.'))
186 requires = get_nested_elements(feat, "require")
188 commands = get_nested_elements(req, "command")
189 enums = get_nested_elements(req, "enum")
190 for t in itertools.chain(commands, enums):
191 name = t.getAttribute("name")
192 thing = things.get(name)
194 thing.supported_apis.setdefault(api, version)
196 if not api or api==target_api:
197 removes = get_nested_elements(feat, "remove")
199 profile = rem.getAttribute("profile")
200 commands = get_nested_elements(rem, "command")
201 enums = get_nested_elements(rem, "enum")
203 for t in itertools.chain(commands, enums):
204 name = t.getAttribute("name")
209 things[name].deprecated.setdefault(api, version)
211 def parse_extension(ext):
212 ext_name = ext.getAttribute("name")
213 if ext_name.startswith("GL_"):
214 ext_name = ext_name[3:]
216 supported = ext.getAttribute("supported").split('|')
217 if target_api not in supported and ext_name!=target_ext:
220 extension = extensions.get(ext_name)
222 extension = Extension(ext_name)
223 extensions[ext_name] = extension
225 extension.supported_apis = supported
227 requires = get_nested_elements(ext, "require")
229 api = req.getAttribute("api")
233 supported = extension.supported_apis
235 commands = get_nested_elements(req, "command")
236 enums = get_nested_elements(req, "enum")
237 for t in itertools.chain(commands, enums):
238 name = t.getAttribute("name")
239 if name in ignore_things:
242 thing = things.get(name)
244 if thing.extension and extension.name!=target_ext:
245 if thing.extension.ext_type=="ARB" or thing.extension.name==target_ext:
247 if thing.extension.ext_type=="EXT" and extension.ext_type!="ARB":
250 thing.extension = extension
252 thing.supported_apis.setdefault(a, "ext")
255 doc = xml.dom.minidom.parse(fn)
256 root = doc.documentElement
258 commands = get_nested_elements(root, "commands/command")
262 enums = get_nested_elements(root, "enums/enum")
266 features = get_nested_elements(root, "feature")
267 for feat in features:
270 extensions = get_nested_elements(root, "extensions/extension")
271 for ext in extensions:
275 parse_file("gl.fixes.xml")
277 ### Additional processing ###
279 if target_ext in extensions:
280 target_ext = extensions[target_ext]
282 print "Extension %s not found"%target_ext
285 # Find aliases for enums
286 enums = [t for t in things.itervalues() if t.kind==Thing.ENUM]
287 core_enums = [e for e in enums if any(v!="ext" for v in e.supported_apis.itervalues())]
288 core_enums_by_value = dict((e.value, None) for e in core_enums)
290 def get_key_api(things):
291 common_apis = set(target_ext.supported_apis)
293 common_apis.intersection_update(t.supported_apis.keys())
295 return common_apis.pop()
300 if all(v=="ext" for v in e.supported_apis.values()) and e.value in core_enums_by_value:
301 if core_enums_by_value[e.value] is None:
302 candidates = [ce for ce in core_enums if ce.value==e.value]
303 key_api = get_key_api(candidates)
304 core_enums_by_value[e.value] = list(sorted(candidates, key=(lambda x: x.supported_apis.get(key_api, "ext"))))
305 for ce in core_enums_by_value[e.value]:
306 if ce.bitmask==e.bitmask:
307 e.aliases.append(ce.name)
310 # Create references from core things to their extension counterparts
311 for t in things.itervalues():
314 alias = things.get(a)
316 if target_api in t.supported_apis:
317 alias.sources.insert(0, t)
319 alias.sources.append(t)
321 # Find the things we want to include in this extension
323 # Unpromoted extension things are relevant
324 if t.extension and t.extension==target_ext and not t.aliases:
327 # Core things promoted from the extension are also relevant
329 if s.extension==target_ext or s.extension.name in secondary:
334 funcs = [t for t in things.itervalues() if t.kind==Thing.FUNCTION and is_relevant(t)]
335 funcs.sort(key=(lambda f: f.name))
336 enums = filter(is_relevant, enums)
337 enums.sort(key=(lambda e: e.value))
339 # Some final preparations for creating the files
340 core_version_candidates = {}
341 min_deprecated_version = [999, 0]
342 backport_ext_candidates = []
343 for t in itertools.chain(funcs, enums):
344 if target_api in t.supported_apis and t.supported_apis[target_api]!="ext":
345 t.version = t.supported_apis[target_api]
347 ver_tuple = tuple(t.version)
348 core_version_candidates[ver_tuple] = core_version_candidates.get(ver_tuple, 0)+1
350 if target_api in t.deprecated:
351 t.deprecated_version = t.deprecated[target_api]
352 min_deprecated_version = min(min_deprecated_version, t.deprecated_version)
354 min_deprecated_version = None
356 # Things in backport extensions don't acquire an extension suffix
357 if t.extension and not t.name.endswith(ext_type) and target_api in t.supported_apis:
358 if t.extension not in backport_ext_candidates:
359 backport_ext_candidates.append(t.extension)
361 if not core_version and core_version_candidates:
362 core_version_candidates = list((v, k) for k, v in core_version_candidates.items())
363 if len(core_version_candidates)>1:
364 core_version_candidates.sort(reverse=True)
365 if core_version_candidates[1][0]+1>=core_version_candidates[0][0]:
366 ver0 = core_version_candidates[0][1]
367 ver1 = core_version_candidates[1][1]
368 print "Warning: multiple likely core version candidates: %d.%d %d.%d"%(ver0[0], ver0[1], ver1[0], ver1[1])
369 core_version = core_version_candidates[0][1]
371 if not deprecated_version:
372 deprecated_version = min_deprecated_version
375 if backport_ext=="none":
378 backport_ext = extensions[backport_ext]
380 if backport_ext not in backport_ext_candidates:
381 print "Warning: explicitly specified backport extension %s does not look like a backport extension"
382 elif backport_ext_candidates:
383 if len(backport_ext_candidates)>1:
384 print "Warning: multiple backport extension candidates: %s"%(" ".join(e.name for e in backport_ext_candidates))
386 for e in backport_ext_candidates:
387 if e.base_name==target_ext.base_name:
390 if not backport_ext and len(backport_ext_candidates)==1:
391 print "Warning: potential backport extension has mismatched name: %s"%backport_ext_candidates[0].name
394 f.typedef = "FPtr_%s"%f.name
396 if target_api in target_ext.supported_apis:
397 source_ext = target_ext
400 for t in itertools.chain(funcs, enums):
402 if target_api in s.supported_apis:
403 candidates[s.extension.name] = candidates.get(s.extension.name, 0)+1
405 source_ext = extensions[max(candidates.iteritems(), key=(lambda x: x[1]))[0]]
410 any_supported = False
412 for t in itertools.chain(funcs, enums):
413 if target_api in t.supported_apis:
416 all_supported = False
418 if not any_supported:
419 print "Warning: %s is not supported by the target API"%target_ext.name
420 elif not all_supported:
421 print "Warning: %s is only partially supported by the target API"%target_ext.name
423 label = "Warning: Unsupported tokens: "
424 for t in itertools.chain(funcs, enums):
425 if target_api not in t.supported_apis:
426 if unsupported and len(label)+len(unsupported)+2+len(t.name)>78:
427 print label+unsupported
428 label = " "*len(label)
432 unsupported += t.name
434 print label+unsupported
438 out = file(out_base+".h", "w")
439 out.write("#ifndef MSP_GL_%s_\n"%target_ext.name.upper())
440 out.write("#define MSP_GL_%s_\n"%target_ext.name.upper())
443 #include <msp/gl/extension.h>
444 #include <msp/gl/gl.h>
454 out.write("typedef %s (APIENTRY *%s)(%s);\n"%(f.return_type, f.typedef, ", ".join(f.params)))
459 if target_api=="gles2":
462 enums_by_category = {}
466 cat = api_prefix+"_VERSION_"+"_".join(map(str, e.version))
468 cat = "GL_"+e.extension.name
469 enums_by_category.setdefault(cat, []).append(e)
471 for cat in sorted(enums_by_category.keys()):
473 out.write("#ifndef %s\n"%cat)
474 for e in enums_by_category[cat]:
475 out.write("#define %s 0x%04X\n"%(e.name, e.value))
477 out.write("#endif\n")
481 out.write("extern %s %s;\n"%(f.typedef, f.name))
483 out.write("extern Extension %s;\n"%target_ext.name)
492 out = file(out_base+".cpp", "w")
493 out.write("#include \"%s.h\"\n"%target_ext.name.lower())
498 #define GET_PROC_ADDRESS(x) &::x
500 #define GET_PROC_ADDRESS(x) get_proc_address(#x)
504 #define GET_PROC_ADDRESS_1_1(x) &::x
506 #define GET_PROC_ADDRESS_1_1(x) GET_PROC_ADDRESS(x)
516 out.write("%s %s = 0;\n"%(f.typedef, f.name))
518 out.write("\nExtension::SupportLevel init_%s()\n{\n"%target_ext.name.lower())
520 out.write("\tif(is_disabled(\"GL_%s\"))\n\t\treturn Extension::UNSUPPORTED;\n"%target_ext.name)
521 out.write("#if !defined(__APPLE__) || defined(GL_VERSION_%d_%d)\n"%tuple(core_version))
524 out.write("is_supported(\"GL_%s\") || "%backport_ext.name)
525 out.write("is_supported(Version(%d, %d)"%tuple(core_version))
526 if deprecated_version:
527 out.write(", Version(%d, %d)"%tuple(deprecated_version))
528 out.write("))\n\t{\n")
530 if target_api in f.supported_apis:
532 if f.version is not None and f.version<=[1, 1]:
534 out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS%s(%s));\n"%(f.name, f.typedef, gpa_suffix, f.name))
535 out.write("\t\treturn Extension::CORE;\n")
537 out.write("#endif\n")
538 if source_ext and source_ext!=backport_ext:
539 out.write("#if !defined(__APPLE__) || defined(GL_%s)\n"%target_ext.name)
540 out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(source_ext.name))
545 if s.name.endswith(source_ext.ext_type):
553 if target_api in src.supported_apis:
554 if not src.name.endswith(source_ext.ext_type):
555 print "Warning: %s does not match extension type %s"%(src.name, source_ext.ext_type)
556 out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, f.typedef, src.name))
557 out.write("\t\treturn Extension::EXTENSION;\n")
559 out.write("#endif\n")
560 out.write("\treturn Extension::UNSUPPORTED;\n")
563 out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(target_ext.name, target_ext.name, target_ext.name.lower()))