[yum-cvs] test/testbase.py yum/config.py yum/depsolve.py yum/__init__.py

Tim Lauridsen timlau at linux.duke.edu
Tue Dec 11 07:10:10 UTC 2007


 test/testbase.py |    1 +
 yum/__init__.py  |   55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 yum/config.py    |    1 +
 yum/depsolve.py  |   29 +++++++++++++++++++++++++----
 4 files changed, 82 insertions(+), 4 deletions(-)

New commits:
commit 30476c7da4aa2c0795855d2f4364395661fd7c70
Author: Tim Lauridsen <tim at naboo.local>
Date:   Tue Dec 11 08:07:57 2007 +0100

    Added skip-broken code

diff --git a/test/testbase.py b/test/testbase.py
index bdb3e81..5b9d261 100644
--- a/test/testbase.py
+++ b/test/testbase.py
@@ -31,6 +31,7 @@ class FakeConf(object):
         self.installroot = '/'
         self.tsflags = []
         self.installonly_limit = 0
+        self.skip_broken = False
         self.disable_excludes = []
 
 class FakeRepo(object):
diff --git a/yum/__init__.py b/yum/__init__.py
index a003489..821e298 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -580,9 +580,64 @@ class YumBase(depsolve.Depsolve):
         
         if self.tsInfo.changed:
             (rescode, restring) = self.resolveDeps()
+
+        # if depsolve failed and skipbroken is enabled
+        # The remove the broken packages from the transactions and
+        # Try another depsolve
+        if self.conf.skip_broken and rescode==1:
+            rescode, restring = self._skipPackagesWithProblems(rescode, restring)
             
         return rescode, restring
 
+    def _skipPackagesWithProblems(self, rescode, restring):
+        ''' Remove the packages with depsolve errors and depsolve again '''
+        # Keep removing packages & Depsolve until all errors is gone
+        # or the transaction is empty
+        depTree = self._buildDepTree()
+        while len(self.po_with_problems) > 0 and rescode == 1:
+            startTs = set(self.tsInfo)
+            toRemove = []
+            for po,wpo in self.po_with_problems:
+                # check if the problem is caused by a package in the transaction
+                if not self.tsInfo.exists(po.pkgtup):
+                    if wpo:
+                        toRemove = self._getPackagesToRemove(wpo, depTree, toRemove)
+                    else:
+                        continue
+                else:
+                    toRemove = self._getPackagesToRemove(po, depTree, toRemove)
+            if toRemove:
+                for po in toRemove:
+                    if self.tsInfo.exists(po.pkgtup):
+                        self.verbose_logger.debug("skipping %s because of depsolving problems" % str(po))
+                        self.tsInfo.remove(po.pkgtup)
+            else: # Nothing was removed, so we still got a problem
+                break # Bail out
+            rescode, restring = self.resolveDeps()
+            endTs = set(self.tsInfo)
+             # Check if tsInfo has changes since we started to skip packages
+             # if there is no changes then we got a loop.
+            if startTs-endTs == set():
+                break    # bail out
+        return rescode, restring
+
+    def _buildDepTree(self):
+        depTree = {}
+        for txmbr in self.tsInfo:
+            if not txmbr.po in depTree.keys():
+                depTree[txmbr.po] = []
+            for po in (txmbr.updates + txmbr.obsoletes+txmbr.depends_on):
+                depTree[txmbr.po].append(po)
+        return depTree
+    
+    def _getPackagesToRemove(self,po,deptree,toRemove):
+        if po not in toRemove:
+            toRemove.append(po)       
+        for child in deptree[po]:
+            if child not in toRemove:
+                toRemove = self._getPackagesToRemove(child, deptree, toRemove)
+        return toRemove
+
     def runTransaction(self, cb):
         """takes an rpm callback object, performs the transaction"""
 
diff --git a/yum/config.py b/yum/config.py
index 0162336..7834211 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -600,6 +600,7 @@ class YumConf(StartupConf):
     mirrorlist_expire = SecondsOption(86400) # time in seconds (1 day)
     rpm_check_debug = BoolOption(True)
     disable_excludes = ListOption()    
