X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=scripts%2Fextgen.py;h=f5df83f21588ab0cbb71f94d8a94a8d05023af7a;hb=137b642f43f175a98a45b95de5b0735c5c487a2e;hp=8d7bfcb1ec1e91c663b33b8aaeec2115b340e05f;hpb=ab2cda34b99f122a647c1457e8cb0b46715802af;p=libs%2Fgl.git diff --git a/scripts/extgen.py b/scripts/extgen.py index 8d7bfcb1..f5df83f2 100755 --- a/scripts/extgen.py +++ b/scripts/extgen.py @@ -5,116 +5,109 @@ 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 -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+11: + candidates.sort(reverse=True) + if candidates[1][0]+1>=candidates[0][0]: + print "Warning: multiple likely core version candidates: %s %s"%(candidates[0][1], candidates[1][1]) + return candidates[0][1] + +def detect_deprecated_version(host_api, things): + min_version = None + for t in things: + supp = t.api_support.get(host_api.name) + if supp and supp.deprecated_version: + if min_version is None: + min_version = supp.deprecated_version + else: + min_version = min(min_version, supp.deprecated_version) + else: + return None -# 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) + return min_version -def get_key_api(things): - common_apis = set(target_ext.supported_apis) +def detect_backport_extension(host_api, target_ext, things): + candidates = [] 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) - else: - alias.sources.append(t) + supp = t.api_support.get(host_api.name) + if supp and supp.core_version: + for e in supp.extensions: + if e.backport and e not in candidates: + candidates.append(e) -# 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 + if len(candidates)>1: + print "Warning: multiple backport extension candidates: %s"%(" ".join(e.name for e in candidates)) - # 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 + for e in candidates: + if e.base_name==target_ext.base_name: + return e - 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: - backport_ext = extensions[backport_ext] + if len(candidates)==1: + print "Warning: potential backport extension has mismatched name: %s"%candidates[0].name - if backport_ext not in backport_ext_candidates: - print "Warning: explicitly specified backport extension %s does not look like a backport extension" -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)) +def collect_extensions(thing, api, exts): + supp = thing.api_support.get(api) + if not supp: + return - for e in backport_ext_candidates: - if e.base_name==target_ext.base_name: - backport_ext = e + for e in supp.extensions: + if not e.backport and e not in exts: + exts.append(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 s in supp.sources: + collect_extensions(s, api, exts) -for f in funcs: - f.typedef = "FPtr_%s"%f.name +def detect_source_extension(host_api, target_ext, things): + if target_ext.name in host_api.extensions: + return target_ext -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]] - else: - source_ext = None - -if funcs or enums: - any_supported = False - all_supported = True - for t in itertools.chain(funcs, enums): - if target_api in t.supported_apis: - any_supported = True - else: - all_supported = False - - if not any_supported: - print "Warning: %s is not supported by the target API"%target_ext.name - elif not all_supported: - print "Warning: %s is only partially supported by the target API"%target_ext.name - unsupported = "" - label = "Warning: Unsupported tokens: " - for t in itertools.chain(funcs, enums): - if target_api not in t.supported_apis: - if unsupported and len(label)+len(unsupported)+2+len(t.name)>78: - 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(""" + things_by_ext = {} + for t in things: + exts = [] + collect_extensions(t, host_api.name, exts) + for e in exts: + things_by_ext.setdefault(e, []).append(t) + + largest_ext = None + largest_count = 0 + for e, t in things_by_ext.iteritems(): + count = len(t) + if count>largest_count: + largest_ext = e + largest_count = count + + return largest_ext + + +class SourceGenerator: + def __init__(self, host_api, target_ext, things): + self.host_api = host_api + self.api_prefix = "GL" + if self.host_api.name=="gles2": + self.api_prefix = "GL_ES" + self.target_ext = target_ext + self.funcs = filter((lambda t: t.kind==Thing.FUNCTION), 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), things) + self.enums.sort(key=(lambda e: e.value)) + self.core_version = detect_core_version(host_api, things) + self.deprecated_version = detect_deprecated_version(host_api, things) + self.backport_ext = detect_backport_extension(host_api, target_ext, things); + self.source_ext = detect_source_extension(host_api, target_ext, things) + + def write_header_intro(self, out): + out.write("#ifndef MSP_GL_%s_\n"%self.target_ext.name.upper()) + out.write("#define MSP_GL_%s_\n"%self.target_ext.name.upper()) + + out.write(""" #include #include @@ -448,24 +449,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()): @@ -477,23 +470,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.target_ext.name.lower()) + if self.funcs: + out.write(""" #ifdef __APPLE__ #define GET_PROC_ADDRESS(x) &::x #else @@ -506,59 +501,200 @@ 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("\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") -if source_ext and source_ext!=backport_ext: - 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] - else: - src = f - - 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("\treturn Extension::UNSUPPORTED;\n") -out.write("}\n") - -out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(target_ext.name, target_ext.name, target_ext.name.lower())) - -out.write(""" + 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.target_ext.name.lower()) + if self.core_version: + out.write("\tif(is_disabled(\"GL_%s\"))\n\t\treturn Extension::UNSUPPORTED;\n"%self.target_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_ext and self.source_ext!=self.backport_ext: + out.write("#if !defined(__APPLE__) || defined(GL_%s)\n"%self.target_ext.name) + out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(self.source_ext.name)) + for f in self.funcs: + supp = f.api_support.get(self.host_api.name) + if supp and supp.sources: + src = None + for s in supp.sources: + if s.name.endswith(self.source_ext.ext_type): + src = s + break + if not src: + src = supp.sources[0] + else: + src = f + + if self.host_api.name in src.api_support: + if not src.name.endswith(self.source_ext.ext_type): + print "Warning: %s does not match extension type %s"%(src.name, self.source_ext.ext_type) + 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.target_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) + ext_name = self.target_ext.name + out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(ext_name, ext_name, ext_name.lower())) + self.write_source_outro(out) + + +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.secondary_exts = [] + self.backport_ext = None + self.ignore_things = [] + + def parse(self, fn): + for line in open(fn): + line = line.strip() + if line.startswith("#"): + continue + + parts = line.split() + keyword = parts[0] + + if keyword=="extension": + self.target_ext = parts[1] + elif keyword=="core_version": + if parts[1]==self.host_api: + self.core_version = Version(*map(int, parts[2].split('.'))) + elif keyword=="deprecated": + if parts[1]==self.host_api: + self.deprecated_version = Version(*map(int, parts[2].split('.'))) + elif keyword=="secondary": + self.secondary_exts.append(parts[1]) + elif keyword=="backport": + self.backport_ext = parts[1] + elif keyword=="ignore": + self.ignore_things.append(parts[1]) + + +def get_extension(api_map, ext_name): + main_api = api_map["gl"] + ext = main_api.extensions.get(ext_name) + if ext: + return ext + + for a in api_map.itervalues(): + ext = a.extensions.get(ext_name) + if ext: + return ext + +def collect_things(host_api, target_ext, secondary, ignore): + ext_things = [t for n, t in target_ext.things.iteritems() if n not in ignore] + core_things = target_ext.api.core_things + + things = [] + for t in ext_things: + found_in_core = False + for a in t.aliases: + if a in core_things: + things.append(core_things[a]) + found_in_core = True + if not found_in_core: + things.append(t) + + for s in secondary: + for t in s.things.itervalues(): + for a in t.aliases: + if a in core_things and core_things[a] not in things: + things.append(core_things[a]) + + return 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(0) + + host_api_name = "gl" + + i = 1 + if sys.argv[i].startswith("gl"): + host_api_name = sys.argv[i] + i += 1 + + ext_parser = ExtensionParser(host_api_name) + ext_parser.parse(sys.argv[i]) + i += 1 + + if i