X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=blobdiff_plain;f=scripts%2Fextgen.py;h=45094a4df35df26fe2608e0f07a4005378ec125e;hp=d8364b26ef3dbdeb160a4c9be5019e0ad0404f43;hb=55dbeb5e04516699b8415104e346243d5e4c48c9;hpb=9cf5d10abe5bdff0708c41544f6e8b15aca5151f diff --git a/scripts/extgen.py b/scripts/extgen.py index d8364b26..45094a4d 100755 --- a/scripts/extgen.py +++ b/scripts/extgen.py @@ -2,132 +2,408 @@ 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 [] [ ...]" + extgen.py [api] [] [ ...] + extgen.py [api] [] -Reads gl.spec and generates files to use . Any promoted functions +Reads gl.xml and generates files to use . Any promoted functions are exposed with their promoted names. If extensions are given, -any promoted functions from those are pulled in as well. can be -given to override the version where was promoted to core.""" +any promoted functions from those are pulled in as well. can +be given to override the version where was promoted to core. + +In the second form, the parameters are read from . If is +absent, the extension's lowercased name is used. Anything after the last dot +in is removed and replaced with cpp and h.""" sys.exit(0) -ext = sys.argv[1] +target_api = "gl" + +i = 1 +if sys.argv[i].startswith("gl"): + target_api = sys.argv[i] + i += 1 + +target_ext = sys.argv[i] +backport_ext = None out_base = None -if ext.endswith(".glext"): - fn = ext - ext = None - ver = 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": - ext = parts[1] + target_ext = parts[1] elif parts[0]=="core_version": - ver = parts[1] + if parts[1]==target_api: + core_version = parts[2] elif parts[0]=="secondary": secondary.append(parts[1]) - if len(sys.argv)>=3: - out_base = os.path.splitext(sys.argv[2])[0] + elif parts[0]=="backport": + backport_ext = parts[1] + elif parts[0]=="ignore": + ignore_things.append(parts[1]) + if i+1=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 +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) + +class Extension: + def __init__(self, name): + self.name = name + self.supported_apis = [] + underscore = name.find('_') + self.ext_type = name[0:underscore] + self.base_name = name[underscore+1:] + +extensions = {} +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 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) - break + 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") + 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") + + for t in itertools.chain(commands, enums): + name = t.getAttribute("name") + if profile!="core" and 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 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 + + extension.supported_apis = supported + + requires = get_nested_elements(ext, "require") + for req in requires: + api = req.getAttribute("api") + if api: + supported = [api] + else: + supported = extension.supported_apis + + 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 + + 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 + + thing.extension = extension + for a in supported: + thing.supported_apis.setdefault(a, "ext") + +def parse_file(fn): + doc = xml.dom.minidom.parse(fn) + root = doc.documentElement + + commands = get_nested_elements(root, "commands/command") + for cmd in commands: + parse_command(cmd) + + enums = get_nested_elements(root, "enums/enum") + for en in enums: + parse_enum(en) + + features = get_nested_elements(root, "feature") + for feat in features: + parse_feature(feat) + + extensions = get_nested_elements(root, "extensions/extension") + for ext in extensions: + parse_extension(ext) + +parse_file("gl.xml") +parse_file("gl.fixes.xml") + +### Additional processing ### + +if target_ext in extensions: + target_ext = extensions[target_ext] +else: + print "Extension %s not found"%target_ext + sys.exit(1) + +# 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) + +def get_key_api(things): + common_apis = set(target_ext.supported_apis) + for t in things: + common_apis.intersection_update(t.supported_apis.keys()) + if common_apis: + return common_apis.pop() + else: + return target_api - for a in f.aliases: - if a in funcs: - funcs[a].extfunc = f +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 -def is_relevant(f): - if f.category==ext and not f.aliases: +# 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) + +# 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 f.extfunc: - e = f.extfunc - if e.category==ext or e.category in secondary: + + # 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 + 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 = filter(is_relevant, enums) +enums.sort(key=(lambda e: e.value)) + +# Some final preparations for creating the files +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 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(ext_type) and target_api in t.supported_apis: + if t.extension not in backport_ext_candidates: + backport_ext_candidates.append(t.extension) + +if backport_ext: + if backport_ext=="none": + backport_ext = None + else: + backport_ext = extensions[backport_ext] + + if backport_ext not in backport_ext_candidates: + print "Warning: explicitly specified backport extension %s does not look like a backport extension" +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)) + + for e in backport_ext_candidates: + if e.base_name==target_ext.base_name: + backport_ext = 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 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 + f.typedef = "FPtr_%s"%f.name + +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: - f.typedef = "PFNGL%sPROC"%f.name.upper() - if not f.name.endswith(exttype): - bp_ext = f.category + 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 -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.name.upper()) +out.write("#define MSP_GL_%s_\n"%target_ext.name.upper()) out.write(""" #include @@ -135,14 +411,42 @@ out.write(""" namespace Msp { 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: + 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" -out.write("\nextern Extension %s;\n"%ext) + enums_by_category = {} + for e in enums: + cat = None + if e.version: + cat = api_prefix+"_VERSION_"+"_".join(map(str, e.version)) + elif e.extension: + cat = "GL_"+e.extension.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") + + for f in funcs: + out.write("extern %s %s;\n"%(f.typedef, f.name)) + +out.write("extern Extension %s;\n"%target_ext.name) out.write(""" } // namespace GL @@ -152,41 +456,69 @@ out.write(""" """) out = file(out_base+".cpp", "w") -out.write("#include \"%s.h\"\n"%ext.lower()) +out.write("#include \"%s.h\"\n"%target_ext.name.lower()) + +if funcs: + out.write(""" +#ifdef __APPLE__ +#define GET_PROC_ADDRESS(x) &::x +#else +#define GET_PROC_ADDRESS(x) get_proc_address(#x) +#endif +#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("%s gl%s = 0;\n"%(f.typedef, f.name)) + out.write("%s %s = 0;\n"%(f.typedef, 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("\nExtension::SupportLevel init_%s()\n{\n"%target_ext.name.lower()) +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.name) 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 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") -if ext!=bp_ext: - out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(ext)) +if source_ext and source_ext!=backport_ext: + out.write("\tif(is_supported(\"GL_%s\"))\n\t{\n"%(source_ext.name)) 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 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("\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.name, target_ext.name, target_ext.name.lower())) out.write(""" } // namespace GL