+    skip_broken = BoolOption(False)
 
     
     _reposlist = []
diff --git a/yum/depsolve.py b/yum/depsolve.py
index 25f5f76..18ddae4 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -258,6 +258,11 @@ class Depsolve(object):
             CheckDeps, missingdep = self._requiringFromTransaction(po, requirement, errormsgs)
         else:
             CheckDeps, missingdep = self._requiringFromInstalled(po, requirement, errormsgs)
+
+        # Check packages with problems
+        if missingdep:
+            self.po_with_problems.add((po,self._working_po))
+
         return (CheckDeps, missingdep, errormsgs)
 
     def _requiringFromInstalled(self, requiringPo, requirement, errorlist):
@@ -560,13 +565,13 @@ class Depsolve(object):
                 name))
             # FIXME: we should probably handle updating multiple packages...
             txmbr = self.tsInfo.addUpdate(best, inst[0])
-            txmbr.setAsDep()
+            txmbr.setAsDep(po=requiringPo)
             txmbr.reason = "dep"
         else:
             self.verbose_logger.debug('TSINFO: Marking %s as install for %s', best,
                 name)
             txmbr = self.tsInfo.addInstall(best)
-            txmbr.setAsDep()
+            txmbr.setAsDep(po=requiringPo)
 
             # if we had other packages with this name.arch that we found
             # before, they're not going to be installed anymore, so we
@@ -589,6 +594,7 @@ class Depsolve(object):
 
         needname, flags, needversion = conflict
         (name, arch, epoch, ver, rel) = po.pkgtup
+        requiringPo = po
 
         niceformatneed = rpmUtils.miscutils.formatRequire(needname, needversion, flags)
         if self.dsCallback: self.dsCallback.procConflict(name, niceformatneed)
@@ -640,7 +646,7 @@ class Depsolve(object):
             self.verbose_logger.log(logginglevels.DEBUG_2,
                 'TSINFO: Updating %s to resolve conflict.', po)
             txmbr = self.tsInfo.addUpdate(po, confpkg)
-            txmbr.setAsDep()
+            txmbr.setAsDep(po=requiringPo)
             txmbr.reason = "dep"
             CheckDeps = 1
             
@@ -649,7 +655,9 @@ class Depsolve(object):
             CheckDeps, conflicts = self._unresolveableConflict(conf, name, errormsgs)
             self.verbose_logger.log(logginglevels.DEBUG_1, '%s conflicts: %s',
                 name, conf)
-        
+            if conflicts:
+                self.po_with_problems.add((requiringPo,None))
+
         return (CheckDeps, conflicts, errormsgs)
 
     def _unresolveableConflict(self, conf, name, errors):
@@ -703,6 +711,8 @@ class Depsolve(object):
             # reset what we've seen as things may have changed between calls
             # to resolveDeps (rh#242368, rh#308321)
             self._dcobj.reset()
+        self.po_with_problems = set()
+        self._working_po = None
 
         CheckDeps = True
         CheckRemoves = False
@@ -770,6 +780,8 @@ class Depsolve(object):
         self.tsInfo.changed = False
         if len(errors) > 0:
             errors = unique(errors)
+            for po,wpo in self.po_with_problems:
+                self.verbose_logger.debug("%s has depsolving problems" % po)
             return (1, errors)
 
         if len(self.tsInfo) > 0:
@@ -794,6 +806,15 @@ class Depsolve(object):
             self.verbose_logger.log(logginglevels.DEBUG_2,
                                     "Checking deps for %s" %(txmbr,))
 
+            # store the primary po we currently are working on 
+            # so we can store it in self.po_with_problems.
+            # it is useful when an update is breaking an require of an installed package
+            # then we want to know who is causing the problem, not just who is having the problem. 
+            if not txmbr.updates and txmbr.relatedto:
+                self._working_po = txmbr.relatedto[0][0]
+            else:
+                self._working_po = txmbr.po
+           
             if txmbr.output_state in inst:
                 thisneeds = self._checkInstall(txmbr)
                 CheckInstalls = True



More information about the Yum-cvs-commits mailing list