* [gentoo-commits] proj/portage:master commit in: man/, pym/_emerge/, pym/portage/dep/, pym/portage/tests/resolver/
@ 2015-01-15 1:05 Zac Medico
0 siblings, 0 replies; only message in thread
From: Zac Medico @ 2015-01-15 1:05 UTC (permalink / raw
To: gentoo-commits
commit: e99fa094ac73514b23509a0f8305b365f114e9a3
Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Mon Dec 22 23:07:12 2014 +0000
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Mon Jan 12 17:00:16 2015 +0000
URL: http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=e99fa094
emerge: add --changed-deps/--binpkg-changed-deps (282927)
The @changed-deps set is useful, but it has limitations similar to the
@installed set (see bug #387059), which can make it unsuitable for use
when updating the whole system. Therefore, implement two new options
that are analogous to --newuse and --binpkg-respect-use, called
--changed-deps and --binpkg-changed-deps.
The rationale for having a separate --binpkg-* option is the same in
both cases: depending on the situation, people may want different
behavior for binary packages. For example, just like
---binpkg-respect-use is automatically enabled if the user has not
specified --usepkgonly, so is --binpkg-changed-deps (though the user
can explicitly override the automatic behavior). In both cases,
inconsistencies in dependencies are automatically avoided, increasing
the probability of a successful dependency calculation.
X-Gentoo-Bug: 282927
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=282927
---
man/emerge.1 | 22 +++-
pym/_emerge/create_depgraph_params.py | 16 +++
pym/_emerge/depgraph.py | 138 ++++++++++++++++++++++--
pym/_emerge/main.py | 26 +++++
pym/portage/dep/_slot_operator.py | 13 +++
pym/portage/tests/resolver/test_changed_deps.py | 120 +++++++++++++++++++++
6 files changed, 323 insertions(+), 12 deletions(-)
diff --git a/man/emerge.1 b/man/emerge.1
index 4636997..aea7cae 100644
--- a/man/emerge.1
+++ b/man/emerge.1
@@ -386,9 +386,20 @@ Specifies an integer number of times to backtrack if
dependency calculation fails due to a conflict or an
unsatisfied dependency (default: \'10\').
.TP
+.BR "\-\-binpkg\-changed\-deps [ y | n ]"
+Tells emerge to ignore binary packages for which the corresponding
+ebuild dependencies have changed since the packages were built.
+In order to help avoid issues with resolving inconsistent dependencies,
+this option is automatically enabled unless the \fB\-\-usepkgonly\fR
+option is enabled. Behavior with respect to changed build\-time
+dependencies is controlled by the \fB\-\-with\-bdeps\fR option.
+.TP
.BR "\-\-binpkg\-respect\-use [ y | n ]"
-Tells emerge to ignore binary packages if their use flags
-don't match the current configuration. (default: \'n\')
+Tells emerge to ignore binary packages if their USE flags
+don't match the current configuration. In order to help avoid issues
+with resolving inconsistent USE flag settings, this option is
+automatically enabled unless the \fB\-\-usepkgonly\fR option
+is enabled.
.TP
.BR "\-\-buildpkg [ y | n ] (\-b short option)"
Tells emerge to build binary packages for all ebuilds processed in
@@ -412,6 +423,13 @@ Creates binary packages for all ebuilds processed without actually
merging the packages. This comes with the caveat that all build-time
dependencies must already be emerged on the system.
.TP
+.BR "\-\-changed\-deps [ y | n ]"
+Tells emerge to replace installed packages for which the corresponding
+ebuild dependencies have changed since the packages were built. This
+option also implies the \fB\-\-selective\fR option. Behavior with
+respect to changed build\-time dependencies is controlled by the
+\fB\-\-with\-bdeps\fR option.
+.TP
.BR "\-\-changed\-use " (\fB\-U\fR)
Tells emerge to include installed packages where USE flags have
changed since installation. This option also implies the
diff --git a/pym/_emerge/create_depgraph_params.py b/pym/_emerge/create_depgraph_params.py
index 6f74de7..11e20f4 100644
--- a/pym/_emerge/create_depgraph_params.py
+++ b/pym/_emerge/create_depgraph_params.py
@@ -22,6 +22,8 @@ def create_depgraph_params(myopts, myaction):
# ignore_built_slot_operator_deps: ignore the slot/sub-slot := operator parts
# of dependencies that have been recorded when packages where built
# with_test_deps: pull in test deps for packages matched by arguments
+ # changed_deps: rebuild installed packages with outdated deps
+ # binpkg_changed_deps: reject binary packages with outdated deps
myparams = {"recurse" : True}
bdeps = myopts.get("--with-bdeps")
@@ -51,6 +53,7 @@ def create_depgraph_params(myopts, myaction):
"--newuse" in myopts or \
"--reinstall" in myopts or \
"--noreplace" in myopts or \
+ myopts.get("--changed-deps", "n") != "n" or \
myopts.get("--selective", "n") != "n":
myparams["selective"] = True
@@ -99,6 +102,19 @@ def create_depgraph_params(myopts, myaction):
# have been specified.
myparams['binpkg_respect_use'] = 'auto'
+ binpkg_changed_deps = myopts.get('--binpkg-changed-deps')
+ if binpkg_changed_deps is not None:
+ myparams['binpkg_changed_deps'] = binpkg_changed_deps
+ elif '--usepkgonly' not in myopts:
+ # In order to avoid dependency resolution issues due to changed
+ # dependencies, enable this automatically, as long as it doesn't
+ # strongly conflict with other options that have been specified.
+ myparams['binpkg_changed_deps'] = 'auto'
+
+ changed_deps = myopts.get('--changed-deps')
+ if changed_deps is not None:
+ myparams['changed_deps'] = changed_deps
+
if myopts.get("--selective") == "n":
# --selective=n can be used to remove selective
# behavior that may have been implied by some
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 41d6c60..1431779 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -24,7 +24,8 @@ from portage.dbapi._similar_name_search import similar_name_search
from portage.dep import Atom, best_match_to_list, extract_affecting_use, \
check_required_use, human_readable_required_use, match_from_list, \
_repo_separator
-from portage.dep._slot_operator import ignore_built_slot_operator_deps
+from portage.dep._slot_operator import (ignore_built_slot_operator_deps,
+ strip_slots)
from portage.eapi import eapi_has_strong_blocks, eapi_has_required_use, \
_get_eapi_attrs
from portage.exception import (InvalidAtom, InvalidData, InvalidDependString,
@@ -796,14 +797,12 @@ class depgraph(object):
match the user's config.
"""
if not self._dynamic_config.ignored_binaries \
- or '--quiet' in self._frozen_config.myopts \
- or self._dynamic_config.myparams.get(
- "binpkg_respect_use") in ("y", "n"):
+ or '--quiet' in self._frozen_config.myopts:
return
- for pkg in list(self._dynamic_config.ignored_binaries):
+ ignored_binaries = {}
- selected_pkg = list()
+ for pkg in list(self._dynamic_config.ignored_binaries):
for selected_pkg in self._dynamic_config._package_tracker.match(
pkg.root, pkg.slot_atom):
@@ -821,15 +820,38 @@ class depgraph(object):
self._dynamic_config.ignored_binaries.pop(pkg)
break
- if not self._dynamic_config.ignored_binaries:
+ else:
+ 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"):
+ ignored_binaries.pop("respect_use", None)
+
+ if self._dynamic_config.myparams.get(
+ "binpkg_changed_deps") in ("y", "n"):
+ ignored_binaries.pop("changed_deps", None)
+
+ if not ignored_binaries:
return
self._show_merge_list()
+ if ignored_binaries.get("respect_use"):
+ self._show_ignored_binaries_respect_use(
+ ignored_binaries["respect_use"])
+
+ if ignored_binaries.get("changed_deps"):
+ self._show_ignored_binaries_changed_deps(
+ ignored_binaries["changed_deps"])
+
+ def _show_ignored_binaries_respect_use(self, respect_use):
+
writemsg("\n!!! The following binary packages have been ignored " + \
"due to non matching USE:\n\n", noiselevel=-1)
- for pkg, flags in self._dynamic_config.ignored_binaries.items():
+ for pkg, flags in respect_use.items():
flag_display = []
for flag in sorted(flags):
if flag not in pkg.use.enabled:
@@ -854,6 +876,30 @@ class depgraph(object):
line = colorize("INFORM", line)
writemsg(line + "\n", noiselevel=-1)
+ def _show_ignored_binaries_changed_deps(self, changed_deps):
+
+ writemsg("\n!!! The following binary packages have been "
+ "ignored due to changed dependencies:\n\n",
+ noiselevel=-1)
+
+ for pkg in changed_deps:
+ msg = " %s%s%s" % (pkg.cpv, _repo_separator, pkg.repo)
+ if pkg.root_config.settings["ROOT"] != "/":
+ msg += " for %s" % pkg.root
+ writemsg("%s\n" % msg, noiselevel=-1)
+
+ msg = [
+ "",
+ "NOTE: The --binpkg-changed-deps=n option will prevent emerge",
+ " from ignoring these binary packages if possible.",
+ " Using --binpkg-changed-deps=y will silence this warning."
+ ]
+
+ for line in msg:
+ if line:
+ line = colorize("INFORM", line)
+ writemsg(line + "\n", noiselevel=-1)
+
def _get_missed_updates(self):
# In order to minimize noise, show only the highest
@@ -2196,6 +2242,52 @@ class depgraph(object):
return flags
return None
+ def _changed_deps(self, pkg):
+
+ ebuild = None
+ try:
+ ebuild = self._pkg(pkg.cpv, "ebuild",
+ pkg.root_config, myrepo=pkg.repo)
+ except PackageNotFound:
+ # Use first available instance of the same version.
+ for ebuild in self._iter_match_pkgs(
+ pkg.root_config, "ebuild", Atom("=" + pkg.cpv)):
+ break
+
+ if ebuild is None:
+ changed = False
+ else:
+ if self._dynamic_config.myparams.get("bdeps", "n") == "y":
+ depvars = Package._dep_keys
+ else:
+ depvars = Package._runtime_keys
+
+ # Use _raw_metadata, in order to avoid interaction
+ # with --dynamic-deps.
+ try:
+ built_deps = []
+ for k in depvars:
+ dep_struct = portage.dep.use_reduce(
+ pkg._raw_metadata[k], uselist=pkg.use.enabled,
+ eapi=pkg.eapi, token_class=Atom)
+ strip_slots(dep_struct)
+ built_deps.append(dep_struct)
+ except InvalidDependString:
+ changed = True
+ else:
+ unbuilt_deps = []
+ for k in depvars:
+ dep_struct = portage.dep.use_reduce(
+ ebuild._raw_metadata[k],
+ uselist=pkg.use.enabled,
+ eapi=ebuild.eapi, token_class=Atom)
+ strip_slots(dep_struct)
+ unbuilt_deps.append(dep_struct)
+
+ changed = built_deps != unbuilt_deps
+
+ return changed
+
def _create_graph(self, allow_unsatisfied=False):
dep_stack = self._dynamic_config._dep_stack
dep_disjunctive_stack = self._dynamic_config._dep_disjunctive_stack
@@ -4618,8 +4710,14 @@ class depgraph(object):
mreasons = ["need to rebuild from source"]
elif pkg.installed and root_slot in self._rebuild.reinstall_list:
mreasons = ["need to rebuild from source"]
- elif pkg.built and not mreasons:
+ elif (pkg.built and not mreasons and
+ self._dynamic_config.ignored_binaries.get(
+ pkg, {}).get("respect_use")):
mreasons = ["use flag configuration mismatch"]
+ elif (pkg.built and not mreasons and
+ self._dynamic_config.ignored_binaries.get(
+ pkg, {}).get("changed_deps")):
+ mreasons = ["changed deps"]
masked_packages.append(
(root_config, pkgsettings, cpv, repo, metadata, mreasons))
@@ -5716,6 +5814,12 @@ class depgraph(object):
# reject the built package if necessary.
reinstall_use = ("--newuse" in self._frozen_config.myopts or \
"--reinstall" in self._frozen_config.myopts)
+ changed_deps = (
+ self._dynamic_config.myparams.get(
+ "changed_deps", "n") != "n")
+ binpkg_changed_deps = (
+ self._dynamic_config.myparams.get(
+ "binpkg_changed_deps", "n") != "n")
respect_use = self._dynamic_config.myparams.get("binpkg_respect_use") in ("y", "auto")
if built and not useoldpkg and \
(not installed or matched_packages) and \
@@ -5742,8 +5846,22 @@ class depgraph(object):
forced_flags, old_use, iuses, now_use, cur_iuse)
if reinstall_for_flags:
if not pkg.installed:
- self._dynamic_config.ignored_binaries.setdefault(pkg, set()).update(reinstall_for_flags)
+ self._dynamic_config.\
+ ignored_binaries.setdefault(
+ pkg, {}).setdefault(
+ "respect_use", set()).update(
+ reinstall_for_flags)
break
+
+ if (((installed and changed_deps) or
+ (not installed and binpkg_changed_deps)) and
+ self._changed_deps(pkg)):
+ if not installed:
+ self._dynamic_config.\
+ ignored_binaries.setdefault(
+ pkg, {})["changed_deps"] = True
+ break
+
# Compare current config to installed package
# and do not reinstall if possible.
if not installed and not useoldpkg and cpv in vardb.match(atom):
diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py
index 3fcfcbf..5d5e936 100644
--- a/pym/_emerge/main.py
+++ b/pym/_emerge/main.py
@@ -130,7 +130,9 @@ def insert_optional_args(args):
'--autounmask-keep-masks': y_or_n,
'--autounmask-unrestricted-atoms' : y_or_n,
'--autounmask-write' : y_or_n,
+ '--binpkg-changed-deps' : y_or_n,
'--buildpkg' : y_or_n,
+ '--changed-deps' : y_or_n,
'--complete-graph' : y_or_n,
'--deep' : valid_integers,
'--depclean-lib-check' : y_or_n,
@@ -354,6 +356,12 @@ def parse_opts(tmpcmdline, silent=False):
"action" : "store"
},
+ "--binpkg-changed-deps": {
+ "help" : ("reject binary packages with outdated "
+ "dependencies"),
+ "choices" : true_y_or_n
+ },
+
"--buildpkg": {
"shortopt" : "-b",
"help" : "build binary packages",
@@ -368,6 +376,12 @@ def parse_opts(tmpcmdline, silent=False):
"action" : "append"
},
+ "--changed-deps": {
+ "help" : ("replace installed packages with "
+ "outdated dependencies"),
+ "choices" : true_y_or_n
+ },
+
"--config-root": {
"help":"specify the location for portage configuration files",
"action":"store"
@@ -730,6 +744,12 @@ def parse_opts(tmpcmdline, silent=False):
if myoptions.autounmask_write in true_y:
myoptions.autounmask_write = True
+ if myoptions.binpkg_changed_deps is not None:
+ if myoptions.binpkg_changed_deps in true_y:
+ myoptions.binpkg_changed_deps = 'y'
+ else:
+ myoptions.binpkg_changed_deps = 'n'
+
if myoptions.buildpkg in true_y:
myoptions.buildpkg = True
@@ -739,6 +759,12 @@ def parse_opts(tmpcmdline, silent=False):
parser.error("Invalid Atom(s) in --buildpkg-exclude parameter: '%s'\n" % \
(",".join(bad_atoms),))
+ if myoptions.changed_deps is not None:
+ if myoptions.changed_deps in true_y:
+ myoptions.changed_deps = 'y'
+ else:
+ myoptions.changed_deps = 'n'
+
if myoptions.changed_use is not False:
myoptions.reinstall = "changed-use"
myoptions.changed_use = False
diff --git a/pym/portage/dep/_slot_operator.py b/pym/portage/dep/_slot_operator.py
index 8b67fc5..8ce570d 100644
--- a/pym/portage/dep/_slot_operator.py
+++ b/pym/portage/dep/_slot_operator.py
@@ -8,6 +8,19 @@ from portage.eapi import _get_eapi_attrs
from portage.exception import InvalidData
from _emerge.Package import Package
+def strip_slots(dep_struct):
+ """
+ Search dep_struct for any slot := operators and remove the
+ slot/sub-slot part, while preserving the operator. The result
+ is suitable for --changed-deps comparisons.
+ """
+ for i, x in enumerate(dep_struct):
+ if isinstance(x, list):
+ strip_slots(x)
+ elif (isinstance(x, Atom) and
+ x.slot_operator == "=" and x.slot is not None):
+ dep_struct[i] = x.with_slot("=")
+
def find_built_slot_operator_atoms(pkg):
atoms = {}
for k in Package._dep_keys:
diff --git a/pym/portage/tests/resolver/test_changed_deps.py b/pym/portage/tests/resolver/test_changed_deps.py
new file mode 100644
index 0000000..2421c53
--- /dev/null
+++ b/pym/portage/tests/resolver/test_changed_deps.py
@@ -0,0 +1,120 @@
+# Copyright 2014 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 ChangedDepsTestCase(TestCase):
+
+ def testChangedDeps(self):
+
+ ebuilds = {
+ "app-misc/A-0": {
+ "DEPEND": "app-misc/B",
+ "RDEPEND": "app-misc/B",
+ },
+ "app-misc/B-0": {
+ }
+ }
+
+ binpkgs = {
+ "app-misc/A-0": {},
+ }
+
+ installed = {
+ "app-misc/A-0": {},
+ }
+
+ world= (
+ "app-misc/A",
+ )
+
+ test_cases = (
+
+ # --dynamic-deps=n causes the original deps to be respected
+ ResolverPlaygroundTestCase(
+ ["@world"],
+ success = True,
+ options = {
+ "--update": True,
+ "--deep": True,
+ "--dynamic-deps": "n",
+ "--usepkg": True,
+ },
+ mergelist = []
+ ),
+
+ # --dynamic-deps causes app-misc/B to get pulled in
+ ResolverPlaygroundTestCase(
+ ["@world"],
+ success = True,
+ options = {
+ "--update": True,
+ "--deep": True,
+ "--usepkg": True,
+ },
+ mergelist = ["app-misc/B-0"]
+ ),
+
+ # --changed-deps causes app-misc/A to be rebuilt
+ ResolverPlaygroundTestCase(
+ ["@world"],
+ success = True,
+ options = {
+ "--update": True,
+ "--deep": True,
+ "--changed-deps": "y",
+ "--usepkg": True,
+ },
+ mergelist = ["app-misc/B-0", "app-misc/A-0"]
+ ),
+
+ # --usepkgonly prevents automatic --binpkg-changed-deps
+ ResolverPlaygroundTestCase(
+ ["app-misc/A"],
+ success = True,
+ options = {
+ "--changed-deps": "y",
+ "--usepkgonly": True,
+ },
+ mergelist = ["[binary]app-misc/A-0"]
+ ),
+
+ # Test automatic --binpkg-changed-deps, which cases the
+ # binpkg with stale deps to be ignored (with warning
+ # message)
+ ResolverPlaygroundTestCase(
+ ["app-misc/A"],
+ success = True,
+ options = {
+ "--usepkg": True,
+ },
+ mergelist = ["app-misc/B-0", "app-misc/A-0"]
+ ),
+ )
+ test_cases = (
+
+ # Forcibly disable --binpkg-changed-deps, which causes
+ # --changed-deps to be overridden by --binpkg-changed-deps
+ ResolverPlaygroundTestCase(
+ ["app-misc/A"],
+ success = True,
+ options = {
+ "--binpkg-changed-deps": "n",
+ "--changed-deps": "y",
+ "--usepkg": True,
+ },
+ mergelist = ["[binary]app-misc/A-0"]
+ ),
+ )
+
+ playground = ResolverPlayground(debug=False, ebuilds=ebuilds,
+ binpkgs=binpkgs, 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()
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2015-01-15 1:05 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-01-15 1:05 [gentoo-commits] proj/portage:master commit in: man/, pym/_emerge/, pym/portage/dep/, pym/portage/tests/resolver/ Zac Medico
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox