X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=blobdiff_plain;f=scripts%2Fextgen.py;h=ff40ec60eadd1f515336f78d4d82eec2e0cfdfff;hp=c7c06b2c07c115a11bf6986f8f52d2761ef87999;hb=e19309340e90ee881e9cb2f8b7c33a5b89681aa6;hpb=56c3e1bc5e6cf7d081c544bfedbfa436ec5d963f diff --git a/scripts/extgen.py b/scripts/extgen.py index c7c06b2c..ff40ec60 100755 --- a/scripts/extgen.py +++ b/scripts/extgen.py @@ -5,116 +5,115 @@ import os import xml.dom import xml.dom.minidom import itertools +import re + +class Version: + def __init__(self, *args): + if len(args)==0: + self.major = 0 + self.minor = 0 + elif len(args)==2: + self.major = args[0] + self.minor = args[1] + else: + raise TypeError, "__init__() takes zero or two arguments (%d given)"%len(args) -### Command line processing ### + def __str__(self): + return "%d.%d"%(self.major, self.minor) -if len(sys.argv)<2: - print """Usage: - extgen.py [api] [] [ ...] - extgen.py [api] [] + def __repr__(self): + return "Version(%d, %d)"%(self.major, self.minor) -Reads gl.xml and generates files to use . Any promoted functions -are exposed with their promoted names. If extensions are given, -any promoted functions from those are pulled in as well. can -be given to override the version where was promoted to core. + def as_define(self): + return "VERSION_%d_%d"%(self.major, self.minor) -In the second form, the parameters are read from . If is -absent, the extension's lowercased name is used. Anything after the last dot -in is removed and replaced with cpp and h.""" - sys.exit(0) + def __lt__(self, other): + if other is None: + return False -target_api = "gl" + if self.major!=other.major: + return self.majorother.major + return self.minor>other.minor + + def __eq__(self, other): + if other is None: + return False + + return (self.major==other.major and self.minor==other.minor) -target_ext = sys.argv[i] -backport_ext = None -deprecated_version = None -out_base = None -ignore_things = [] -if target_ext.endswith(".glext"): - fn = target_ext - target_ext = None - core_version = None - secondary = [] - for line in open(fn): - parts = line.split() - if parts[0]=="extension": - target_ext = parts[1] - elif parts[0]=="core_version": - if parts[1]==target_api: - core_version = parts[2] - elif parts[0]=="deprecated": - if parts[1]==target_api: - deprecated_version = parts[2] - elif parts[0]=="secondary": - secondary.append(parts[1]) - elif parts[0]=="backport": - backport_ext = parts[1] - elif parts[0]=="ignore": - ignore_things.append(parts[1]) - if i+1max_version: + max_version = supp.core_version + lower_count += max_count + max_count = 1 + elif supp.core_version==max_version: + max_count += 1 + else: + lower_count += 1 + else: + missing.append(t) - features = get_nested_elements(root, "feature") - for feat in features: - parse_feature(feat) + if lower_count>max_count or (missing and len(missing)*2best_count: + best_ext = e + best_count = count + + if best_count*2>=total_count: + print "Warning: Inconsistent backport extension %s"%best_ext.name + +def collect_extensions(thing, api, exts): + supp = thing.api_support.get(api) + if not supp: + return -if target_ext in extensions: - target_ext = extensions[target_ext] -else: - print "Extension %s not found"%target_ext - sys.exit(1) + for e in supp.extensions: + if not e.backport and e.ext_type!="MSP" and e not in exts: + exts.append(e) -# Find aliases for enums -enums = [t for t in things.itervalues() if t.kind==Thing.ENUM] -core_enums = [e for e in enums if any(v!="ext" for v in e.supported_apis.itervalues())] -core_enums_by_value = dict((e.value, None) for e in core_enums) + for s in supp.sources: + collect_extensions(s, api, exts) -def get_key_api(things): - common_apis = set(target_ext.supported_apis) +def detect_source_extension(host_api, things, debug=False): + things_by_ext = {} for t in things: - common_apis.intersection_update(t.supported_apis.keys()) - if common_apis: - return common_apis.pop() - else: - return target_api - -for e in enums: - if all(v=="ext" for v in e.supported_apis.values()) and e.value in core_enums_by_value: - if core_enums_by_value[e.value] is None: - candidates = [ce for ce in core_enums if ce.value==e.value] - key_api = get_key_api(candidates) - core_enums_by_value[e.value] = list(sorted(candidates, key=(lambda x: x.supported_apis.get(key_api, "ext")))) - for ce in core_enums_by_value[e.value]: - if ce.bitmask==e.bitmask: - e.aliases.append(ce.name) - break - -# Create references from core things to their extension counterparts -for t in things.itervalues(): - if t.extension: - for a in t.aliases: - alias = things.get(a) - if alias: - if target_api in t.supported_apis: - alias.sources.insert(0, t) + exts = [] + collect_extensions(t, host_api.name, exts) + for e in exts: + things_by_ext.setdefault(e, []).append(t) + + if debug: + print "---" + print "Looking for %d things in %d extensions"%(len(things), len(things_by_ext)) + + extensions = [] + keep_exts = 0 + base_version = None + recheck_base_version = True + missing = set(things) + while 1: + if recheck_base_version: + max_version = Version(1, 0) + for t in missing: + supp = t.api_support.get(host_api.name) + if supp and supp.core_version and max_version: + max_version = max(max_version, supp.core_version) else: - alias.sources.append(t) - -# Find the things we want to include in this extension -def is_relevant(t): - # Unpromoted extension things are relevant - if t.extension and t.extension==target_ext and not t.aliases: - return True - - # Core things promoted from the extension are also relevant - for s in t.sources: - if s.extension==target_ext or s.extension.name in secondary: - return True - - return False - -funcs = [t for t in things.itervalues() if t.kind==Thing.FUNCTION and is_relevant(t)] -funcs.sort(key=(lambda f: f.name)) -enums = filter(is_relevant, enums) -enums.sort(key=(lambda e: e.value)) - -# Some final preparations for creating the files -core_version_candidates = {} -min_deprecated_version = [999, 0] -backport_ext_candidates = [] -for t in itertools.chain(funcs, enums): - if target_api in t.supported_apis and t.supported_apis[target_api]!="ext": - t.version = t.supported_apis[target_api] - if t.version: - ver_tuple = tuple(t.version) - core_version_candidates[ver_tuple] = core_version_candidates.get(ver_tuple, 0)+1 - - if target_api in t.deprecated: - t.deprecated_version = t.deprecated[target_api] - min_deprecated_version = min(min_deprecated_version, t.deprecated_version) - else: - min_deprecated_version = None - - # Things in backport extensions don't acquire an extension suffix - if t.extension and not t.name.endswith(ext_type) and target_api in t.supported_apis: - if t.extension not in backport_ext_candidates: - backport_ext_candidates.append(t.extension) - -if not core_version and core_version_candidates: - core_version_candidates = list((v, k) for k, v in core_version_candidates.items()) - if len(core_version_candidates)>1: - core_version_candidates.sort(reverse=True) - if core_version_candidates[1][0]+1>=core_version_candidates[0][0]: - ver0 = core_version_candidates[0][1] - ver1 = core_version_candidates[1][1] - print "Warning: multiple likely core version candidates: %d.%d %d.%d"%(ver0[0], ver0[1], ver1[0], ver1[1]) - core_version = core_version_candidates[0][1] - -if not deprecated_version: - deprecated_version = min_deprecated_version - -if backport_ext: - if backport_ext=="none": - backport_ext = None - else: - bpe_name = backport_ext - backport_ext = extensions.get(backport_ext) - - if backport_ext not in backport_ext_candidates: - print "Warning: explicitly specified backport extension %s does not look like a backport extension"%bpe_name -elif backport_ext_candidates: - if len(backport_ext_candidates)>1: - print "Warning: multiple backport extension candidates: %s"%(" ".join(e.name for e in backport_ext_candidates)) - - for e in backport_ext_candidates: - if e.base_name==target_ext.base_name: - backport_ext = e - - if not backport_ext and len(backport_ext_candidates)==1: - print "Warning: potential backport extension has mismatched name: %s"%backport_ext_candidates[0].name - -for f in funcs: - f.typedef = "FPtr_%s"%f.name - -if target_api in target_ext.supported_apis: - source_ext = target_ext -else: - candidates = {} - for t in itertools.chain(funcs, enums): - for s in t.sources: - if target_api in s.supported_apis: - candidates[s.extension.name] = candidates.get(s.extension.name, 0)+1 - if candidates: - source_ext = extensions[max(candidates.iteritems(), key=(lambda x: x[1]))[0]] + max_version = None + + if max_version: + if not base_version or max_versionlargest_count: + largest_ext = e + largest_count = count + elif count==largest_count and e.preference>largest_ext.preference: + largest_ext = e + + if debug: + print "Found %d things in %s"%(largest_count, largest_ext.name) + + extensions.append(largest_ext) + for t in things_by_ext[largest_ext]: + missing.remove(t) + + supp = t.api_support.get(host_api.name) + if supp and supp.core_version==base_version: + recheck_base_version = True + + del things_by_ext[largest_ext] + for e in things_by_ext.keys(): + unseen = filter((lambda t: t in missing), things_by_ext[e]) + if unseen: + things_by_ext[e] = unseen + else: + del things_by_ext[e] + + if not missing: + return None, extensions + elif base_version: + if debug: + print "Found remaining things in version %s"%base_version + if keep_exts78: - print label+unsupported - label = " "*len(label) - unsupported = "" - if unsupported: - unsupported += ", " - unsupported += t.name - if unsupported: - print label+unsupported - -### Output ### - -out = file(out_base+".h", "w") -out.write("#ifndef MSP_GL_%s_\n"%target_ext.name.upper()) -out.write("#define MSP_GL_%s_\n"%target_ext.name.upper()) - -out.write(""" + if debug: + print "%d things still missing:"%len(missing) + for t in missing: + print " "+t.name + return None, None + + +class SourceGenerator: + def __init__(self, host_api, ext_name, things, optional_things, debug=False): + self.host_api = host_api + self.api_prefix = "GL" + if self.host_api.name=="gles2": + self.api_prefix = "GL_ES" + self.ext_name = ext_name + all_things = things+optional_things + self.funcs = filter((lambda t: t.kind==Thing.FUNCTION), all_things) + self.funcs.sort(key=(lambda f: f.name)) + self.func_typedefs = dict((f.name, "FPtr_"+f.name) for f in self.funcs) + self.enums = filter((lambda t: t.kind==Thing.ENUM), all_things) + self.enums.sort(key=(lambda e: e.value)) + self.core_version = detect_core_version(host_api, things, debug) + self.deprecated_version = detect_deprecated_version(host_api, things, debug) + self.backport_ext = detect_backport_extension(host_api, things); + b, e = detect_source_extension(host_api, things, debug) + self.base_version = b + self.source_exts = e + + if not self.core_version and not self.backport_ext and not self.source_exts: + print "Warning: Not supportable on host API" + + def dump_info(self): + print "--- Extension information ---" + print "Extension %s"%self.ext_name + print "Core %s"%self.core_version + print "Deprecated %s"%self.deprecated_version + if self.backport_ext: + print "Backport %s"%self.backport_ext.name + if self.source_exts: + names = [e.name for e in self.source_exts] + if self.base_version: + names.insert(0, "Version %s"%self.base_version) + print "Sources %s"%", ".join(names) + + def write_header_intro(self, out): + out.write("#ifndef MSP_GL_%s_\n"%self.ext_name.upper()) + out.write("#define MSP_GL_%s_\n"%self.ext_name.upper()) + + out.write(""" #include #include @@ -449,24 +548,16 @@ namespace GL { """) -if funcs or enums: - if funcs: - for f in funcs: - out.write("typedef %s (APIENTRY *%s)(%s);\n"%(f.return_type, f.typedef, ", ".join(f.params))) - out.write("\n") - - if enums: - api_prefix = "GL" - if target_api=="gles2": - api_prefix = "GL_ES" - + def write_enum_definitions(self, out): enums_by_category = {} - for e in enums: + for e in self.enums: cat = None - if e.version: - cat = api_prefix+"_VERSION_"+"_".join(map(str, e.version)) - elif e.extension: - cat = "GL_"+e.extension.name + supp = e.api_support.get(self.host_api.name) + if supp: + if supp.core_version: + cat = "%s_%s"%(self.api_prefix, supp.core_version.as_define()) + elif supp.extensions: + cat = "GL_"+supp.extensions[0].name enums_by_category.setdefault(cat, []).append(e) for cat in sorted(enums_by_category.keys()): @@ -478,23 +569,25 @@ if funcs or enums: out.write("#endif\n") out.write("\n") - for f in funcs: - out.write("extern %s %s;\n"%(f.typedef, f.name)) - -out.write("extern Extension %s;\n"%target_ext.name) + def write_function_pointer_declarations(self, out): + for f in self.funcs: + typedef = self.func_typedefs[f.name] + out.write("typedef %s (APIENTRY *%s)(%s);\n"%(f.return_type, typedef, ", ".join(f.params))) + out.write("extern %s %s;\n"%(typedef, f.name)) + out.write("\n") -out.write(""" + def write_header_outro(self, out): + out.write(""" } // namespace GL } // namespace Msp #endif """) -out = file(out_base+".cpp", "w") -out.write("#include \"%s.h\"\n"%target_ext.name.lower()) - -if funcs: - out.write(""" + def write_source_intro(self, out): + out.write("#include \"%s.h\"\n"%self.ext_name.lower()) + if self.funcs: + out.write(""" #ifdef __APPLE__ #define GET_PROC_ADDRESS(x) &::x #else @@ -507,63 +600,267 @@ if funcs: #define GET_PROC_ADDRESS_1_1(x) GET_PROC_ADDRESS(x) #endif """) -out.write(""" + out.write(""" namespace Msp { namespace GL { """) -for f in funcs: - out.write("%s %s = 0;\n"%(f.typedef, f.name)) - -out.write("\nExtension::SupportLevel init_%s()\n{\n"%target_ext.name.lower()) -if core_version: - out.write("\tif(is_disabled(\"GL_%s\"))\n\t\treturn Extension::UNSUPPORTED;\n"%target_ext.name) - out.write("#if !defined(__APPLE__) || defined(GL_VERSION_%d_%d)\n"%tuple(core_version)) - out.write("\tif(") - if backport_ext: - out.write("is_supported(\"GL_%s\") || "%backport_ext.name) - out.write("is_supported(Version(%d, %d)"%tuple(core_version)) - if deprecated_version: - out.write(", Version(%d, %d)"%tuple(deprecated_version)) - out.write("))\n\t{\n") - for f in funcs: - if target_api in f.supported_apis: - gpa_suffix = "" - if f.version is not None and f.version<=[1, 1]: - gpa_suffix = "_1_1" - out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS%s(%s));\n"%(f.name, f.typedef, gpa_suffix, f.name)) - out.write("\t\treturn Extension::CORE;\n") - out.write("\t}\n") - out.write("#endif\n") -if source_ext and source_ext!=backport_ext: - out.write("#if !defined(__APPLE__) || defined(GL_%s)\n"%target_ext.name) - out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(source_ext.name)) - for f in funcs: - if f.sources: - src = None - for s in f.sources: - if s.name.endswith(source_ext.ext_type): - src = s - break - if not src: - src = f.sources[0] + def write_function_pointer_definitions(self, out): + for f in self.funcs: + out.write("%s %s = 0;\n"%(self.func_typedefs[f.name], f.name)) + + def write_init_function(self, out): + out.write("\nExtension::SupportLevel init_%s()\n{\n"%self.ext_name.lower()) + if self.core_version: + out.write("\tif(is_disabled(\"GL_%s\"))\n\t\treturn Extension::UNSUPPORTED;\n"%self.ext_name) + out.write("#if !defined(__APPLE__) || defined(%s_%s)\n"%(self.api_prefix, self.core_version.as_define())) + out.write("\tif(") + if self.backport_ext: + out.write("is_supported(\"GL_%s\") || "%self.backport_ext.name) + out.write("is_supported(%r"%self.core_version) + if self.deprecated_version: + out.write(", %r"%self.deprecated_version) + out.write("))\n\t{\n") + for f in self.funcs: + supp = f.api_support.get(self.host_api.name) + if supp: + gpa_suffix = "" + if supp.core_version is not None and supp.core_version<=Version(1, 1): + gpa_suffix = "_1_1" + out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS%s(%s));\n"%(f.name, self.func_typedefs[f.name], gpa_suffix, f.name)) + out.write("\t\treturn Extension::CORE;\n") + out.write("\t}\n") + out.write("#endif\n") + if self.source_exts: + out.write("#if !defined(__APPLE__) || defined(GL_%s)\n"%self.ext_name) + out.write("\tif(") + if self.base_version: + out.write("is_supported(%r) && "%self.base_version) + out.write("%s)\n\t{\n"%" && ".join("is_supported(\"GL_%s\")"%s.name for s in self.source_exts)) + for f in self.funcs: + supp = f.api_support.get(self.host_api.name) + src = None + for e in self.source_exts: + if f in e.things: + src = f + elif supp: + for s in supp.sources: + if s.name in e.things: + src = s + break + if src: + break + if not src and supp and supp.core_version and self.base_version>=supp.core_version: + sec = f + + if src: + out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, self.func_typedefs[f.name], src.name)) + out.write("\t\treturn Extension::EXTENSION;\n") + out.write("\t}\n") + out.write("#endif\n") + out.write("\treturn Extension::UNSUPPORTED;\n") + out.write("}\n") + + def write_source_outro(self, out): + out.write(""" +} // namespace GL +} // namespace Msp +""") + + def write_header(self, fn): + out = file(fn, "w") + self.write_header_intro(out) + self.write_enum_definitions(out) + self.write_function_pointer_declarations(out) + out.write("extern Extension %s;\n"%self.ext_name) + self.write_header_outro(out) + + def write_source(self, fn): + out = file(fn, "w") + self.write_source_intro(out) + self.write_function_pointer_definitions(out) + self.write_init_function(out) + out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(self.ext_name, self.ext_name, self.ext_name.lower())) + self.write_source_outro(out) + + +def dump_api_support(supp, api, indent): + if supp.core_version: + print indent+"core in version "+str(supp.core_version) + if supp.deprecated_version: + print indent+"deprecated in version "+str(supp.deprecated_version) + for e in supp.extensions: + print indent+"extension %s (preference %d)"%(e.name, e.preference) + for r in supp.sources: + print indent+"source "+r.name + dump_thing_info(r, api, indent+" ") + +def dump_thing_info(thing, api, indent): + for a in thing.aliases: + print indent+"alias "+a + if api: + supp = thing.api_support.get(api) + dump_api_support(supp, api, indent) + else: + for a, s in thing.api_support.iteritems(): + print indent+"api "+a + dump_api_support(s, a, indent+" ") + + +class ExtensionParser: + def __init__(self, host_api): + self.host_api = host_api + self.target_ext = None + self.core_version = None + self.deprecated_version = None + self.backport_ext = None + self.source_exts = [] + self.ignore_things = [] + self.optional_things = [] + + def parse(self, fn): + for line in open(fn): + line = line.strip() + if not line or line.startswith("#"): + continue + + parts = line.split() + api = None + keyword = parts[0] + if ":" in keyword: + api, keyword = keyword.split(":") + + if api is not None and api!=self.host_api: + continue + + if keyword=="extension": + self.target_ext = parts[1] + elif keyword=="core_version": + self.core_version = Version(*map(int, parts[1].split('.'))) + elif keyword=="deprecated": + self.deprecated_version = Version(*map(int, parts[1].split('.'))) + elif keyword=="backport": + self.backport_ext = parts[1] + elif keyword=="source": + self.source_exts.append(parts[1]) + elif keyword=="ignore": + self.ignore_things.append(parts[1]) + elif keyword=="optional": + self.optional_things.append(parts[1]) + else: + print "Unknown keyword "+keyword + return False + + return True + + +def get_extension(api_map, ext_name): + if "." in ext_name: + ext_api_name, ext_name = ext_name.split(".") + else: + ext_api_name = "gl" + + return api_map[ext_api_name].extensions[ext_name] + +def resolve_things(api, things): + rthings = [] + for t in things: + ct = filter(None, map(api.core_things.get, t.aliases)) + if ct: + rthings += ct else: - src = f + rthings.append(t) - if target_api in src.supported_apis: - if not src.name.endswith(source_ext.ext_type): - print "Warning: %s does not match extension type %s"%(src.name, source_ext.ext_type) - out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, f.typedef, src.name)) - out.write("\t\treturn Extension::EXTENSION;\n") - out.write("\t}\n") - out.write("#endif\n") -out.write("\treturn Extension::UNSUPPORTED;\n") -out.write("}\n") + return rthings -out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(target_ext.name, target_ext.name, target_ext.name.lower())) +def collect_extension_things(host_api, target_ext, ignore): + ext_things = [t for n, t in target_ext.things.iteritems() if n not in ignore] + return resolve_things(target_ext.api, ext_things) -out.write(""" -} // namespace GL -} // namespace Msp -""") +def collect_optional_things(target_ext, names): + things = [] + for t in names: + if t in target_ext.things: + things.append(target_ext.things[t]) + else: + things.append(target_ext.api.core_things[t]) + return resolve_things(target_ext.api, things) + +def main(): + if len(sys.argv)<2: + print """Usage: + extgen.py [api] [] + +Reads gl.xml and generates C++ source files to use an OpenGL extension +described in . If is absent, the extension's lowercased +name is used. Anything after the last dot in is removed and +replaced with cpp and h.""" + sys.exit(1) + + i = 1 + + debug = False + if sys.argv[i]=="-g": + debug = True + i += 1 + + host_api_name = "gl" + if sys.argv[i].startswith("gl"): + host_api_name = sys.argv[i] + i += 1 + + ext_parser = ExtensionParser(host_api_name) + if not ext_parser.parse(sys.argv[i]): + sys.exit(1) + i += 1 + + if i