]> git.tdb.fi Git - libs/gl.git/blobdiff - scripts/extgen.py
Check the flat qualifier from the correct member
[libs/gl.git] / scripts / extgen.py
index f5df83f21588ab0cbb71f94d8a94a8d05023af7a..9c859c3336aabcf7936682a7fa71d1a3b518d557 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 
 import sys
 import os
@@ -16,16 +16,16 @@ class Version:
                        self.major = args[0]
                        self.minor = args[1]
                else:
-                       raise TypeError, "__init__() takes zero or two arguments (%d given)"%len(args)
+                       raise TypeError("__init__() takes zero or two arguments ({} given)".format(len(args)))
 
        def __str__(self):
-               return "%d.%d"%(self.major, self.minor)
+               return "{}.{}".format(self.major, self.minor)
 
        def __repr__(self):
-               return "Version(%d, %d)"%(self.major, self.minor)
+               return "Version({}, {})".format(self.major, self.minor)
 
        def as_define(self):
-               return "VERSION_%d_%d"%(self.major, self.minor)
+               return "VERSION_{}_{}".format(self.major, self.minor)
 
        def __lt__(self, other):
                if other is None:
@@ -43,6 +43,18 @@ class Version:
                        return self.major>other.major
                return self.minor>other.minor
 
+       def __le__(self, other):
+               return not self.__gt__(other)
+
+       def __ge__(self, other):
+               return not self.__lt__(other)
+
+       def __eq__(self, other):
+               if other is None:
+                       return False
+
+               return (self.major==other.major and self.minor==other.minor)
+
 
 class Thing:
        FUNCTION = 1
@@ -145,9 +157,7 @@ def get_or_create(map, name, type, *args):
 
 
 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 = {}
 
@@ -177,10 +187,12 @@ class GlXmlParser:
 
                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")
@@ -204,7 +216,6 @@ class GlXmlParser:
 
                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")
 
@@ -212,15 +223,8 @@ class GlXmlParser:
                                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 = {}
@@ -275,21 +279,20 @@ class GlXmlParser:
                        self.parse_extension(ext)
 
        def check_backport_extensions(self, api):
-               for e in api.extensions.itervalues():
+               for e in api.extensions.values():
+                       if e.ext_type!="ARB":
+                               continue
+
                        e.backport = True
-                       for t in e.things.itervalues():
+                       for t in e.things.values():
                                if t.name.endswith(e.ext_type):
                                        e.backport = False
                                        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())
+               for e in api.extensions.values():
                        enum_suffix = "_"+e.ext_type
-                       for n in ext_enums:
+                       for n in (t for t in e.things.values() if t.kind==Thing.ENUM):
                                if n.api_support[api.name].core_version:
                                        continue
 
@@ -297,19 +300,12 @@ class GlXmlParser:
                                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)
 
        def resolve_sources(self, api):
-               for e in api.extensions.itervalues():
-                       for t in e.things.itervalues():
+               for e in api.extensions.values():
+                       for t in e.things.values():
                                for a in t.aliases:
                                        # There are a few cases where a vendor function is aliased to
                                        # an EXT or ARB function but those are rare and not relevant for
@@ -321,39 +317,53 @@ class GlXmlParser:
                                                        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():
+               for t in self.things.values():
+                       for s in t.api_support.values():
                                s.extensions.sort(key=(lambda e: e.preference), reverse=True)
 
        def finalize(self):
-               for a in self.apis.itervalues():
+               for a in self.apis.values():
                        self.check_backport_extensions(a)
                        self.resolve_enum_aliases(a)
                        self.resolve_sources(a)
                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 {}".format(max_version))
 
