import sys
import os
+import xml.dom
+import xml.dom.minidom
+import itertools
+
+### Command line processing ###
if len(sys.argv)<2:
print """Usage:
- extgen.py <extension> [<version>] [<secondary> ...]"
+ extgen.py <extension> [<core_version>] [<secondary> ...]
+ extgen.py <extfile> [<outfile>]
-Reads gl.spec and generates files to use <extension>. Any promoted functions
+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. <version> can be
-given to override the version where <extension> was promoted to core."""
+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.
+
+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)
ext = sys.argv[1]
ver = secondary.pop(0)
exttype = ext.split('_')[0]
-bp_ext = None
+backport_ext = None
if not out_base:
out_base = ext.lower()
-class Function:
- def __init__(self, name):
+### XML file parsing ###
+
+class Thing:
+ FUNCTION = 1
+ ENUM = 2
+
+ def __init__(self, name, kind):
self.name = name
- self.typedef = None
+ self.kind = kind
self.version = None
- self.category = None
- self.vectorequiv = None
+ self.extension = None
self.aliases = []
- self.extfunc = None
+ self.source = None
+
+class Function(Thing):
+ def __init__(self, name):
+ Thing.__init__(self, name, Thing.FUNCTION)
+ self.return_type = "void"
+ self.params = []
+ self.typedef = None
+ self.vectorequiv = None
-funcs = {}
-cur_func = None
+class Enum(Thing):
+ def __init__(self, name):
+ Thing.__init__(self, name, Thing.ENUM)
+ self.value = 0
+
+things = {}
+
+def get_nested_elements(elem, path):
+ if '/' in path:
+ head, tail = path.split('/', 1)
+ result = []
+ for e in elem.getElementsByTagName(head):
+ result += get_nested_elements(e, tail)
+ return result
+ else:
+ return elem.getElementsByTagName(path)
+
+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:
+ result += get_text_contents(c)
+ return result
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
+ doc = xml.dom.minidom.parse(fn)
+ root = doc.documentElement
+ commands = get_nested_elements(root, "commands/command")
+ for cmd in commands:
+ proto = cmd.getElementsByTagName("proto")[0]
+ name = get_text_contents(proto.getElementsByTagName("name")[0])
+ func = things.get(name)
+ if not func:
+ func = Function(name)
+ things[name] = func
+
+ aliases = cmd.getElementsByTagName("alias")
+ for a in aliases:
+ func.aliases.append(a.getAttribute("name"))
+
+ vec = cmd.getElementsByTagName("vecequiv")
+ if vec:
+ func.vectorequiv = vec[0].getAttribute("name")
+
+ ptype = proto.getElementsByTagName("ptype")
+ if ptype:
+ func.return_type = get_text_contents(ptype[0])
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)
+ 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 = cmd.getElementsByTagName("param")
+ for p in params:
+ func.params.append(get_text_contents(p))
+
+ enums = get_nested_elements(root, "enums/enum")
+ for en in enums:
+ 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 is_relevant(f):
- if f.category==ext and not f.aliases:
+ features = root.getElementsByTagName("feature")
+ for feat in features:
+ api = feat.getAttribute("api")
+ if api=="gl":
+ version = feat.getAttribute("number")
+
+ commands = get_nested_elements(feat, "require/command")
+ enums = get_nested_elements(feat, "require/enum")
+ for t in itertools.chain(commands, enums):
+ name = t.getAttribute("name")
+ thing = things.get(name)
+ if thing:
+ thing.version = version
+
+ if feat.getAttribute("name")=="MSPGL_REMOVE":
+ commands = get_nested_elements(feat, "remove/command")
+ enums = get_nested_elements(feat, "require/enum")
+ for t in itertools.chain(commands, enums):
+ name = t.getAttribute("name")
+ if name in things:
+ del things[name]
+
+ extensions = get_nested_elements(root, "extensions/extension")
+ for ext in extensions:
+ ext_name = ext.getAttribute("name")
+ if ext_name.startswith("GL_"):
+ ext_name = ext_name[3:]
+ supported = ext.getAttribute("supported").split('|')
+ if "gl" in supported:
+ commands = get_nested_elements(ext, "require/command")
+ enums = get_nested_elements(ext, "require/enum")
+ for t in itertools.chain(commands, enums):
+ name = t.getAttribute("name")
+ thing = things.get(name)
+ if thing:
+ thing.extension = ext_name
+
+parse_file("gl.xml")
+parse_file("gl.fixes.xml")
+
+### Additional processing ###
+
+# Find aliases for enums
+enums = [t for t in things.itervalues() if t.kind==Thing.ENUM]
+core_enums_by_value = dict((e.value, e) for e in enums if e.version)
+
+for e in enums:
+ if e.kind==t.ENUM and e.extension:
+ ce = core_enums_by_value.get(e.value)
+ if ce and ce!=e:
+ e.aliases.append(ce.name)
+
+# Create references from core things to their extension counterparts
+for t in things.itervalues():
+ if t.extension==ext or t.extension in secondary:
+ for a in t.aliases:
+ alias = things.get(a)
+ if alias:
+ alias.source = t
+
+def is_relevant(t):
+ # Unpromoted extension things are relevant
+ if t.extension==ext and not t.aliases:
return True
- if f.extfunc:
- e = f.extfunc
- if e.category==ext or e.category in secondary:
+
+ # Core things promoted from the extension are also relevant
+ if t.source:
+ e = t.source
+ if e.extension==ext or e.extension in secondary:
return True
+
return False
-funcs = [f for f in funcs.itervalues() if is_relevant(f)]
+funcs = [t for t in things.itervalues() if t.kind==Thing.FUNCTION and is_relevant(t)]
funcs.sort(key=(lambda f: f.name))
+enums = [e for e in enums if is_relevant(e)]
+enums.sort(key=(lambda e: e.value))
+
+for t in itertools.chain(funcs, enums):
+ if not ver:
+ ver = t.version
+
+ # Things in backport extensions don't acquire an extension suffix
+ if t.extension and not t.name.endswith(exttype):
+ backport_ext = t.extension
for f in funcs:
- if f.extfunc:
- f.typedef = "PFNGL%sPROC"%f.extfunc.name.upper()
- if not ver:
- ver = f.version
- if not f.category.startswith("VERSION_"):
- bp_ext = f.category
+ if f.source:
+ # Typedefs for early core functions are not available in all
+ # implementations
+ f.typedef = "PFN%sPROC"%f.source.name.upper()
else:
- f.typedef = "PFNGL%sPROC"%f.name.upper()
- if not f.name.endswith(exttype):
- bp_ext = f.category
+ f.typedef = "PFN%sPROC"%f.name.upper()
if ver:
ver = map(int, ver.split('.'))
+### Output ###
+
out = file(out_base+".h", "w")
out.write("#ifndef MSP_GL_%s_\n"%ext.upper())
out.write("#define MSP_GL_%s_\n"%ext.upper())
namespace GL {
""")
-if funcs:
- out.write("\n")
-for f in funcs:
- out.write("extern %s gl%s;\n"%(f.typedef, f.name))
+if funcs or enums:
+ out.write("\n#ifndef GL_%s\n"%ext)
+ for f in funcs:
+ out.write("typedef %s (*%s)(%s);\n"%(f.return_type, f.typedef, ", ".join(f.params)))
+ if funcs and enums:
+ out.write("\n")
+ for e in enums:
+ out.write("#define %s 0x%04X\n"%(e.name, e.value))
+ out.write("#endif\n\n")
+
+ # Apple's OpenGL implementation doesn't have a GetProcAddress function; link
+ # directly to the OpenGL library
+ out.write("\n#if !defined(__APPLE__) || !defined(GL_%s)\n"%ext)
+ for f in funcs:
+ out.write("extern %s %s;\n"%(f.typedef, f.name))
+ out.write("#endif\n")
out.write("\nextern Extension %s;\n"%ext)
""")
if funcs:
- out.write("\n")
-for f in funcs:
- out.write("%s gl%s = 0;\n"%(f.typedef, f.name))
+ out.write("\n#if !defined(__APPLE__) || !defined(GL_%s)\n"%ext)
+ for f in funcs:
+ out.write("%s %s = 0;\n"%(f.typedef, f.name))
+ out.write("\n#endif\n")
out.write("\nExtension::SupportLevel init_%s()\n{\n"%ext.lower())
+out.write("#ifdef GL_%s\n"%ext)
if ver:
out.write("\tif(is_version_at_least(%d, %d)"%tuple(ver))
- if bp_ext:
- out.write(" || is_supported(\"GL_%s\")"%bp_ext)
+ if backport_ext:
+ out.write(" || is_supported(\"GL_%s\")"%backport_ext)
out.write(")\n\t{\n")
- for f in funcs:
- out.write("\t\tgl%s = reinterpret_cast<%s>(get_proc_address(\"gl%s\"));\n"%(f.name, f.typedef, f.name))
+ if funcs:
+ out.write("#ifndef __APPLE__\n")
+ for f in funcs:
+ out.write("\t\t%s = reinterpret_cast<%s>(get_proc_address(\"%s\"));\n"%(f.name, f.typedef, f.name))
+ out.write("#endif\n")
out.write("\t\treturn Extension::CORE;\n")
out.write("\t}\n")
-if ext!=bp_ext:
+if ext!=backport_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<%s>(get_proc_address(\"gl%s\"));\n"%(f.name, f.typedef, n))
+ if funcs:
+ out.write("#ifndef __APPLE__\n")
+ for f in funcs:
+ n = f.name
+ if f.source:
+ n = f.source.name
+ out.write("\t\t%s = reinterpret_cast<%s>(get_proc_address(\"%s\"));\n"%(f.name, f.typedef, n))
+ out.write("#endif\n")
out.write("\t\treturn Extension::EXTENSION;\n")
out.write("\t}\n")
+out.write("#endif\n")
out.write("\treturn Extension::UNSUPPORTED;\n")
out.write("}\n")