+ 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:
+ 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
+
+ params = get_nested_elements(cmd, "param")
+ func.params = map(get_text_contents, params)
+
+ 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)
+
+ 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)
+
+ if lower_count>max_count or (missing and len(missing)*2<lower_count+max_count):
+ print "Warning: Inconsistent core version %s"%max_version
+
+ if missing:
+ if debug:
+ print "---"
+ print "%d things missing from core:"%len(missing)
+ for t in missing:
+ print " "+t.name
+ return None
+
+ return max_version
+
+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)