aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2020-01-25 17:44:14 -0800
committerZac Medico <zmedico@gentoo.org>2020-01-26 19:18:02 -0800
commitf7d83d75c6b05a16ef07473917082dbd0cd9955c (patch)
treee97c7d91e8cc7321222bf90e19edd8d9d88d260f
parentOrChoicesTestCase: split out bug 480736 libpostproc test case (diff)
downloadgentoo-portage-f7d83d75c6b05a16ef07473917082dbd0cd9955c.tar.xz
gentoo-portage-f7d83d75c6b05a16ef07473917082dbd0cd9955c.zip
dep_zapdeps: adjust || preference for slot upgrades (bug 706278)
Prefer choices that include a slot upgrade when appropriate, like for the || ( llvm:10 ... llvm:7 ) case reported in bug 706278. In order to avoid pulling in inappropriate slot upgrades, like those which should only be pulled in with --update and --deep, add a want_update flag to each choice which is True for choices that pull in a new slot for which an update is desirable. Mark the test case for bug 480736 as todo, since the "undesirable" slot upgrade which triggers a blocker conflict in this test case is practically indistinguishable from a desirable slot upgrade. This particular blocker conflict is no longer relevant, since current versions of media-libs/libpostproc are no longer compatible with any available media-video/ffmpeg slot. In order to solve this test case, some fancy backtracking (like for bug 382421) will be required. Bug: https://bugs.gentoo.org/706278 Bug: https://bugs.gentoo.org/480736 Signed-off-by: Zac Medico <zmedico@gentoo.org>
-rw-r--r--lib/_emerge/depgraph.py16
-rw-r--r--lib/portage/dep/dep_check.py79
-rw-r--r--lib/portage/tests/resolver/test_or_choices.py9
-rw-r--r--lib/portage/tests/resolver/test_or_upgrade_installed.py3
4 files changed, 67 insertions, 40 deletions
diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py
index bf8882774..cae1c4470 100644
--- a/lib/_emerge/depgraph.py
+++ b/lib/_emerge/depgraph.py
@@ -95,6 +95,14 @@ if sys.hexversion >= 0x3000000:
else:
_unicode = unicode
+# Exposes a depgraph interface to dep_check.
+_dep_check_graph_interface = collections.namedtuple('_dep_check_graph_interface',(
+ # Indicates a removal action, like depclean or prune.
+ 'removal_action',
+ # Checks if update is desirable for a given package.
+ 'want_update_pkg',
+))
+
class _scheduler_graph_config(object):
def __init__(self, trees, pkg_cache, graph, mergelist):
self.trees = trees
@@ -510,6 +518,10 @@ class _dynamic_depgraph_config(object):
soname_deps=depgraph._frozen_config.soname_deps_enabled)
# Track missed updates caused by solved conflicts.
self._conflict_missed_update = collections.defaultdict(dict)
+ dep_check_iface = _dep_check_graph_interface(
+ removal_action="remove" in myparams,
+ want_update_pkg=depgraph._want_update_pkg,
+ )
for myroot in depgraph._frozen_config.trees:
self.sets[myroot] = _depgraph_sets()
@@ -530,7 +542,7 @@ class _dynamic_depgraph_config(object):
self._graph_trees[myroot]["vartree"] = graph_tree
self._graph_trees[myroot]["graph_db"] = graph_tree.dbapi
self._graph_trees[myroot]["graph"] = self.digraph
- self._graph_trees[myroot]["want_update_pkg"] = depgraph._want_update_pkg
+ self._graph_trees[myroot]["graph_interface"] = dep_check_iface
self._graph_trees[myroot]["downgrade_probe"] = depgraph._downgrade_probe
def filtered_tree():
pass
@@ -558,7 +570,7 @@ class _dynamic_depgraph_config(object):
self._filtered_trees[myroot]["graph"] = self.digraph
self._filtered_trees[myroot]["vartree"] = \
depgraph._frozen_config.trees[myroot]["vartree"]
- self._filtered_trees[myroot]["want_update_pkg"] = depgraph._want_update_pkg
+ self._filtered_trees[myroot]["graph_interface"] = dep_check_iface
self._filtered_trees[myroot]["downgrade_probe"] = depgraph._downgrade_probe
dbs = []
diff --git a/lib/portage/dep/dep_check.py b/lib/portage/dep/dep_check.py
index 321d961dd..a7ae2cfa4 100644
--- a/lib/portage/dep/dep_check.py
+++ b/lib/portage/dep/dep_check.py
@@ -296,7 +296,7 @@ def dep_eval(deplist):
class _dep_choice(SlotObject):
__slots__ = ('atoms', 'slot_map', 'cp_map', 'all_available',
- 'all_installed_slots', 'new_slot_count')
+ 'all_installed_slots', 'new_slot_count', 'want_update', 'all_in_graph')
def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
minimize_slots=False):
@@ -331,9 +331,9 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
# c) contains masked installed packages
# d) is the first item
- preferred_installed = []
preferred_in_graph = []
- preferred_any_slot = []
+ preferred_installed = preferred_in_graph
+ preferred_any_slot = preferred_in_graph
preferred_non_installed = []
unsat_use_in_graph = []
unsat_use_installed = []
@@ -347,8 +347,6 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
# for correct ordering in cases like || ( foo[a] foo[b] ).
choice_bins = (
preferred_in_graph,
- preferred_installed,
- preferred_any_slot,
preferred_non_installed,
unsat_use_in_graph,
unsat_use_installed,
@@ -365,7 +363,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
graph_db = trees[myroot].get("graph_db")
graph = trees[myroot].get("graph")
pkg_use_enabled = trees[myroot].get("pkg_use_enabled")
- want_update_pkg = trees[myroot].get("want_update_pkg")
+ graph_interface = trees[myroot].get("graph_interface")
downgrade_probe = trees[myroot].get("downgrade_probe")
circular_dependency = trees[myroot].get("circular_dependency")
vardb = None
@@ -506,14 +504,24 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
if current_higher or (all_match_current and not all_match_previous):
cp_map[avail_pkg.cp] = avail_pkg
- new_slot_count = (len(slot_map) if graph_db is None else
- sum(not graph_db.match_pkgs(slot_atom) for slot_atom in slot_map
- if not slot_atom.cp.startswith("virtual/")))
+ want_update = False
+ if graph_interface is None or graph_interface.removal_action:
+ new_slot_count = len(slot_map)
+ else:
+ new_slot_count = 0
+ for slot_atom, avail_pkg in slot_map.items():
+ if graph_interface.want_update_pkg(parent, avail_pkg):
+ want_update = True
+ if (not slot_atom.cp.startswith("virtual/")
+ and not graph_db.match_pkgs(slot_atom)):
+ new_slot_count += 1
this_choice = _dep_choice(atoms=atoms, slot_map=slot_map,
cp_map=cp_map, all_available=all_available,
all_installed_slots=False,
- new_slot_count=new_slot_count)
+ new_slot_count=new_slot_count,
+ all_in_graph=False,
+ want_update=want_update)
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
@@ -567,6 +575,8 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
graph_db.match_pkgs(atom)):
all_in_graph = False
break
+ this_choice.all_in_graph = all_in_graph
+
circular_atom = None
if not (parent is None or priority is None) and \
(parent.onlydeps or
@@ -607,27 +617,8 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
elif all_installed:
if all_installed_slots:
preferred_installed.append(this_choice)
- elif parent is None or want_update_pkg is None:
- preferred_any_slot.append(this_choice)
else:
- # When appropriate, prefer a slot that is not
- # installed yet for bug #478188.
- want_update = True
- for slot_atom, avail_pkg in slot_map.items():
- if avail_pkg in graph:
- continue
- # New-style virtuals have zero cost to install.
- if slot_atom.startswith("virtual/") or \
- vardb.match(slot_atom):
- continue
- if not want_update_pkg(parent, avail_pkg):
- want_update = False
- break
-
- if want_update:
- preferred_installed.append(this_choice)
- else:
- preferred_any_slot.append(this_choice)
+ preferred_any_slot.append(this_choice)
else:
preferred_non_installed.append(this_choice)
else:
@@ -676,10 +667,6 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
if len(choices) < 2:
continue
- sort_keys = []
- # Prefer choices with all_installed_slots for bug #480736.
- sort_keys.append(lambda x: not x.all_installed_slots)
-
if minimize_slots:
# Prefer choices having fewer new slots. When used with DNF form,
# this can eliminate unecessary packages that depclean would
@@ -694,15 +681,35 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
# contribute to outcomes that appear to be random. Meanwhile,
# the order specified in the ebuild is without variance, so it
# does not have this problem.
- sort_keys.append(lambda x: x.new_slot_count)
+ choices.sort(key=operator.attrgetter('new_slot_count'))
- choices.sort(key=lambda x: tuple(f(x) for f in sort_keys))
for choice_1 in choices[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
+ if (
+ # For removal actions, prefer choices where all packages
+ # have been pulled into the graph.
+ (graph_interface and graph_interface.removal_action and
+ choice_1.all_in_graph and not choice_2.all_in_graph)
+
+ # Prefer choices where all_installed_slots is True, except
+ # in cases where we want to upgrade to a new slot as in
+ # bug 706278. Don't compare new_slot_count here since that
+ # would aggressively override the preference order defined
+ # in the ebuild, breaking the test case for bug 645002.
+ or (choice_1.all_installed_slots and
+ not choice_2.all_installed_slots and
+ not choice_2.want_update)
+ ):
+ # promote choice_1 in front of choice_2
+ choices.remove(choice_1)
+ index_2 = choices.index(choice_2)
+ choices.insert(index_2, choice_1)
+ break
+
intersecting_cps = cps.intersection(choice_2.cp_map)
if not intersecting_cps:
continue
diff --git a/lib/portage/tests/resolver/test_or_choices.py b/lib/portage/tests/resolver/test_or_choices.py
index c0316bfb3..a50ad0151 100644
--- a/lib/portage/tests/resolver/test_or_choices.py
+++ b/lib/portage/tests/resolver/test_or_choices.py
@@ -288,6 +288,15 @@ class OrChoicesTestCase(TestCase):
class OrChoicesLibpostprocTestCase(TestCase):
def testOrChoicesLibpostproc(self):
+ # This test case is expected to fail after the fix for bug 706278,
+ # since the "undesirable" slot upgrade which triggers a blocker conflict
+ # in this test case is practically indistinguishable from a desirable
+ # slot upgrade. This particular blocker conflict is no longer relevant,
+ # since current versions of media-libs/libpostproc are no longer
+ # compatible with any available media-video/ffmpeg slot. In order to
+ # solve this test case, some fancy backtracking (like for bug 382421)
+ # will be required.
+ self.todo = True
ebuilds = {
"media-video/ffmpeg-0.10" : {
diff --git a/lib/portage/tests/resolver/test_or_upgrade_installed.py b/lib/portage/tests/resolver/test_or_upgrade_installed.py
index c3efebf55..3889d53dc 100644
--- a/lib/portage/tests/resolver/test_or_upgrade_installed.py
+++ b/lib/portage/tests/resolver/test_or_upgrade_installed.py
@@ -213,8 +213,7 @@ class OrUpgradeInstalledTestCase(TestCase):
['@world'],
options={'--update': True, '--deep': True},
success=True,
- mergelist=[],
- #mergelist=['sys-devel/llvm-9'],
+ mergelist=['sys-devel/llvm-9', 'media-libs/mesa-19.2.8'],
),
)