+ 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)
+
+ if min_version and len(deprecated)*2<len(things):
+ print "Warning: Inconsistent deprecation version %s"%min_version
+ if debug:
+ print "---"
+ print "%d things are deprecated:"%len(deprecated)
+ for t in deprecated:
+ print " "+t.name
+
+ return min_version
+
+def detect_backport_extension(host_api, things):
+ candidates = []
+ for t in things:
+ 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)
+
+ total_count = len(things)
+ best_ext = None
+ best_count = 0
+ for e in candidates:
+ things_in_ext = filter((lambda t: t.name in e.things), things)
+ count = len(things_in_ext)
+ if count==total_count:
+ return e
+ elif count>best_count:
+ best_ext = e
+ best_count = count
+
+ if total_count and best_count*2>=total_count:
+ print "Warning: Inconsistent backport extension %s"%best_ext.name
+
+def collect_extensions(thing, api, exts):
+ supp = thing.api_support.get(api)
+ if not supp:
+ return
+
+ for e in supp.extensions:
+ if not e.backport and e.ext_type!="MSP" and e not in exts:
+ exts.append(e)
+
+ for s in supp.sources:
+ collect_extensions(s, api, exts)
+
+def detect_source_extension(host_api, things, debug=False):
+ 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)
+
+ if debug:
+ print "---"
+ print "Looking for %d things in %d extensions"%(len(things), len(things_by_ext))
+
+ extensions = []
+ keep_exts = 0
+ base_version = None
+ recheck_base_version = True
+ missing = set(things)
+ while 1:
+ if recheck_base_version:
+ max_version = Version(1, 0)
+ for t in missing:
+ supp = t.api_support.get(host_api.name)
+ if supp and supp.core_version and max_version:
+ max_version = max(max_version, supp.core_version)
+ else:
+ max_version = None
+
+ if max_version:
+ if not base_version or max_version<base_version:
+ base_version = max_version
+ keep_exts = len(extensions)
+ elif not base_version:
+ keep_exts = len(extensions)
+
+ recheck_base_version = False
+
+ if not missing or not things_by_ext:
+ break
+
+ 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
+ elif count==largest_count and e.preference>largest_ext.preference:
+ largest_ext = e
+
+ if debug:
+ print "Found %d things in %s"%(largest_count, largest_ext.name)
+
+ extensions.append(largest_ext)
+ for t in things_by_ext[largest_ext]:
+ missing.remove(t)
+
+ supp = t.api_support.get(host_api.name)
+ if supp and supp.core_version==base_version:
+ recheck_base_version = True
+
+ del things_by_ext[largest_ext]
+ for e in things_by_ext.keys():
+ unseen = filter((lambda t: t in missing), things_by_ext[e])
+ if unseen:
+ things_by_ext[e] = unseen
+ else:
+ del things_by_ext[e]
+
+ if not missing:
+ return None, extensions
+ elif base_version:
+ if debug:
+ print "Found remaining things in version %s"%base_version
+ if keep_exts<len(extensions):
+ print "Discarding %d extensions that do not improve base version"%(len(extensions)-keep_exts)
+ del extensions[keep_exts:]
+ return base_version, extensions