[Yum-devel] [PATCH] Try comparing provides versions, if they all match. BZ 782345.

James Antill james at and.org
Tue Jan 17 21:13:47 UTC 2012


 When comparing packages due to a provides match, if all the winning packages
provide an = versioned number ... then we sort via. that and promote
that as the winner.
 Note that it _only_ works when you specify _just_ the provide name,
with no wildcards and each currently winning package has a single '='
versioned provide for that name.

 This happens before requirements lookups and shortest name, but after
everything else.
 Basically when we have:

pkgA: Provides: foo = 1.2
pkgB: Provides: foo = 3.6

...we'll now pick pkgB, before looking at requirements or shortest name.

 Test case in BZ.

 This could easily be considered "wrong" for things like:

pkgA: Provides: foo-ABI = 1234
pkgB: Provides: foo-ABI = abcd

...and maybe other cases I can't think of, but hopefully we have more
of (mostly, even) the former.

 Things to consider:

1. Do we want to do it after requirements checks, or maybe before where
we do it now?

2. Spew all over the API. Is it worth it?

3. Do we want to pass the req. down into the plugin callback?
---
 yum/__init__.py |   26 +++++++++++++++-----------
 yum/depsolve.py |   40 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 12 deletions(-)

diff --git a/yum/__init__.py b/yum/__init__.py
index 994a840..02f25ae 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -3461,7 +3461,8 @@ class YumBase(depsolve.Depsolve):
             raise Errors.YumBaseError, _('No Package found for %s') % errstring
         
         ps = ListPackageSack(pkglist)
-        result = self._bestPackageFromList(ps.returnNewestByNameArch())
+        result = self._bestPackageFromList(ps.returnNewestByNameArch(),
+                                           req=errstring)
         if result is None:
             raise Errors.YumBaseError, _('No Package found for %s') % errstring
         
@@ -3523,13 +3524,14 @@ class YumBase(depsolve.Depsolve):
             raise Errors.YumBaseError, _('No Package found for %s') % errstring
         
         ps = ListPackageSack(pkglist)
-        result = self._bestPackageFromList(ps.returnNewestByNameArch())
+        result = self._bestPackageFromList(ps.returnNewestByNameArch(),
+                                           req=errstring)
         if result is None:
             raise Errors.YumBaseError, _('No Package found for %s') % errstring
         
         return result
 
-    def _bestPackageFromList(self, pkglist):
+    def _bestPackageFromList(self, pkglist, req=None):
         """take list of package objects and return the best package object.
            If the list is empty, return None. 
            
@@ -3543,10 +3545,11 @@ class YumBase(depsolve.Depsolve):
         if len(pkglist) == 1:
             return pkglist[0]
 
-        bestlist = self._compare_providers(pkglist, None)
+        bestlist = self._compare_providers(pkglist, reqpo=None, req=req)
         return bestlist[0][0]
 
-    def bestPackagesFromList(self, pkglist, arch=None, single_name=False):
+    def bestPackagesFromList(self, pkglist, arch=None, single_name=False,
+                             req=None):
         """Return the best packages from a list of packages.  This
         function is multilib aware, so that it will not compare
         multilib to singlelib packages.
@@ -3574,9 +3577,9 @@ class YumBase(depsolve.Depsolve):
                 singleLib.append(po)
                 
         # we now have three lists.  find the best package(s) of each
-        multi = self._bestPackageFromList(multiLib)
-        single = self._bestPackageFromList(singleLib)
-        no = self._bestPackageFromList(noarch)
+        multi = self._bestPackageFromList(multiLib, req=req)
+        single = self._bestPackageFromList(singleLib, req=req)
+        no = self._bestPackageFromList(noarch, req=req)
 
         if single_name and multi and single and multi.name != single.name:
             # Sinlge _must_ match multi, if we want a single package name
@@ -3590,7 +3593,7 @@ class YumBase(depsolve.Depsolve):
         # if there's a noarch and it's newer than the multilib, we want
         # just the noarch.  otherwise, we want multi + single
         elif multi:
