Fix check_reverse_dependencies to ignore direct circular dependencies,
since these dependencies tend to prevent updates of packages. This
solves a missed update from llvm:0 to llvm:4 when clang is not in the
world file, as demonstrated by the included test case.

X-Gentoo-bug: 612874
X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=612874
---
 pym/_emerge/depgraph.py                            | 31 ++++++++++++-----
 .../resolver/test_slot_operator_exclusive_slots.py | 39 ++++++++++++++++++++++
 pym/portage/util/digraph.py                        |  6 ++++
 3 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index f4145d0..e94b96c 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -1844,14 +1844,29 @@ class depgraph(object):
                                        if (not self._too_deep(parent.depth) and
                                                not 
self._frozen_config.excluded_pkgs.
                                                findAtomForPackage(parent,
-                                               
modified_use=self._pkg_use_enabled(parent)) and
-                                               
(self._upgrade_available(parent) or
-                                               (parent.installed and 
self._in_blocker_conflict(parent)))):
-                                               # This parent may be 
irrelevant, since an
-                                               # update is available (see bug 
584626), or
-                                               # it could be uninstalled in 
order to solve
-                                               # a blocker conflict (bug 
612772).
-                                               continue
+                                               
modified_use=self._pkg_use_enabled(parent))):
+                                               # Check for common reasons that 
the parent's
+                                               # dependency might be 
irrelevant.
+                                               if 
self._upgrade_available(parent):
+                                                       # This parent could be 
replaced by
+                                                       # an upgrade (bug 
584626).
+                                                       continue
+                                               if parent.installed and 
self._in_blocker_conflict(parent):
+                                                       # This parent could be 
uninstalled in order
+                                                       # to solve a blocker 
conflict (bug 612772).
+                                                       continue
+                                               if 
self._dynamic_config.digraph.has_edge(parent,
+                                                       existing_pkg):
+                                                       # There is a direct 
circular dependency between
+                                                       # parent and 
existing_pkg. This type of
+                                                       # relationship tends to 
prevent updates
+                                                       # of packages (bug 
612874). Since candidate_pkg
+                                                       # is available, we risk 
a missed update if we
+                                                       # don't try to 
eliminate this parent from the
+                                                       # graph. Therefore, we 
give candidate_pkg a
+                                                       # chance, and assume 
that it will be masked
+                                                       # by backtracking if 
necessary.
+                                                       continue
 
                                atom_set = 
InternalPackageSet(initial_atoms=(atom,),
                                        allow_repo=True)
diff --git a/pym/portage/tests/resolver/test_slot_operator_exclusive_slots.py 
b/pym/portage/tests/resolver/test_slot_operator_exclusive_slots.py
index 2ab379c..689ed31 100644
--- a/pym/portage/tests/resolver/test_slot_operator_exclusive_slots.py
+++ b/pym/portage/tests/resolver/test_slot_operator_exclusive_slots.py
@@ -107,3 +107,42 @@ class SlotOperatorExclusiveSlotsTestCase(TestCase):
                                        test_case.fail_msg)
                finally:
                        playground.cleanup()
+
+
+               world = ["media-libs/mesa"]
+
+               test_cases = (
+
+                       # Test bug #612874, where a direct circular dependency
+                       # between llvm-3.9.1 and clang-3.9.1-r100 causes a
+                       # missed update from llvm:0 to llvm:4. Since llvm:4 does
+                       # not have a dependency on clang, the upgrade from 
llvm:0
+                       # to llvm:4 makes the installed 
sys-devel/clang-3.9.1-r100
+                       # instance eligible for removal by emerge --depclean, 
which
+                       # explains why clang does not appear in the mergelist.
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update": True, "--deep": True},
+                               success = True,
+                               ambiguous_merge_order = True,
+                               mergelist = [
+                                       'sys-devel/llvm-4.0.0',
+                                       (
+                                               'media-libs/mesa-17.0.1',
+                                               
'[uninstall]sys-devel/llvm-3.9.1',
+                                               '!sys-devel/llvm:0',
+                                       )
+                               ],
+                       ),
+
+               )
+
+               playground = ResolverPlayground(ebuilds=ebuilds,
+                       installed=installed, world=world)
+               try:
+                       for test_case in test_cases:
+                               playground.run_TestCase(test_case)
+                               self.assertEqual(test_case.test_success, True,
+                                       test_case.fail_msg)
+               finally:
+                       playground.cleanup()
diff --git a/pym/portage/util/digraph.py b/pym/portage/util/digraph.py
index 4a9cb43..b6be0c9 100644
--- a/pym/portage/util/digraph.py
+++ b/pym/portage/util/digraph.py
@@ -93,6 +93,12 @@ class digraph(object):
                        del self.nodes[node]
                self.order = order
 
+       def has_edge(self, child, parent):
+               """
+               Return True if the given edge exists.
+               """
+               return child in self.nodes[parent][0]
+
        def remove_edge(self, child, parent):
                """
                Remove edge in the direction from child to parent. Note that it 
is
-- 
2.10.2


Reply via email to