]> git.tdb.fi Git - libs/gl.git/commitdiff
Refactor and optimize the extension generator script
authorMikko Rasa <tdb@tdb.fi>
Wed, 15 Oct 2014 15:36:31 +0000 (18:36 +0300)
committerMikko Rasa <tdb@tdb.fi>
Wed, 15 Oct 2014 15:44:49 +0000 (18:44 +0300)
Overly short variable names have been expanded
parse_file was getting a bit unwieldy and has been split
getElementsByTagName is recursive and slow, replaced with custom function

scripts/extgen.py

index 7a1b18629cafa4b2e5a116b316a2b9beb8e63346..c8f87c3bf79278bb98e5e1342912f66570752cab 100755 (executable)
@@ -23,34 +23,37 @@ 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]
+target_ext = sys.argv[1]
 out_base = None
-if ext.endswith(".glext"):
-       fn = ext
-       ext = None
-       ver = None
+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":
-                       ext = parts[1]
+                       target_ext = parts[1]
                elif parts[0]=="core_version":
-                       ver = parts[1]
+                       core_version = parts[1]
                elif parts[0]=="secondary":
                        secondary.append(parts[1])
        if len(sys.argv)>=3:
                out_base = os.path.splitext(sys.argv[2])[0]
 else:
        secondary = sys.argv[2:]
-       ver = None
+       core_version = None
        if secondary and secondary[0][0].isdigit():
-               ver = secondary.pop(0)
+               core_version = secondary.pop(0)
 
-exttype = ext.split('_')[0]
+ext_type = target_ext.split('_')[0]
 backport_ext = None
 
+if core_version:
+       core_version = map(int, core_version.split('.'))
+
 if not out_base:
-       out_base = ext.lower()
+       out_base = target_ext.lower()
 
 ### XML file parsing ###
 
@@ -72,7 +75,6 @@ class Function(Thing):
                self.return_type = "void"
                self.params = []
                self.typedef = None
-               self.vectorequiv = None
 
 class Enum(Thing):
        def __init__(self, name):
@@ -82,14 +84,22 @@ class Enum(Thing):
 things = {}
 
 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 e in elem.getElementsByTagName(head):
-                       result += get_nested_elements(e, tail)
+               for c in childElements:
+                       if c.tagName==head:
+                               result += get_nested_elements(c, tail)
                return result
        else:
-               return elem.getElementsByTagName(path)
+               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 = ""
@@ -100,85 +110,97 @@ def get_text_contents(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
+
+       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")
+       if api=="gl":
+               version = feat.getAttribute("number")
+               if version:
+                       version = map(int, version.split('.'))
+               else:
+                       version = None
+
+               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, "remove/enum")
+                       for t in itertools.chain(commands, enums):
+                               name = t.getAttribute("name")
+                               if name in things:
+                                       del things[name]
+
+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 "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
+
 def parse_file(fn):
        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:
-                       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 = cmd.getElementsByTagName("param")
-               for p in params:
-                       func.params.append(get_text_contents(p))
+               parse_command(cmd)
 
        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
+               parse_enum(en)
 
-               enum.value = int(en.getAttribute("value"), 16)
-
-       features = root.getElementsByTagName("feature")
+       features = get_nested_elements(root, "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]
+               parse_feature(feat)
 
        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_extension(ext)
 
 parse_file("gl.xml")
 parse_file("gl.fixes.xml")
@@ -197,7 +219,7 @@ for e in enums:
 
 # Create references from core things to their extension counterparts
 for t in things.itervalues():
-       if t.extension==ext or t.extension in secondary:
+       if t.extension==target_ext or t.extension in secondary:
                for a in t.aliases:
                        alias = things.get(a)
                        if alias:
@@ -205,28 +227,28 @@ for t in things.itervalues():
 
 def is_relevant(t):
        # Unpromoted extension things are relevant
-       if t.extension==ext and not t.aliases:
+       if t.extension==target_ext and not t.aliases:
                return True
 
        # Core things promoted from the extension are also relevant
        if t.source:
                e = t.source
-               if e.extension==ext or e.extension in secondary:
+               if e.extension==target_ext or e.extension in secondary:
                        return True
 
        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 = [e for e in enums if is_relevant(e)]
+enums = filter(is_relevant, enums)
 enums.sort(key=(lambda e: e.value))
 
 for t in itertools.chain(funcs, enums):
-       if not ver:
-               ver = t.version
+       if not core_version:
+               core_version = t.version
 
        # Things in backport extensions don't acquire an extension suffix
-       if t.extension and not t.name.endswith(exttype):
+       if t.extension and not t.name.endswith(ext_type):
                backport_ext = t.extension
 
 for f in funcs:
@@ -237,14 +259,11 @@ for f in funcs:
        else:
                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())
