commit: 35208d80a55228a50ea4ac64904465ce4651e381 Author: Zac Medico <zmedico <AT> gentoo <DOT> org> AuthorDate: Fri Mar 17 06:14:12 2017 +0000 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> CommitDate: Fri Mar 17 16:01:58 2017 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=35208d80
depgraph: avoid missed update with slot operator and circ dep (bug 612874) 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 Acked-by: Brian Dolbec <dolsen <AT> gentoo.org> pym/_emerge/depgraph.py | 31 ++++++++++++----- .../resolver/test_slot_operator_exclusive_slots.py | 39 ++++++++++++++++++++++ pym/portage/tests/util/test_digraph.py | 2 ++ pym/portage/util/digraph.py | 9 +++++ 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index f4145d05f..e94b96ce8 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 2ab379cce..689ed31d0 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/tests/util/test_digraph.py b/pym/portage/tests/util/test_digraph.py index 4e858cf88..f519536d3 100644 --- a/pym/portage/tests/util/test_digraph.py +++ b/pym/portage/tests/util/test_digraph.py @@ -133,6 +133,8 @@ class DigraphTest(TestCase): for x in g, f: self.assertEqual(bool(x), True) self.assertEqual(x.contains("A"), True) + self.assertEqual(x.has_edge("B", "A"), True) + self.assertEqual(x.has_edge("A", "B"), False) self.assertEqual(x.firstzero(), "B") self.assertRaises(KeyError, x.remove, "Z") x.delnode("Z") diff --git a/pym/portage/util/digraph.py b/pym/portage/util/digraph.py index 4a9cb43b6..99b24fa1d 100644 --- a/pym/portage/util/digraph.py +++ b/pym/portage/util/digraph.py @@ -93,6 +93,15 @@ class digraph(object): del self.nodes[node] self.order = order + def has_edge(self, child, parent): + """ + Return True if the given edge exists. + """ + try: + return child in self.nodes[parent][0] + except KeyError: + return False + def remove_edge(self, child, parent): """ Remove edge in the direction from child to parent. Note that it is
