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] <extension> [<core_version>] [<secondary> ...]
- extgen.py [api] <extfile> [<outfile>]
+ def __repr__(self):
+ return "Version(%d, %d)"%(self.major, self.minor)
-Reads gl.xml 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. <core_version> can
-be given to override the version where <extension> 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 <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)
+ def __lt__(self, other):
+ if other is None:
+ return False
-target_api = "gl"
+ if self.major!=other.major:
+ return self.major<other.major
+ return self.minor<other.minor
-i = 1
-if sys.argv[i].startswith("gl"):
- target_api = sys.argv[i]
- i += 1
+ 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
-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+1<len(sys.argv):
- out_base = os.path.splitext(sys.argv[i+1])[0]
-else:
- secondary = sys.argv[i+1:]
- core_version = None
- if secondary and secondary[0][0].isdigit():
- core_version = secondary.pop(0)
-
-ext_type = target_ext.split('_')[0]
-
-if core_version:
- core_version = map(int, core_version.split('.'))
-
-if deprecated_version:
- deprecated_version = map(int, deprecated_version.split('.'))
-
-if not out_base:
- out_base = target_ext.lower()
-
-### XML file parsing ###
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.kind = kind
- self.version = None
- self.deprecated_version = None
- self.extension = None
- self.supported_apis = {}
- self.deprecated = {}
self.aliases = []
- self.sources = []
+ 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 = []
- self.typedef = None
+
+
+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 = (name.endswith("_BIT") or "_BIT_" in name)
+ self.bitmask = bool(r_bitmask.search(self.name))
+
class Extension:
- def __init__(self, name):
+ def __init__(self, name, api):
self.name = name
- self.supported_apis = []
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 = {}
-extensions = {}
-things = {}
def get_nested_elements(elem, path):
childElements = [c for c in elem.childNodes if c.nodeType==xml.dom.Node.ELEMENT_NODE]
result += get_text_contents(c)
return result
-def parse_command(cmd):
- proto = get_first_child(cmd, "proto")
- name = get_text_contents(get_first_child(proto, "name"))
- func = things.get(name)
- if not func:
- func = Function(name)
- things[name] = func
+def get_or_create(map, name, type, *args):
+ obj = map.get(name)
+ if not obj:
+ obj = type(name, *args)
+ map[name] = obj
+ return obj
- 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
-
- params = get_nested_elements(cmd, "param")
- func.params = map(get_text_contents, params)
-
-def parse_enum(en):
- name = en.getAttribute("name")
- enum = things.get(name)
- if not enum:
- enum = Enum(name)
- things[name] = enum
-
- enum.value = int(en.getAttribute("value"), 16)
-
-def parse_feature(feat):
- api = feat.getAttribute("api")
- version = feat.getAttribute("number")
- if 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 = things.get(name)
- if thing:
- thing.supported_apis.setdefault(api, version)
-
- if not api or api==target_api:
- 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")
+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
+ self.apis = {}
+ self.things = {}
- for t in itertools.chain(commands, enums):
- name = t.getAttribute("name")
- if name in things:
- if profile!="core":
- del things[name]
- else:
- things[name].deprecated.setdefault(api, version)
-
-def parse_extension(ext):
- ext_name = ext.getAttribute("name")
- if ext_name.startswith("GL_"):
- ext_name = ext_name[3:]
-
- supported = ext.getAttribute("supported").split('|')
- if target_api not in supported and ext_name!=target_ext:
- return
-
- extension = extensions.get(ext_name)
- if not extension:
- extension = Extension(ext_name)
- extensions[ext_name] = extension
+ 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)
- extension.supported_apis = supported
+ aliases = get_nested_elements(cmd, "alias")
+ func.aliases = [a.getAttribute("name") for a in aliases]
- requires = get_nested_elements(ext, "require")
- for req in requires:
- api = req.getAttribute("api")
- if api:
- supported = [api]
+ ptype = get_first_child(proto, "ptype")
+ if ptype:
+ func.return_type = get_text_contents(ptype)
else:
- supported = extension.supported_apis
+ for c in proto.childNodes:
+ if c.nodeType==xml.dom.Node.TEXT_NODE and c.data.strip():
+ func.return_type = c.data.strip()
+ break
- commands = get_nested_elements(req, "command")
- enums = get_nested_elements(req, "enum")
- for t in itertools.chain(commands, enums):
- name = t.getAttribute("name")
- if name in ignore_things:
- continue
+ params = get_nested_elements(cmd, "param")
+ func.params = map(get_text_contents, params)
- thing = things.get(name)
- if thing:
- if thing.extension and extension.name!=target_ext:
- if thing.extension.ext_type=="ARB" or thing.extension.name==target_ext:
- continue
- if thing.extension.ext_type=="EXT" and extension.ext_type!="ARB":
- continue
+ def parse_enum(self, en):
+ name = en.getAttribute("name")
+ enum = get_or_create(self.things, name, Enum)
- thing.extension = extension
- for a in supported:
- thing.supported_apis.setdefault(a, "ext")
+ enum.value = int(en.getAttribute("value"), 16)
-def parse_file(fn):
- doc = xml.dom.minidom.parse(fn)
- root = doc.documentElement
+ 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)
- commands = get_nested_elements(root, "commands/command")
- for cmd in commands:
- parse_command(cmd)
+ version = feat.getAttribute("number")
+ if version:
+ version = Version(*map(int, version.split('.')))
+ else:
+ version = None
- enums = get_nested_elements(root, "enums/enum")
- for en in enums:
- parse_enum(en)
+ 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
- features = get_nested_elements(root, "feature")
- for feat in features:
- parse_feature(feat)
+ 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")
- extensions = get_nested_elements(root, "extensions/extension")
- for ext in extensions:
- parse_extension(ext)
+ for t in itertools.chain(commands, enums):
+ 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
+
+ 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)
+
+ 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
-parse_file("gl.xml")
-parse_file("gl.fixes.xml")
+ 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():
+ e.backport = True
+ for t in e.things.itervalues():
+ if t.name.endswith(e.ext_type):
+ e.backport = False
+ break
-### Additional processing ###
+ 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
+ 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 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 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 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)
+
+ 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):
+ candidates = {}
+ 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 target_ext in extensions:
- target_ext = extensions[target_ext]
-else:
- print "Extension %s not found"%target_ext
- sys.exit(1)
+ 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]
+
+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:
- bpe_name = backport_ext
- backport_ext = extensions.get(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"%bpe_name
-elif backport_ext_candidates:
- if len(backport_ext_candidates)>1:
- print "Warning: multiple backport extension candidates: %s"%(" ".join(e.name for e in backport_ext_candidates))
+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 <msp/gl/extension.h>
#include <msp/gl/gl.h>
""")
-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()):
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
#define GET_PROC_ADDRESS_1_1(x) GET_PROC_ADDRESS(x)
#endif
""")
-out.write("""
+ out.write("""
namespace Msp {
namespace GL {
""")
-for f in funcs:
- out.write("%s %s = 0;\n"%(f.typedef, f.name))
-
-out.write("\nExtension::SupportLevel init_%s()\n{\n"%target_ext.name.lower())
-if core_version:
- out.write("\tif(is_disabled(\"GL_%s\"))\n\t\treturn Extension::UNSUPPORTED;\n"%target_ext.name)
- out.write("#if !defined(__APPLE__) || defined(GL_VERSION_%d_%d)\n"%tuple(core_version))
- out.write("\tif(")
- if backport_ext:
- out.write("is_supported(\"GL_%s\") || "%backport_ext.name)
- out.write("is_supported(Version(%d, %d)"%tuple(core_version))
- if deprecated_version:
- out.write(", Version(%d, %d)"%tuple(deprecated_version))
- out.write("))\n\t{\n")
- for f in funcs:
- if target_api in f.supported_apis:
- gpa_suffix = ""
- if f.version is not None and f.version<=[1, 1]:
- gpa_suffix = "_1_1"
- out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS%s(%s));\n"%(f.name, f.typedef, gpa_suffix, f.name))
- out.write("\t\treturn Extension::CORE;\n")
- out.write("\t}\n")
- out.write("#endif\n")
-if source_ext and source_ext!=backport_ext:
- out.write("#if !defined(__APPLE__) || defined(GL_%s)\n"%target_ext.name)
- out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(source_ext.name))
- for f in funcs:
- if f.sources:
- src = None
- for s in f.sources:
- if s.name.endswith(source_ext.ext_type):
- src = s
- break
- if not src:
- src = f.sources[0]
- 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("#endif\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] <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"
+
+ 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<len(sys.argv):
+ out_base = os.path.splitext(sys.argv[i])[0]
+ else:
+ out_base = ext_parser.target_ext.lower()
+
+ xml_parser = GlXmlParser(host_api_name, ext_parser.target_ext)
+ xml_parser.parse_file("gl.xml")
+ xml_parser.parse_file("gl.fixes.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)
+ if ext_parser.core_version:
+ generator.core_version = ext_parser.core_version
+ if ext_parser.deprecated_version:
+ generator.deprecated_version = ext_parser.deprecated_version
+ generator.write_header(out_base+".h")
+ generator.write_source(out_base+".cpp")
+
+if __name__=="__main__":
+ main()