Squashed 'yocto-poky/' content from commit ea562de

git-subtree-dir: yocto-poky
git-subtree-split: ea562de57590c966cd5a75fda8defecd397e6436
diff --git a/bitbake/lib/bb/providers.py b/bitbake/lib/bb/providers.py
new file mode 100644
index 0000000..637e1fa
--- /dev/null
+++ b/bitbake/lib/bb/providers.py
@@ -0,0 +1,381 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# Copyright (C) 2003, 2004  Chris Larson
+# Copyright (C) 2003, 2004  Phil Blundell
+# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
+# Copyright (C) 2005        Holger Hans Peter Freyther
+# Copyright (C) 2005        ROAD GmbH
+# Copyright (C) 2006        Richard Purdie
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import re
+import logging
+from bb import data, utils
+from collections import defaultdict
+import bb
+
+logger = logging.getLogger("BitBake.Provider")
+
+class NoProvider(bb.BBHandledException):
+    """Exception raised when no provider of a build dependency can be found"""
+
+class NoRProvider(bb.BBHandledException):
+    """Exception raised when no provider of a runtime dependency can be found"""
+
+class MultipleRProvider(bb.BBHandledException):
+    """Exception raised when multiple providers of a runtime dependency can be found"""
+
+def findProviders(cfgData, dataCache, pkg_pn = None):
+    """
+    Convenience function to get latest and preferred providers in pkg_pn
+    """
+
+    if not pkg_pn:
+        pkg_pn = dataCache.pkg_pn
+
+    # Need to ensure data store is expanded
+    localdata = data.createCopy(cfgData)
+    bb.data.update_data(localdata)
+    bb.data.expandKeys(localdata)
+
+    preferred_versions = {}
+    latest_versions = {}
+
+    for pn in pkg_pn:
+        (last_ver, last_file, pref_ver, pref_file) = findBestProvider(pn, localdata, dataCache, pkg_pn)
+        preferred_versions[pn] = (pref_ver, pref_file)
+        latest_versions[pn] = (last_ver, last_file)
+
+    return (latest_versions, preferred_versions)
+
+
+def allProviders(dataCache):
+    """
+    Find all providers for each pn
+    """
+    all_providers = defaultdict(list)
+    for (fn, pn) in dataCache.pkg_fn.items():
+        ver = dataCache.pkg_pepvpr[fn]
+        all_providers[pn].append((ver, fn))
+    return all_providers
+
+
+def sortPriorities(pn, dataCache, pkg_pn = None):
+    """
+    Reorder pkg_pn by file priority and default preference
+    """
+
+    if not pkg_pn:
+        pkg_pn = dataCache.pkg_pn
+
+    files = pkg_pn[pn]
+    priorities = {}
+    for f in files:
+        priority = dataCache.bbfile_priority[f]
+        preference = dataCache.pkg_dp[f]
+        if priority not in priorities:
+            priorities[priority] = {}
+        if preference not in priorities[priority]:
+            priorities[priority][preference] = []
+        priorities[priority][preference].append(f)
+    tmp_pn = []
+    for pri in sorted(priorities):
+        tmp_pref = []
+        for pref in sorted(priorities[pri]):
+            tmp_pref.extend(priorities[pri][pref])
+        tmp_pn = [tmp_pref] + tmp_pn
+
+    return tmp_pn
+
+def preferredVersionMatch(pe, pv, pr, preferred_e, preferred_v, preferred_r):
+    """
+    Check if the version pe,pv,pr is the preferred one.
+    If there is preferred version defined and ends with '%', then pv has to start with that version after removing the '%'
+    """
+    if (pr == preferred_r or preferred_r == None):
+        if (pe == preferred_e or preferred_e == None):
+            if preferred_v == pv:
+                return True
+            if preferred_v != None and preferred_v.endswith('%') and pv.startswith(preferred_v[:len(preferred_v)-1]):
+                return True
+    return False
+
+def findPreferredProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
+    """
+    Find the first provider in pkg_pn with a PREFERRED_VERSION set.
+    """
+
+    preferred_file = None
+    preferred_ver = None
+
+    localdata = data.createCopy(cfgData)
+    localdata.setVar('OVERRIDES', "%s:pn-%s:%s" % (data.getVar('OVERRIDES', localdata), pn, pn))
+    bb.data.update_data(localdata)
+
+    preferred_v = localdata.getVar('PREFERRED_VERSION', True)
+    if preferred_v:
+        m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
+        if m:
+            if m.group(1):
+                preferred_e = m.group(1)[:-1]
+            else:
+                preferred_e = None
+            preferred_v = m.group(2)
+            if m.group(3):
+                preferred_r = m.group(3)[1:]
+            else:
+                preferred_r = None
+        else:
+            preferred_e = None
+            preferred_r = None
+
+        for file_set in pkg_pn:
+            for f in file_set:
+                pe, pv, pr = dataCache.pkg_pepvpr[f]
+                if preferredVersionMatch(pe, pv, pr, preferred_e, preferred_v, preferred_r):
+                    preferred_file = f
+                    preferred_ver = (pe, pv, pr)
+                    break
+            if preferred_file:
+                break;
+        if preferred_r:
+            pv_str = '%s-%s' % (preferred_v, preferred_r)
+        else:
+            pv_str = preferred_v
+        if not (preferred_e is None):
+            pv_str = '%s:%s' % (preferred_e, pv_str)
+        itemstr = ""
+        if item:
+            itemstr = " (for item %s)" % item
+        if preferred_file is None:
+            logger.info("preferred version %s of %s not available%s", pv_str, pn, itemstr)
+            available_vers = []
+            for file_set in pkg_pn:
+                for f in file_set:
+                    pe, pv, pr = dataCache.pkg_pepvpr[f]
+                    ver_str = pv
+                    if pe:
+                        ver_str = "%s:%s" % (pe, ver_str)
+                    if not ver_str in available_vers:
+                        available_vers.append(ver_str)
+            if available_vers:
+                available_vers.sort()
+                logger.info("versions of %s available: %s", pn, ' '.join(available_vers))
+        else:
+            logger.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s%s", preferred_file, pv_str, pn, itemstr)
+
+    return (preferred_ver, preferred_file)
+
+
+def findLatestProvider(pn, cfgData, dataCache, file_set):
+    """
+    Return the highest version of the providers in file_set.
+    Take default preferences into account.
+    """
+    latest = None
+    latest_p = 0
+    latest_f = None
+    for file_name in file_set:
+        pe, pv, pr = dataCache.pkg_pepvpr[file_name]
+        dp = dataCache.pkg_dp[file_name]
+
+        if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pe, pv, pr)) < 0)) or (dp > latest_p):
+            latest = (pe, pv, pr)
+            latest_f = file_name
+            latest_p = dp
+
+    return (latest, latest_f)
+
+
+def findBestProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
+    """
+    If there is a PREFERRED_VERSION, find the highest-priority bbfile
+    providing that version.  If not, find the latest version provided by
+    an bbfile in the highest-priority set.
+    """
+
+    sortpkg_pn = sortPriorities(pn, dataCache, pkg_pn)
+    # Find the highest priority provider with a PREFERRED_VERSION set
+    (preferred_ver, preferred_file) = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn, item)
+    # Find the latest version of the highest priority provider
+    (latest, latest_f) = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[0])
+
+    if preferred_file is None:
+        preferred_file = latest_f
+        preferred_ver = latest
+
+    return (latest, latest_f, preferred_ver, preferred_file)
+
+
+def _filterProviders(providers, item, cfgData, dataCache):
+    """
+    Take a list of providers and filter/reorder according to the
+    environment variables and previous build results
+    """
+    eligible = []
+    preferred_versions = {}
+    sortpkg_pn = {}
+
+    # The order of providers depends on the order of the files on the disk
+    # up to here. Sort pkg_pn to make dependency issues reproducible rather
+    # than effectively random.
+    providers.sort()
+
+    # Collate providers by PN
+    pkg_pn = {}
+    for p in providers:
+        pn = dataCache.pkg_fn[p]
+        if pn not in pkg_pn:
+            pkg_pn[pn] = []
+        pkg_pn[pn].append(p)
+
+    logger.debug(1, "providers for %s are: %s", item, pkg_pn.keys())
+
+    # First add PREFERRED_VERSIONS
+    for pn in pkg_pn:
+        sortpkg_pn[pn] = sortPriorities(pn, dataCache, pkg_pn)
+        preferred_versions[pn] = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn[pn], item)
+        if preferred_versions[pn][1]:
+            eligible.append(preferred_versions[pn][1])
+
+    # Now add latest versions
+    for pn in sortpkg_pn:
+        if pn in preferred_versions and preferred_versions[pn][1]:
+            continue
+        preferred_versions[pn] = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[pn][0])
+        eligible.append(preferred_versions[pn][1])
+
+    if len(eligible) == 0:
+        logger.error("no eligible providers for %s", item)
+        return 0
+
+    # If pn == item, give it a slight default preference
+    # This means PREFERRED_PROVIDER_foobar defaults to foobar if available
+    for p in providers:
+        pn = dataCache.pkg_fn[p]
+        if pn != item:
+            continue
+        (newvers, fn) = preferred_versions[pn]
+        if not fn in eligible:
+            continue
+        eligible.remove(fn)
+        eligible = [fn] + eligible
+
+    return eligible
+
+
+def filterProviders(providers, item, cfgData, dataCache):
+    """
+    Take a list of providers and filter/reorder according to the
+    environment variables and previous build results
+    Takes a "normal" target item
+    """
+
+    eligible = _filterProviders(providers, item, cfgData, dataCache)
+
+    prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % item, True)
+    if prefervar:
+        dataCache.preferred[item] = prefervar
+
+    foundUnique = False
+    if item in dataCache.preferred:
+        for p in eligible:
+            pn = dataCache.pkg_fn[p]
+            if dataCache.preferred[item] == pn:
+                logger.verbose("selecting %s to satisfy %s due to PREFERRED_PROVIDERS", pn, item)
+                eligible.remove(p)
+                eligible = [p] + eligible
+                foundUnique = True
+                break
+
+    logger.debug(1, "sorted providers for %s are: %s", item, eligible)
+
+    return eligible, foundUnique
+
+def filterProvidersRunTime(providers, item, cfgData, dataCache):
+    """
+    Take a list of providers and filter/reorder according to the
+    environment variables and previous build results
+    Takes a "runtime" target item
+    """
+
+    eligible = _filterProviders(providers, item, cfgData, dataCache)
+
+    # Should use dataCache.preferred here?
+    preferred = []
+    preferred_vars = []
+    pns = {}
+    for p in eligible:
+        pns[dataCache.pkg_fn[p]] = p
+    for p in eligible:
+        pn = dataCache.pkg_fn[p]
+        provides = dataCache.pn_provides[pn]
+        for provide in provides:
+            prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide, True)
+            #logger.debug(1, "checking PREFERRED_PROVIDER_%s (value %s) against %s", provide, prefervar, pns.keys())
+            if prefervar in pns and pns[prefervar] not in preferred:
+                var = "PREFERRED_PROVIDER_%s = %s" % (provide, prefervar)
+                logger.verbose("selecting %s to satisfy runtime %s due to %s", prefervar, item, var)
+                preferred_vars.append(var)
+                pref = pns[prefervar]
+                eligible.remove(pref)
+                eligible = [pref] + eligible
+                preferred.append(pref)
+                break
+
+    numberPreferred = len(preferred)
+
+    if numberPreferred > 1:
+        logger.error("Trying to resolve runtime dependency %s resulted in conflicting PREFERRED_PROVIDER entries being found.\nThe providers found were: %s\nThe PREFERRED_PROVIDER entries resulting in this conflict were: %s", item, preferred, preferred_vars)
+
+    logger.debug(1, "sorted runtime providers for %s are: %s", item, eligible)
+
+    return eligible, numberPreferred
+
+regexp_cache = {}
+
+def getRuntimeProviders(dataCache, rdepend):
+    """
+    Return any providers of runtime dependency
+    """
+    rproviders = []
+
+    if rdepend in dataCache.rproviders:
+        rproviders += dataCache.rproviders[rdepend]
+
+    if rdepend in dataCache.packages:
+        rproviders += dataCache.packages[rdepend]
+
+    if rproviders:
+        return rproviders
+
+    # Only search dynamic packages if we can't find anything in other variables
+    for pattern in dataCache.packages_dynamic:
+        pattern = pattern.replace('+', "\+")
+        if pattern in regexp_cache:
+            regexp = regexp_cache[pattern]
+        else:
+            try:
+                regexp = re.compile(pattern)
+            except:
+                logger.error("Error parsing regular expression '%s'", pattern)
+                raise
+            regexp_cache[pattern] = regexp
+        if regexp.match(rdepend):
+            rproviders += dataCache.packages_dynamic[pattern]
+            logger.debug(1, "Assuming %s is a dynamic package, but it may not exist" % rdepend)
+
+    return rproviders