From c08402d745eef26b99091f62556f48aa9b60345a Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Wed, 21 Aug 2013 18:32:22 -0700 Subject: [PATCH] dep_zapdeps: prefer all_installed_slots choices This will fix bug #480736, by preferring libpostproc in deps like || ( ffmpeg:0 libpostproc ) where libpostproc is already installed and ffmpeg:0 is not. --- pym/portage/dep/dep_check.py | 29 +++++++--- pym/portage/tests/resolver/test_or_choices.py | 55 +++++++++++++++++++ 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/pym/portage/dep/dep_check.py b/pym/portage/dep/dep_check.py index 86112a236..07d6d1e6f 100644 --- a/pym/portage/dep/dep_check.py +++ b/pym/portage/dep/dep_check.py @@ -6,12 +6,14 @@ from __future__ import unicode_literals __all__ = ['dep_check', 'dep_eval', 'dep_wordreduce', 'dep_zapdeps'] import logging +import operator import portage from portage.dep import Atom, match_from_list, use_reduce from portage.exception import InvalidDependString, ParseError from portage.localization import _ from portage.util import writemsg, writemsg_level +from portage.util.SlotObject import SlotObject from portage.versions import vercmp, _pkg_str def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/", @@ -255,6 +257,10 @@ def dep_eval(deplist): return 0 return 1 +class _dep_choice(SlotObject): + __slots__ = ('atoms', 'slot_map', 'cp_map', 'all_available', + 'all_installed_slots') + def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): """ Takes an unreduced and reduced deplist and removes satisfied dependencies. @@ -382,7 +388,9 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): vercmp(avail_pkg.version, highest_cpv.version) > 0: cp_map[avail_pkg.cp] = avail_pkg - this_choice = (atoms, slot_map, cp_map, all_available) + this_choice = _dep_choice(atoms=atoms, slot_map=slot_map, + cp_map=cp_map, all_available=all_available, + all_installed_slots=False) if all_available: # The "all installed" criterion is not version or slot specific. # If any version of a package is already in the graph then we @@ -403,6 +411,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): not slot_atom.startswith("virtual/"): all_installed_slots = False break + this_choice.all_installed_slots = all_installed_slots if graph_db is None: if all_use_satisfied: if all_installed: @@ -505,6 +514,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): all_installed = False if all_installed: + this_choice.all_installed_slots = True other_installed.append(this_choice) elif some_installed: other_installed_some.append(this_choice) @@ -521,22 +531,23 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): for choices in choice_bins: if len(choices) < 2: continue + # Prefer choices with all_installed_slots for bug #480736. + choices.sort(key=operator.attrgetter('all_installed_slots'), + reverse=True) for choice_1 in choices[1:]: - atoms_1, slot_map_1, cp_map_1, all_available_1 = choice_1 - cps = set(cp_map_1) + cps = set(choice_1.cp_map) for choice_2 in choices: if choice_1 is choice_2: # choice_1 will not be promoted, so move on break - atoms_2, slot_map_2, cp_map_2, all_available_2 = choice_2 - intersecting_cps = cps.intersection(cp_map_2) + intersecting_cps = cps.intersection(choice_2.cp_map) if not intersecting_cps: continue has_upgrade = False has_downgrade = False for cp in intersecting_cps: - version_1 = cp_map_1[cp] - version_2 = cp_map_2[cp] + version_1 = choice_1.cp_map[cp] + version_2 = choice_2.cp_map[cp] difference = vercmp(version_1.version, version_2.version) if difference != 0: if difference > 0: @@ -553,9 +564,9 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): for allow_masked in (False, True): for choices in choice_bins: - for atoms, slot_map, cp_map, all_available in choices: + for choice in choices: if all_available or allow_masked: - return atoms + return choice.atoms assert(False) # This point should not be reachable diff --git a/pym/portage/tests/resolver/test_or_choices.py b/pym/portage/tests/resolver/test_or_choices.py index ca021123e..90e681408 100644 --- a/pym/portage/tests/resolver/test_or_choices.py +++ b/pym/portage/tests/resolver/test_or_choices.py @@ -77,3 +77,58 @@ class OrChoicesTestCase(TestCase): self.assertEqual(test_case.test_success, True, test_case.fail_msg) finally: playground.cleanup() + + def testOrChoicesLibpostproc(self): + ebuilds = { + "media-video/ffmpeg-0.10" : { + "EAPI": "5", + "SLOT": "0.10" + }, + "media-video/ffmpeg-1.2.2" : { + "EAPI": "5", + "SLOT": "0" + }, + "media-libs/libpostproc-0.8.0.20121125" : { + "EAPI": "5" + }, + "media-plugins/gst-plugins-ffmpeg-0.10.13_p201211-r1" : { + "EAPI": "5", + "RDEPEND" : "|| ( media-video/ffmpeg:0 media-libs/libpostproc )" + }, + } + + installed = { + "media-video/ffmpeg-0.10" : { + "EAPI": "5", + "SLOT": "0.10" + }, + "media-libs/libpostproc-0.8.0.20121125" : { + "EAPI": "5" + }, + "media-plugins/gst-plugins-ffmpeg-0.10.13_p201211-r1" : { + "EAPI": "5", + "RDEPEND" : "|| ( media-video/ffmpeg:0 media-libs/libpostproc )" + }, + } + + world = ["media-plugins/gst-plugins-ffmpeg"] + + test_cases = ( + # Demonstrate that libpostproc is preferred + # over ffmpeg:0 for bug #480736. + ResolverPlaygroundTestCase( + ["@world"], + options = {"--update": True, "--deep": True}, + success=True, + all_permutations = True, + mergelist = []), + ) + + playground = ResolverPlayground(ebuilds=ebuilds, installed=installed, + world=world, debug=False) + 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() -- 2.26.2