#!/usr/bin/python
import sys
+import os
+import xml.dom
+import xml.dom.minidom
+import itertools
+import re
-if len(sys.argv)<2:
- print """Usage:
- extgen.py <extension> [<version>] [<secondary> ...]"
-
-Reads gl.spec and generates files to use <extension>. Any promoted functions
-are exposed with their promoted names. If <secondary> extensions are given,
-any promoted functions from those are pulled in as well. <version> can be
-given to override the version where <extension> was promoted to core."""
- sys.exit(0)
-
-ext = sys.argv[1]
-exttype = ext.split('_')[0]
-secondary = sys.argv[2:]
-ver = None
-if secondary and secondary[0][0].isdigit():
- ver = secondary.pop(0)
-bp_ext = None
-
-class Function:
- def __init__(self, name):
+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)
+
+ def __str__(self):
+ return "%d.%d"%(self.major, self.minor)
+
+ def __repr__(self):
+ return "Version(%d, %d)"%(self.major, self.minor)
+
+ def as_define(self):
+ return "VERSION_%d_%d"%(self.major, self.minor)
+
+ def __lt__(self, other):
+ if other is None:
+ return False
+
+ if self.major!=other.major:
+ return self.major<other.major
+ return self.minor<other.minor
+
+ def __gt__(self, other):
+ if other is None:
+ return True
+
+ if self.major!=other.major:
+ 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
+ ENUM = 2
+
+ class ApiSupport:
+ def __init__(self):
+ self.core_version = None
+ self.deprecated_version = None
+ self.extensions = []
+ self.sources = []
+
+ def __init__(self, name, kind):
self.name = name
- self.version = None
- self.category = None
- self.vectorequiv = None
+ self.kind = kind
self.aliases = []
- self.extfunc = None
-
-funcs = {}
-cur_func = None
-
-def parse_file(fn):
- for line in open(fn):
- if line[0]=='#' or line.find(':')>=0:
- continue
- elif line[0]=='\t':
- if cur_func:
- parts = line.split()
- if parts[0]=="category":
- cur_func.category = parts[1]
- elif parts[0]=="vectorequiv":
- cur_func.vectorequiv = parts[1]
- elif parts[0]=="alias":
- cur_func.aliases.append(parts[1])
- elif parts[0]=="version":
- cur_func.version = parts[1]
- elif parts[0]=="delete":
- del funcs[cur_func.name]
- cur_func = None
+ self.api_support = {}
+
+ def get_or_create_api_support(self, api):
+ supp = self.api_support.get(api)
+ if not supp:
+ supp = Thing.ApiSupport()
+ self.api_support[api] = supp
+ return supp
+
+
+class Function(Thing):
+ def __init__(self, name):
+ Thing.__init__(self, name, Thing.FUNCTION)
+ self.return_type = "void"
+ self.params = []
+
+
+r_bitmask = re.compile("_BIT[0-9]*(_|$)")
+
+class Enum(Thing):
+ def __init__(self, name):
+ Thing.__init__(self, name, Thing.ENUM)
+ self.value = 0
+ self.bitmask = bool(r_bitmask.search(self.name))
+
+
+class Extension:
+ def __init__(self, name, api):
+ self.name = name
+ underscore = name.find('_')
+ self.ext_type = name[0:underscore]
+ self.base_name = name[underscore+1:]
+ self.api = api
+ self.things = {}
+ self.preference = 0
+ if self.ext_type=="EXT":
+ self.preference = 1
+ elif self.ext_type=="ARB" or self.ext_type=="OES":
+ self.preference = 2
+ self.backport = False
+
+
+class Api:
+ def __init__(self, name):
+ self.name = name
+ self.latest_version = None
+ self.core_things = {}
+ self.extensions = {}
+
+
+def get_nested_elements(elem, path):
+ childElements = [c for c in elem.childNodes if c.nodeType==xml.dom.Node.ELEMENT_NODE]
+ if '/' in path:
+ head, tail = path.split('/', 1)
+ result = []
+ for c in childElements:
+ if c.tagName==head:
+ result += get_nested_elements(c, tail)
+ return result
+ else:
+ return [c for c in childElements if c.tagName==path]
+
+def get_first_child(elem, tag):
+ for c in elem.childNodes:
+ if c.nodeType==xml.dom.Node.ELEMENT_NODE and c.tagName==tag:
+ return c
+ return None
+
+def get_text_contents(node):
+ result = ""
+ for c in node.childNodes:
+ if c.nodeType==xml.dom.Node.TEXT_NODE or c.nodeType==xml.dom.Node.CDATA_SECTION_NODE:
+ result += c.data
else:
- paren = line.find('(')
- if paren>0:
- name = line[:paren]
- if name in funcs:
- cur_func = funcs[name]
- else:
- cur_func = Function(name)
- funcs[name] = cur_func
-
-parse_file("gl.spec")
-parse_file("gl.spec.fixes")
-
-for f in funcs.itervalues():
- if f.category==ext or f.category in secondary:
- if not f.aliases and f.vectorequiv:
- for g in funcs.itervalues():
- if g!=f and g.vectorequiv==f.vectorequiv and f.name.startswith(g.name):
- f.aliases.append(g.name)
+ result += get_text_contents(c)
+ return result
+
+def get_or_create(map, name, type, *args):
+ obj = map.get(name)
+ if not obj:
+ obj = type(name, *args)
+ map[name] = obj
+ return obj
+
+
+class GlXmlParser:
+ def __init__(self):
+ self.apis = {}
+ self.things = {}
+
+ def parse_command(self, cmd):
+ proto = get_first_child(cmd, "proto")
+ name = get_text_contents(get_first_child(proto, "name"))
+ func = get_or_create(self.things, name, Function)
+
+ aliases = get_nested_elements(cmd, "alias")
+ func.aliases = [a.getAttribute("name") for a in aliases]
+
+ ptype = get_first_child(proto, "ptype")
+ if ptype:
+ func.return_type = get_text_contents(ptype)
+ else:
+ for c in proto.childNodes:
+ if c.nodeType==xml.dom.Node.TEXT_NODE and c.data.strip():
+ func.return_type = c.data.strip()
break
- for a in f.aliases:
- if a in funcs:
- funcs[a].extfunc = f
+ params = get_nested_elements(cmd, "param")
+ func.params = map(get_text_contents, params)
-def is_relevant(f):
- if f.category==ext and not f.aliases:
- return True
- if f.extfunc:
- e = f.extfunc
- if e.category==ext or e.category in secondary:
- return True
- return False
+ def parse_enum(self, en):
+ name = en.getAttribute("name")
+ enum = get_or_create(self.things, name, Enum)
+
+ 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")
+ api = get_or_create(self.apis, api_name, Api)
+
+ version = feat.getAttribute("number")
+ if version:
+ version = Version(*map(int, version.split('.')))
+ else:
+ version = None
+
+ requires = get_nested_elements(feat, "require")
+ for req in requires:
+ commands = get_nested_elements(req, "command")
+ enums = get_nested_elements(req, "enum")
+ for t in itertools.chain(commands, enums):
+ name = t.getAttribute("name")
+ thing = self.things.get(name)
+ if thing:
+ supp = thing.get_or_create_api_support(api.name)
+ if not supp.core_version or version<supp.core_version:
+ supp.core_version = version
+ api.core_things[thing.name] = thing
+
+ removes = get_nested_elements(feat, "remove")
+ for rem in removes:
+ commands = get_nested_elements(rem, "command")
+ enums = get_nested_elements(rem, "enum")
+
+ for t in itertools.chain(commands, enums):
+ name = t.getAttribute("name")
+ thing = self.things.get(name)
+ if thing:
+ supp = thing.get_or_create_api_support(api.name)
+ supp.deprecated_version = version
+
+ def parse_extension(self, ext):
+ ext_things_by_api = {}
+ requires = get_nested_elements(ext, "require")
+ for req in requires:
+ api = req.getAttribute("api")
+ ext_things = ext_things_by_api.setdefault(api, [])
+
+ commands = get_nested_elements(req, "command")
+ enums = get_nested_elements(req, "enum")
+ for t in itertools.chain(commands, enums):
+ name = t.getAttribute("name")
+ thing = self.things.get(name)
+ if thing:
+ ext_things.append(thing)
-funcs = [f for f in funcs.itervalues() if is_relevant(f)]
-funcs.sort(key=(lambda f: f.name))
+ ext_name = ext.getAttribute("name")
+ if ext_name.startswith("GL_"):
+ ext_name = ext_name[3:]
+
+ common_things = ext_things_by_api.get("", [])
+ supported = ext.getAttribute("supported").split('|')
+ for s in supported:
+ api = self.apis.get(s)
+ if not api:
+ continue
+
+ ext = get_or_create(api.extensions, ext_name, Extension, api)
+ api_things = ext_things_by_api.get(s, [])
+ for t in itertools.chain(common_things, api_things):
+ ext.things[t.name] = t
+ t.get_or_create_api_support(api.name).extensions.append(ext)
+
+ def parse_file(self, fn):
+ doc = xml.dom.minidom.parse(fn)
+ root = doc.documentElement
+
+ commands = get_nested_elements(root, "commands/command")
+ for cmd in commands:
+ self.parse_command(cmd)
+
+ enums = get_nested_elements(root, "enums/enum")
+ for en in enums:
+ self.parse_enum(en)
+
+ features = get_nested_elements(root, "feature")
+ for feat in features:
+ self.parse_feature(feat)
+
+ extensions = get_nested_elements(root, "extensions/extension")
+ for ext in extensions:
+ self.parse_extension(ext)
+
+ 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):
+ e.backport = False
+ break
+
+ def resolve_enum_aliases(self, api):
+ for e in api.extensions.itervalues():
+ ext_enums = filter((lambda t: t.kind==Thing.ENUM), e.things.itervalues())
+ enum_suffix = "_"+e.ext_type
+ for n in ext_enums:
+ if n.api_support[api.name].core_version:
+ continue
+
+ name = n.name
+ if name.endswith(enum_suffix):
+ name = name[:-len(enum_suffix)]
+ ce = api.core_things.get(name)
+ 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 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
+ # our use
+ alias = api.core_things.get(a)
+ if alias:
+ sources = alias.api_support[api.name].sources
+ if t not in sources:
+ sources.append(t)
+
+ def sort_extensions(self):
+ for t in self.things.itervalues():
+ for s in t.api_support.itervalues():
+ s.extensions.sort(key=(lambda e: e.preference), reverse=True)
+
+ def finalize(self):
+ for a in self.apis.itervalues():
+ self.check_backport_extensions(a)
+ self.resolve_enum_aliases(a)
+ self.resolve_sources(a)
+ self.sort_extensions()
+
+
+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:
+ 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)
-for f in funcs:
- if f.extfunc:
- if not ver:
- ver = f.version
- if not f.category.startswith("VERSION_"):
- bp_ext = f.category
- if not f.extfunc and not f.name.endswith(exttype):
- bp_ext = f.category
+ if lower_count>max_count or (missing and len(missing)*2<lower_count+max_count):
+ print "Warning: Inconsistent core version %s"%max_version
-if ver:
- ver = map(int, ver.split('.'))
+ if missing:
+ if debug:
+ print "---"
+ print "%d things missing from core:"%len(missing)
+ for t in missing:
+ print " "+t.name
+ return None
-out = file(ext.lower()+".h", "w")
-out.write("#ifndef MSP_GL_%s_\n"%ext.upper())
-out.write("#define MSP_GL_%s_\n"%ext.upper())
+ return max_version
-out.write("""
-#include "extension.h"
-#include "gl.h"
-#include <GL/glext.h>
+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:
+ if min_version is None:
+ min_version = supp.deprecated_version
+ else:
+ min_version = min(min_version, supp.deprecated_version)
+ 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, things):
+ candidates = []
+ for t in things:
+ 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)
+
+ total_count = len(things)
+ best_ext = None
+ best_count = 0
+ for e in candidates:
+ 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 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
+
+ for e in supp.extensions:
+ 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, things, debug=False):
+ 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)
+
+ 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, 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 <msp/gl/extension.h>
+#include <msp/gl/gl.h>
namespace Msp {
namespace GL {
+
""")
-if funcs:
- out.write("\n")
-for f in funcs:
- out.write("extern PFNGL%sPROC gl%s;\n"%(f.name.upper(), f.name))
+ def write_enum_definitions(self, out):
+ enums_by_category = {}
+ for e in self.enums:
+ cat = None
+ 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()):
+ if cat:
+ out.write("#ifndef %s\n"%cat)
+ for e in enums_by_category[cat]:
+ out.write("#define %s 0x%04X\n"%(e.name, e.value))
+ if cat:
+ out.write("#endif\n")
+ out.write("\n")
-out.write("\nextern Extension %s;\n"%ext)
+ 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(ext.lower()+".cpp", "w")
-out.write("#include \"%s.h\"\n"%ext.lower())
+ 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
+#define GET_PROC_ADDRESS(x) get_proc_address(#x)
+#endif
-out.write("""
+#ifdef _WIN32
+#define GET_PROC_ADDRESS_1_1(x) &::x
+#else
+#define GET_PROC_ADDRESS_1_1(x) GET_PROC_ADDRESS(x)
+#endif
+""")
+ out.write("""
namespace Msp {
namespace GL {
+
""")
-if funcs:
- out.write("\n")
-for f in funcs:
- out.write("PFNGL%sPROC gl%s = 0;\n"%(f.name.upper(), f.name))
-
-out.write("\nExtension::SupportLevel init_%s()\n{\n"%ext.lower())
-if ver:
- out.write("\tif(is_version_at_least(%d, %d)"%tuple(ver))
- if bp_ext:
- out.write(" || is_supported(\"GL_%s\")"%bp_ext)
- out.write(")\n\t{\n")
- for f in funcs:
- out.write("\t\tgl%s = reinterpret_cast<PFNGL%sPROC>(get_proc_address(\"gl%s\"));\n"%(f.name, f.name.upper(), f.name))
- out.write("\t\treturn Extension::CORE;\n")
- out.write("\t}\n")
-if ext!=bp_ext:
- out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(ext))
- for f in funcs:
- n = f.name
- if f.extfunc:
- n = f.extfunc.name
- out.write("\t\tgl%s = reinterpret_cast<PFNGL%sPROC>(get_proc_address(\"gl%s\"));\n"%(f.name, f.name.upper(), n))
- 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"%(ext, ext, ext.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.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.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")
+ 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:
+ rthings.append(t)
+
+ return rthings
+
+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)
+
+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] <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(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<len(sys.argv):
+ out_base = os.path.splitext(sys.argv[i])[0]
+ else:
+ out_base = ext_parser.target_ext.lower()
+
+ 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)
+ 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")
+
+if __name__=="__main__":
+ main()