-            best = self._bestPackageFromList([multi,no])
+            best = self._bestPackageFromList([multi,no], req=req)
             if best.arch == "noarch":
                 returnlist.append(no)
             else:
@@ -3598,7 +3601,7 @@ class YumBase(depsolve.Depsolve):
                 if single: returnlist.append(single)
         # similar for the non-multilib case
         elif single:
-            best = self._bestPackageFromList([single,no])
+            best = self._bestPackageFromList([single,no], req=req)
             if best.arch == "noarch":
                 returnlist.append(no)
             else:
@@ -3861,7 +3864,8 @@ class YumBase(depsolve.Depsolve):
                         #                                all of the pkgs)
                         if mypkgs and not misc.re_glob(arg):
                             mypkgs = self.bestPackagesFromList(mypkgs,
-                                                               single_name=True)
+                                                               single_name=True,
+                                                               req=arg)
                         if mypkgs:
                             pkgs.extend(mypkgs)
                         
diff --git a/yum/depsolve.py b/yum/depsolve.py
index 720188c..1d1a4fd 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -31,6 +31,7 @@ from transactioninfo import TransactionMember
 import rpm
 
 from packageSack import ListPackageSack
+from packages import PackageEVR
 from constants import *
 import logginglevels
 import Errors
@@ -1248,11 +1249,12 @@ class Depsolve(object):
         return True
     _isPackageInstalled = isPackageInstalled
 
-    def _compare_providers(self, pkgs, reqpo):
+    def _compare_providers(self, pkgs, reqpo, req=None):
         """take the list of pkgs and score them based on the requesting package
            return a dictionary of po=score"""
         self.verbose_logger.log(logginglevels.DEBUG_4,
               _("Running compare_providers() for %s") %(str(pkgs)))
+        print "JDBG:", pkgs, reqpo, req
         
         def _common_prefix_len(x, y, minlen=2):
             num = min(len(x), len(y))
@@ -1292,6 +1294,22 @@ class Depsolve(object):
                 return None
             return x
 
+        def _pkg2prov_version(pkg, provname):
+            ''' This converts a package into a specific version tuple for the
+            required provide. The provide _must_ be '=' and epoch=None and
+            release=None == '0'.
+               If there is 0 or 2+ matches, return None.
+               If the req does not == the provide name, return None. '''
+            ret = None
+            for prov in pkg.provides:
+                (n, f, (e, v, r)) = prov
+                if n != provname:
+                    continue
+                if ret is not None:
+                    return None
+                ret = (e or '0', v, r or '0')
+            return ret
+
         #  Actual start of _compare_providers().
 
         # Do a NameArch filtering, based on repo. __cmp__
@@ -1414,6 +1432,26 @@ class Depsolve(object):
                         _('common prefix of %s between %s and %s' % (cpl, po, reqpo)))
                 
                     pkgresults[po] += cpl*2
+
+        if req is not None:
+            bestnum = max(pkgresults.values())
+            prov_depsolve = {}
+            for po in pkgs:
+                if pkgresults[po] != bestnum:
+                    continue
+                evr = _pkg2prov_version(po, req)
+                if evr is None:
+                    prov_depsolve = {}
+                    break
+                prov_depsolve[po] = evr
+            if len(prov_depsolve) > 1:
+                self.verbose_logger.log(logginglevels.DEBUG_4,
+                                        _('provides vercmp: %s') % str(req))
+                newest = sorted(prov_depsolve,
+                                key = lambda x: PackageEVR(*prov_depsolve[x]))[-1]
+                self.verbose_logger.log(logginglevels.DEBUG_4,
+                                        _(' Winner: %s') % newest)
+                pkgresults[newest] += 1
                 
         #  If we have more than one "best", see what would happen if we picked
         # each package ... ie. what things do they require that _aren't_ already
-- 
1.7.6.4



More information about the Yum-devel mailing list