+out.write("#ifndef MSP_GL_%s_\n"%target_ext.upper())
+out.write("#define MSP_GL_%s_\n"%target_ext.upper())
 
 out.write("""
 #include <msp/gl/extension.h>
@@ -257,16 +276,16 @@ namespace GL {
 
 if funcs or enums:
        if funcs:
-               out.write("#if defined(__APPLE__) || !defined(GL_%s)\n"%ext)
+               out.write("#if defined(__APPLE__) || !defined(GL_%s)\n"%target_ext)
                for f in funcs:
                        out.write("typedef %s (*%s)(%s);\n"%(f.return_type, f.typedef, ", ".join(f.params)))
                out.write("#endif\n\n")
 
        if enums:
-               if ver:
-                       out.write("#ifndef GL_VERSION_%s\n"%"_".join(map(str, ver)))
+               if core_version:
+                       out.write("#ifndef GL_VERSION_%s\n"%"_".join(map(str, core_version)))
                else:
-                       out.write("#ifndef GL_%s\n"%ext)
+                       out.write("#ifndef GL_%s\n"%target_ext)
                for e in enums:
                        out.write("#define %s 0x%04X\n"%(e.name, e.value))
                out.write("#endif\n\n")
@@ -274,7 +293,7 @@ if funcs or enums:
        for f in funcs:
                out.write("extern %s %s;\n"%(f.typedef, f.name))
 
-out.write("extern Extension %s;\n"%ext)
+out.write("extern Extension %s;\n"%target_ext)
 
 out.write("""
 } // namespace GL
@@ -284,15 +303,17 @@ out.write("""
 """)
 
 out = file(out_base+".cpp", "w")
-out.write("#include \"%s.h\"\n"%ext.lower())
+out.write("#include \"%s.h\"\n"%target_ext.lower())
 
-out.write("""
+if funcs:
+       out.write("""
 #ifdef __APPLE__
 #define GET_PROC_ADDRESS(x) ::x
 #else
 #define GET_PROC_ADDRESS(x) get_proc_address(#x)
 #endif
-
+""")
+out.write("""
 namespace Msp {
 namespace GL {
 
@@ -301,10 +322,10 @@ namespace GL {
 for f in funcs:
        out.write("%s %s = 0;\n"%(f.typedef, f.name))
 
-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))
+out.write("\nExtension::SupportLevel init_%s()\n{\n"%target_ext.lower())
+out.write("#ifdef GL_%s\n"%target_ext)
+if core_version:
+       out.write("\tif(is_version_at_least(%d, %d)"%tuple(core_version))
        if backport_ext:
                out.write(" || is_supported(\"GL_%s\")"%backport_ext)
        out.write(")\n\t{\n")
@@ -313,8 +334,8 @@ if ver:
                        out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, f.typedef, f.name))
        out.write("\t\treturn Extension::CORE;\n")
        out.write("\t}\n")
-if ext!=backport_ext:
-       out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(ext))
+if target_ext!=backport_ext:
+       out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(target_ext))
        if funcs:
                for f in funcs:
                        n = f.name
@@ -327,7 +348,7 @@ out.write("#endif\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("\nExtension %s(\"GL_%s\", init_%s);\n"%(target_ext, target_ext, target_ext.lower()))
 
 out.write("""
 } // namespace GL