commit: 60af7e2696b96b47b0cd9e70caabd10546206b8b Author: Zac Medico <zmedico <AT> gentoo <DOT> org> AuthorDate: Mon May 29 08:22:40 2017 +0000 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> CommitDate: Fri Jun 2 05:38:02 2017 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=60af7e26
depgraph: prune unnecessary rebuilds for --autounmask-continue (bug 619626) 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 Acked-by: Brian Dolbec <dolsen <AT> gentoo.org> pym/_emerge/depgraph.py | 96 ++++++++++++++++++---- .../tests/resolver/test_autounmask_binpkg_use.py | 64 +++++++++++++++ 2 files changed, 142 insertions(+), 18 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 2dc432431..abe2cb1bd 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( @@ -894,10 +889,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"): @@ -4254,6 +4306,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 @@ -6233,13 +6292,14 @@ 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) + forced_flags = set(chain( + myeb.use.force, myeb.use.mask)) else: pkgsettings.setcpv(pkg) - now_use = pkgsettings["PORTAGE_USE"].split() - forced_flags = set() - forced_flags.update(pkgsettings.useforce) - forced_flags.update(pkgsettings.usemask) + now_use = pkgsettings["PORTAGE_USE"].split() + forced_flags = set(chain( + pkgsettings.useforce, pkgsettings.usemask)) cur_iuse = iuses if myeb and not usepkgonly and not useoldpkg: cur_iuse = myeb.iuse.all 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()