return self.major>other.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)
+
class Thing:
FUNCTION = 1
class GlXmlParser:
- def __init__(self, host_api_name, target_ext_name):
- self.host_api_name = host_api_name
- self.target_ext_name = target_ext_name
+ def __init__(self):
self.apis = {}
self.things = {}
enum.value = int(en.getAttribute("value"), 16)
+ alias = en.getAttribute("alias")
+ if alias:
+ enum.aliases.append(alias)
+
def parse_feature(self, feat):
api_name = feat.getAttribute("api")
- if not api_name:
- api_name = self.host_api_name
api = get_or_create(self.apis, api_name, Api)
version = feat.getAttribute("number")
removes = get_nested_elements(feat, "remove")
for rem in removes:
- profile = rem.getAttribute("profile")
commands = get_nested_elements(rem, "command")
enums = get_nested_elements(rem, "enum")
name = t.getAttribute("name")
thing = self.things.get(name)
if thing:
- if profile!="core":
- if thing.name in api.core_things:
- del api.core_things[thing.name]
- for s in thing.api_support.itervalues():
- for e in s.extensions:
- del e.things[thing.name]
- else:
- supp = thing.get_or_create_api_support(api.name)
- supp.deprecated_version = version
+ supp = thing.get_or_create_api_support(api.name)
+ supp.deprecated_version = version
def parse_extension(self, ext):
ext_things_by_api = {}
def check_backport_extensions(self, api):
for e in api.extensions.itervalues():
+ if e.ext_type!="ARB":
+ continue
+
e.backport = True
for t in e.things.itervalues():
if t.name.endswith(e.ext_type):
break
def resolve_enum_aliases(self, api):
- core_enums = filter((lambda t: t.kind==Thing.ENUM), api.core_things.itervalues())
- core_enums_by_value = dict((e.value, None) for e in core_enums)
-
for e in api.extensions.itervalues():
ext_enums = filter((lambda t: t.kind==Thing.ENUM), e.things.itervalues())
enum_suffix = "_"+e.ext_type
if name.endswith(enum_suffix):
name = name[:-len(enum_suffix)]
ce = api.core_things.get(name)
- if not ce and n.value in core_enums_by_value:
- if core_enums_by_value[n.value] is None:
- core_enums_by_value[n.value] = filter((lambda e: e.value==n.value), core_enums)
- for c in core_enums_by_value[n.value]:
- if c.bitmask==n.bitmask:
- ce = c
- break
if ce and ce.value==n.value and ce.name not in n.aliases:
n.aliases.append(ce.name)
sources.append(t)
def sort_extensions(self):
- for a in self.apis.itervalues():
- e = a.extensions.get(self.target_ext_name)
- if e:
- e.preference = 3
for t in self.things.itervalues():
for s in t.api_support.itervalues():
s.extensions.sort(key=(lambda e: e.preference), reverse=True)
self.sort_extensions()
-def detect_core_version(host_api, things):
- candidates = {}
+def detect_core_version(host_api, things, debug=None):
+ max_version = Version(1, 0)
+ max_count = 0
+ lower_count = 0
+ missing = []
for t in things:
supp = t.api_support.get(host_api.name)
if supp and supp.core_version:
- candidates[supp.core_version] = candidates.get(supp.core_version, 0)+1
+ if supp.core_version>max_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)
- if candidates:
- candidates = list((v, k) for k, v in candidates.items())
- if len(candidates)>1:
- 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]
+ if lower_count>max_count or (missing and len(missing)*2<lower_count+max_count):
+ print "Warning: Inconsistent core version %s"%max_version
-def detect_deprecated_version(host_api, things):
+ if missing:
+ if debug:
+ print "---"
+ print "%d things missing from core:"%len(missing)
+ for t in missing:
+ print " "+t.name
+ return None
+
+ return max_version
+
+def detect_deprecated_version(host_api, things, debug):
min_version = None
+ deprecated = []
for t in things:
supp = t.api_support.get(host_api.name)
if supp and supp.deprecated_version:
min_version = supp.deprecated_version
else:
min_version = min(min_version, supp.deprecated_version)
- else:
- return None
+ deprecated.append(t)
+
+ if min_version and len(deprecated)*2<len(things):
+ print "Warning: Inconsistent deprecation version %s"%min_version
+ if debug:
+ print "---"
+ print "%d things are deprecated:"%len(deprecated)
+ for t in deprecated:
+ print " "+t.name
return min_version
-def detect_backport_extension(host_api, target_ext, things):
+def detect_backport_extension(host_api, things):
candidates = []
for t in things:
supp = t.api_support.get(host_api.name)
if e.backport and e not in candidates:
candidates.append(e)
- if len(candidates)>1:
- print "Warning: multiple backport extension candidates: %s"%(" ".join(e.name for e in candidates))
-
+ total_count = len(things)
+ best_ext = None
+ best_count = 0
for e in candidates:
- if e.base_name==target_ext.base_name:
+ things_in_ext = filter((lambda t: t.name in e.things), things)
+ count = len(things_in_ext)
+ if count==total_count:
return e
+ elif count>best_count:
+ best_ext = e
+ best_count = count
- if len(candidates)==1:
- print "Warning: potential backport extension has mismatched name: %s"%candidates[0].name
+ 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)
return
for e in supp.extensions:
- if not e.backport and e not in exts:
+ if not e.backport and e.ext_type!="MSP" and e not in exts:
exts.append(e)
for s in supp.sources:
collect_extensions(s, api, exts)
-def detect_source_extension(host_api, target_ext, things):
- if target_ext.name in host_api.extensions:
- return target_ext
-
+def detect_source_extension(host_api, things, debug=False):
things_by_ext = {}
for t in things:
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
+ 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:
+ max_version = None
+
+ if max_version:
+ if not base_version or max_version<base_version:
+ base_version = max_version
+ keep_exts = len(extensions)
+ elif not base_version:
+ keep_exts = len(extensions)
+
+ recheck_base_version = False
+
+ if not missing or not things_by_ext:
+ break
+
+ 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
+ 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_exts<len(extensions):
+ print "Discarding %d extensions that do not improve base version"%(len(extensions)-keep_exts)
+ del extensions[keep_exts:]
+ return base_version, extensions
+ else:
+ 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, target_ext, things):
+ 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.target_ext = target_ext
- self.funcs = filter((lambda t: t.kind==Thing.FUNCTION), things)
+ 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), things)
+ 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)
- 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)
+ 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.target_ext.name.upper())
- out.write("#define MSP_GL_%s_\n"%self.target_ext.name.upper())
+ 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 <msp/gl/extension.h>
""")
def write_source_intro(self, out):
- out.write("#include \"%s.h\"\n"%self.target_ext.name.lower())
+ out.write("#include \"%s.h\"\n"%self.ext_name.lower())
if self.funcs:
out.write("""
#ifdef __APPLE__
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())
+ 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.target_ext.name)
+ 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("\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))
+ 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)
- 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)
+ src = None
+ for e in self.source_exts:
+ if f.name 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")
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)
+ out.write("extern Extension %s;\n"%self.ext_name)
self.write_header_outro(out)
def write_source(self, fn):
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()))
+ 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.secondary_exts = []
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 line.startswith("#"):
+ 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":
- if parts[1]==self.host_api:
- self.core_version = Version(*map(int, parts[2].split('.')))
+ self.core_version = Version(*map(int, parts[1].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])
+ 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):
- main_api = api_map["gl"]
- ext = main_api.extensions.get(ext_name)
- if ext:
- return ext
+ 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:
+ rthings.append(t)
- for a in api_map.itervalues():
- ext = a.extensions.get(ext_name)
- if ext:
- return ext
+ return rthings
-def collect_things(host_api, target_ext, secondary, ignore):
+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]
- core_things = target_ext.api.core_things
+ return resolve_things(target_ext.api, ext_things)
+def collect_optional_things(target_ext, names):
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
+ 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:
described in <extfile>. If <outfile> is absent, the extension's lowercased
name is used. Anything after the last dot in <outfile> is removed and
replaced with cpp and h."""
- sys.exit(0)
-
- host_api_name = "gl"
+ 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)
- ext_parser.parse(sys.argv[i])
+ if not ext_parser.parse(sys.argv[i]):
+ sys.exit(1)
i += 1
if i<len(sys.argv):
else:
out_base = ext_parser.target_ext.lower()
- xml_parser = GlXmlParser(host_api_name, ext_parser.target_ext)
+ xml_parser = GlXmlParser()
xml_parser.parse_file("gl.xml")
xml_parser.parse_file("gl.fixes.xml")
+ xml_parser.parse_file("gl.msp.xml")
xml_parser.finalize()
host_api = xml_parser.apis[host_api_name]
target_ext = get_extension(xml_parser.apis, ext_parser.target_ext)
- secondary_exts = [get_extension(xml_parser.apis, s) for s in ext_parser.secondary_exts]
- things = collect_things(host_api, target_ext, secondary_exts, ext_parser.ignore_things)
-
- generator = SourceGenerator(host_api, target_ext, things)
+ things = collect_extension_things(host_api, target_ext, ext_parser.ignore_things+ext_parser.optional_things)
+ optional_things = collect_optional_things(target_ext, ext_parser.optional_things)
+
+ if debug:
+ print "--- Things included in this extension ---"
+ all_things = things+optional_things
+ all_things.sort(key=(lambda t: t.name))
+ for t in all_things:
+ print t.name
+ if t in optional_things:
+ print " optional"
+ dump_thing_info(t, None, " ")
+
+ generator = SourceGenerator(host_api, target_ext.name, things, optional_things, debug)
if ext_parser.core_version:
generator.core_version = ext_parser.core_version
if ext_parser.deprecated_version:
generator.deprecated_version = ext_parser.deprecated_version
+ if ext_parser.backport_ext:
+ if ext_parser.backport_ext=="none":
+ generator.backport_ext = None
+ else:
+ generator.backport_ext = get_extension(xml_parser.apis, ext_parser.backport_ext)
+ if ext_parser.source_exts:
+ generator.base_version = None
+ if len(ext_parser.source_exts)==1 and ext_parser.source_exts[0]=="none":
+ generator.source_exts = []
+ else:
+ generator.source_exts = map((lambda e: get_extension(xml_parser.apis, e)), ext_parser.source_exts)
+ if debug:
+ generator.dump_info()
generator.write_header(out_base+".h")
generator.write_source(out_base+".cpp")