When there are autounmask USE changes, avoid unnecessary rebuilds by accepting binary packages that were rejected due to the preexisting USE configuration. This reuses the prune_rebuilds backtracker support which was added for bug 439688.
X-Gentoo-bug: 619626 X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=619626 --- [PATCH v2] * Ensure that it won't try to prune rebuilds more than once. * Simplify boolean logic at the beginning of the _ignored_binaries_autounmask_backtrack method, as suggested by Brian Dolbec. pym/_emerge/depgraph.py | 89 ++++++++++++++++++---- .../tests/resolver/test_autounmask_binpkg_use.py | 64 ++++++++++++++++ 2 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 pym/portage/tests/resolver/test_autounmask_binpkg_use.py diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 726835dd4..f47ce559c 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -5,6 +5,7 @@ from __future__ import division, print_function, unicode_literals import collections import errno +import functools import io import logging import stat @@ -856,17 +857,11 @@ class depgraph(object): for parent in self._forced_rebuilds[root][child]: writemsg_stdout(" %s\n" % (parent,), noiselevel=-1) - def _show_ignored_binaries(self): + def _eliminate_ignored_binaries(self): """ - Show binaries that have been ignored because their USE didn't - match the user's config. + Eliminate any package from self._dynamic_config.ignored_binaries + for which a more optimal alternative exists. """ - if not self._dynamic_config.ignored_binaries \ - or '--quiet' in self._frozen_config.myopts: - return - - ignored_binaries = {} - for pkg in list(self._dynamic_config.ignored_binaries): for selected_pkg in self._dynamic_config._package_tracker.match( @@ -885,10 +880,67 @@ class depgraph(object): self._dynamic_config.ignored_binaries.pop(pkg) break - else: - for reason, info in self._dynamic_config.\ - ignored_binaries[pkg].items(): - ignored_binaries.setdefault(reason, {})[pkg] = info + def _ignored_binaries_autounmask_backtrack(self): + """ + Check if there are ignored binaries that would have been + accepted with the current autounmask USE changes. + + @rtype: bool + @return: True if there are unnecessary rebuilds that + can be avoided by backtracking + """ + if not all([ + self._dynamic_config._allow_backtracking, + self._dynamic_config._needed_use_config_changes, + self._dynamic_config.ignored_binaries]): + return False + + self._eliminate_ignored_binaries() + + # _eliminate_ignored_binaries may have eliminated + # all of the ignored binaries + if not self._dynamic_config.ignored_binaries: + return False + + use_changes = collections.defaultdict( + functools.partial(collections.defaultdict, dict)) + for pkg, (new_use, changes) in self._dynamic_config._needed_use_config_changes.items(): + if pkg in self._dynamic_config.digraph: + use_changes[pkg.root][pkg.slot_atom] = (pkg, new_use) + + for pkg in self._dynamic_config.ignored_binaries: + selected_pkg, new_use = use_changes[pkg.root].get( + pkg.slot_atom, (None, None)) + if new_use is None: + continue + + if new_use != pkg.use.enabled: + continue + + if selected_pkg > pkg: + continue + + return True + + return False + + def _show_ignored_binaries(self): + """ + Show binaries that have been ignored because their USE didn't + match the user's config. + """ + if not self._dynamic_config.ignored_binaries \ + or '--quiet' in self._frozen_config.myopts: + return + + self._eliminate_ignored_binaries() + + ignored_binaries = {} + + for pkg in self._dynamic_config.ignored_binaries: + for reason, info in self._dynamic_config.\ + ignored_binaries[pkg].items(): + ignored_binaries.setdefault(reason, {})[pkg] = info if self._dynamic_config.myparams.get( "binpkg_respect_use") in ("y", "n"): @@ -4245,6 +4297,13 @@ class depgraph(object): self._dynamic_config._skip_restart = True return False, myfavorites + if (not self._dynamic_config._prune_rebuilds and + self._ignored_binaries_autounmask_backtrack()): + config = self._dynamic_config._backtrack_infos.setdefault("config", {}) + config["prune_rebuilds"] = True + self._dynamic_config._need_restart = True + return False, myfavorites + # Any failures except those due to autounmask *alone* should return # before this point, since the success_without_autounmask flag that's # set below is reserved for cases where there are *zero* other @@ -6224,10 +6283,10 @@ class depgraph(object): iuses = pkg.iuse.all old_use = self._pkg_use_enabled(pkg) if myeb: - pkgsettings.setcpv(myeb) + now_use = self._pkg_use_enabled(myeb) else: pkgsettings.setcpv(pkg) - now_use = pkgsettings["PORTAGE_USE"].split() + now_use = pkgsettings["PORTAGE_USE"].split() forced_flags = set() forced_flags.update(pkgsettings.useforce) forced_flags.update(pkgsettings.usemask) diff --git a/pym/portage/tests/resolver/test_autounmask_binpkg_use.py b/pym/portage/tests/resolver/test_autounmask_binpkg_use.py new file mode 100644 index 000000000..1ca4bf3d9 --- /dev/null +++ b/pym/portage/tests/resolver/test_autounmask_binpkg_use.py @@ -0,0 +1,64 @@ +# Copyright 2017 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground, ResolverPlaygroundTestCase + +class AutounmaskBinpkgUseTestCase(TestCase): + + def testAutounmaskBinpkgUse(self): + ebuilds = { + "dev-libs/A-1": { + "EAPI": "6", + "DEPEND": "dev-libs/B[foo]", + "RDEPEND": "dev-libs/B[foo]", + }, + "dev-libs/B-1": { + "EAPI": "6", + "IUSE": "foo", + }, + } + binpkgs = { + "dev-libs/A-1": { + "EAPI": "6", + "DEPEND": "dev-libs/B[foo]", + "RDEPEND": "dev-libs/B[foo]", + }, + "dev-libs/B-1": { + "EAPI": "6", + "IUSE": "foo", + "USE": "foo", + }, + } + installed = { + } + + test_cases = ( + # Bug 619626: Test for unnecessary rebuild due + # to rejection of binary packages that would + # be acceptable after appplication of autounmask + # USE changes. + ResolverPlaygroundTestCase( + ["dev-libs/A"], + all_permutations = True, + success = True, + options = { + "--usepkg": True, + }, + mergelist = [ + "[binary]dev-libs/B-1", + "[binary]dev-libs/A-1", + ], + use_changes = {"dev-libs/B-1": {"foo": True}} + ), + ) + + playground = ResolverPlayground(ebuilds=ebuilds, + binpkgs=binpkgs, installed=installed, 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.debug = False + playground.cleanup() -- 2.13.0