-def detect_deprecated_version(host_api, things):
+       if missing:
+               if debug:
+                       print("---")
+                       print("{} things missing from core:".format(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:
@@ -361,12 +371,19 @@ def detect_deprecated_version(host_api, things):
                                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 {}".format(min_version))
+               if debug:
+                       print("---")
+                       print("{} things are deprecated:".format(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)
@@ -375,15 +392,19 @@ def detect_backport_extension(host_api, target_ext, things):
                                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:
+               count = sum(t.name in e.things for t in things)
+               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 total_count and best_count*2>=total_count:
+               print("Warning: Inconsistent backport extension {}".format(best_ext.name))
 
 def collect_extensions(thing, api, exts):
        supp = thing.api_support.get(api)
@@ -391,16 +412,13 @@ def collect_extensions(thing, api, exts):
                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 = []
@@ -408,37 +426,122 @@ def detect_source_extension(host_api, target_ext, things):
                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 {} things in {} extensions".format(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.items():
+                       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 {} things in {}".format(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 list(things_by_ext.keys()):
+                       unseen = [t for t in things_by_ext[e] if t in missing]
+                       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 {}".format(base_version))
+                       if keep_exts<len(extensions):
+                               print("Discarding {} extensions that do not improve base version".format(len(extensions)-keep_exts))
+               del extensions[keep_exts:]
+               return base_version, extensions
+       else:
+               if debug:
+                       print("{} things still missing:".format(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 = [t for t in all_things if t.kind==Thing.FUNCTION]
                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 = [t for t in all_things if t.kind==Thing.ENUM]
                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 {}".format(self.ext_name))
+               print("Core {}".format(self.core_version))
+               print("Deprecated {}".format(self.deprecated_version))
+               if self.backport_ext:
+                       print("Backport {}".format(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 {}".format(self.base_version))
+                       print("Sources {}".format(", ".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_{}_\n".format(self.ext_name.upper()))
+               out.write("#define MSP_GL_{}_\n".format(self.ext_name.upper()))
 
                out.write("""
 #include <msp/gl/extension.h>
@@ -456,16 +559,16 @@ namespace GL {
                        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())
+                                       cat = "{}_{}".format(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()):
                        if cat:
-                               out.write("#ifndef %s\n"%cat)
+                               out.write("#ifndef {}\n".format(cat))
                        for e in enums_by_category[cat]:
-                               out.write("#define %s 0x%04X\n"%(e.name, e.value))
+                               out.write("#define {} 0x{:04X}\n".format(e.name, e.value))
                        if cat:
                                out.write("#endif\n")
                        out.write("\n")
@@ -473,8 +576,8 @@ namespace GL {
        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("typedef {} (APIENTRY *{})({});\n".format(f.return_type, typedef, ", ".join(f.params)))
+                       out.write("extern {} {};\n".format(typedef, f.name))
                        out.write("\n")
 
        def write_header_outro(self, out):
@@ -486,7 +589,7 @@ namespace GL {
 """)
 
        def write_source_intro(self, out):
-               out.write("#include \"%s.h\"\n"%self.target_ext.name.lower())
+               out.write("#include \"{}.h\"\n".format(self.ext_name.lower()))
                if self.funcs:
                        out.write("""
 #ifdef __APPLE__
@@ -509,19 +612,19 @@ namespace GL {
 
        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))
+                       out.write("{} {} = 0;\n".format(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_{}()\n{{\n".format(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("#if !defined(__APPLE__) || defined(%s_%s)\n"%(self.api_prefix, self.core_version.as_define()))
+                       out.write("\tif(is_disabled(\"GL_{}\"))\n\t\treturn Extension::UNSUPPORTED;\n".format(self.ext_name))
+                       out.write("#if !defined(__APPLE__) || defined({}_{})\n".format(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)
+                               out.write("is_supported(\"GL_{}\") || ".format(self.backport_ext.name))
+                       out.write("is_supported({!r}".format(self.core_version))
                        if self.deprecated_version:
-                               out.write(", %r"%self.deprecated_version)
+                               out.write(", {!r}".format(self.deprecated_version))
                        out.write("))\n\t{\n")
                        for f in self.funcs:
                                supp = f.api_support.get(self.host_api.name)
@@ -529,30 +632,34 @@ namespace GL {
                                        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\t{} = reinterpret_cast<{}>(GET_PROC_ADDRESS{}({}));\n".format(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))
+               if self.source_exts:
+                       out.write("#if !defined(__APPLE__) || defined(GL_{})\n".format(self.ext_name))
+                       out.write("\tif(")
+                       if self.base_version:
+                               out.write("is_supported({!r}) && ".format(self.base_version))
+                       out.write("{})\n\t{{\n".format(" && ".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)
-                                       out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, self.func_typedefs[f.name], src.name))
+                               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{} = reinterpret_cast<{}>(GET_PROC_ADDRESS({}));\n".format(f.name, self.func_typedefs[f.name], src.name))
                        out.write("\t\treturn Extension::EXTENSION;\n")
                        out.write("\t}\n")
                        out.write("#endif\n")
@@ -566,111 +673,150 @@ namespace GL {
 """)
 
        def write_header(self, fn):
-               out = file(fn, "w")
+               out = open(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)
+               out.write("extern Extension {};\n".format(self.ext_name))
                self.write_header_outro(out)
 
        def write_source(self, fn):
-               out = file(fn, "w")
+               out = open(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()))
+               out.write("\nExtension {}(\"GL_{}\", init_{});\n".format(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 {}".format(supp.core_version))
+       if supp.deprecated_version:
+               print(indent+"deprecated in version {}".format(supp.deprecated_version))
+       for e in supp.extensions:
+               print(indent+"extension {} (preference {})".format(e.name, e.preference))
+       for r in supp.sources:
+               print(indent+"source {}".format(r.name))
+               dump_thing_info(r, api, indent+"  ")
+
+def dump_thing_info(thing, api, indent):
+       for a in thing.aliases:
+               print(indent+"alias {}".format(a))
+       if api:
+               supp = thing.api_support.get(api)
+               dump_api_support(supp, api, indent)
+       else:
+               for a, s in thing.api_support.items():
+                       print(indent+"api {}".format(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 = [api.core_things[a] for a in t.aliases if a in api.core_things]
+               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):
-       ext_things = [t for n, t in target_ext.things.iteritems() if n not in ignore]
-       core_things = target_ext.api.core_things
+def collect_extension_things(host_api, target_ext, ignore):
+       ext_things = [t for n, t in target_ext.things.items() if n not in ignore]
+       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:
-               print """Usage:
+               print("""Usage:
   extgen.py [api] <extfile> [<outfile>]
 
 Reads gl.xml and generates C++ source files to use an OpenGL extension
 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"
+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)
-       ext_parser.parse(sys.argv[i])
+       if not ext_parser.parse(sys.argv[i]):
+               sys.exit(1)
        i += 1
 
        if i<len(sys.argv):
@@ -678,21 +824,45 @@ replaced with cpp and h."""
        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")