From: "Zac Medico" <zmedico@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/portage:master commit in: pym/portage/tests/resolver/, pym/_emerge/
Date: Thu, 11 Sep 2014 21:37:12 +0000 (UTC) [thread overview]
Message-ID: <1410471368.336ab90212c80ce9548362bf4fbdafd388c3515c.zmedico@gentoo> (raw)
commit: 336ab90212c80ce9548362bf4fbdafd388c3515c
Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Sun Sep 7 05:19:46 2014 +0000
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Thu Sep 11 21:36:08 2014 +0000
URL: http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=336ab902
depgraph._add_dep: fix bug #520950
This handles a case which occurs when
_solve_non_slot_operator_slot_conflicts calls _create_graph. In this
case, ignore unsatisfied deps for installed packages only if their depth
is beyond the depth requested by the user and the dep was initially
unsatisfied (not broken by a slot conflict in the current graph).
Since depth is meaningless for packages that are not reachable as deep
dependencies of arguments, the _UNREACHABLE_DEPTH constant is used as
the depth value for any packages added via _complete_graph. Also, any
sets added via _complete_graph have their reset_depth attribute set to
False.
The sys.stderr -> writemsg changes are necessary to ensure that the test
cases do not output unwanted error messages.
X-Gentoo-Bug: 520950
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=520950
---
pym/_emerge/depgraph.py | 128 ++++++++++++++++-----
pym/portage/tests/resolver/ResolverPlayground.py | 11 +-
.../test_slot_conflict_unsatisfied_deep_deps.py | 115 ++++++++++++++++++
3 files changed, 226 insertions(+), 28 deletions(-)
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index d6cd24d..cc87d9f 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -107,7 +107,7 @@ def _wildcard_set(atoms):
class _frozen_depgraph_config(object):
- def __init__(self, settings, trees, myopts, spinner):
+ def __init__(self, settings, trees, myopts, params, spinner):
self.settings = settings
self.target_root = settings["EROOT"]
self.myopts = myopts
@@ -115,6 +115,7 @@ class _frozen_depgraph_config(object):
if settings.get("PORTAGE_DEBUG", "") == "1":
self.edebug = 1
self.spinner = spinner
+ self.requested_depth = params.get("deep", 0)
self._running_root = trees[trees._running_eroot]["root_config"]
self.pkgsettings = {}
self.trees = {}
@@ -502,13 +503,18 @@ class _dynamic_depgraph_config(object):
class depgraph(object):
+ # Represents the depth of a node that is unreachable from explicit
+ # user arguments (or their deep dependencies). Such nodes are pulled
+ # in by the _complete_graph method.
+ _UNREACHABLE_DEPTH = object()
+
pkg_tree_map = RootConfig.pkg_tree_map
def __init__(self, settings, trees, myopts, myparams, spinner,
frozen_config=None, backtrack_parameters=BacktrackParameter(), allow_backtracking=False):
if frozen_config is None:
frozen_config = _frozen_depgraph_config(settings, trees,
- myopts, spinner)
+ myopts, myparams, spinner)
self._frozen_config = frozen_config
self._dynamic_config = _dynamic_depgraph_config(self, myparams,
allow_backtracking, backtrack_parameters)
@@ -2095,6 +2101,13 @@ class depgraph(object):
arg = arg_stack.pop()
if arg in traversed_set_args:
continue
+
+ # If a node with the same hash already exists in
+ # the digraph, preserve the existing instance which
+ # may have a different reset_depth attribute
+ # (distiguishes user arguments from sets added for
+ # another reason such as complete mode).
+ arg = self._dynamic_config.digraph.get(arg, arg)
traversed_set_args.add(arg)
if add_to_digraph:
@@ -2114,8 +2127,16 @@ class depgraph(object):
if nested_set is None:
nested_set = root_config.sets.get(s)
if nested_set is not None:
+ # Propagate the reset_depth attribute from
+ # parent set to nested set.
nested_arg = SetArg(arg=token, pset=nested_set,
+ reset_depth=arg.reset_depth,
root_config=root_config)
+
+ # Preserve instances already in the graph (same
+ # reason as for the "arg" variable above).
+ nested_arg = self._dynamic_config.digraph.get(
+ nested_arg, nested_arg)
arg_stack.append(nested_arg)
if add_to_digraph:
self._dynamic_config.digraph.add(nested_arg, arg,
@@ -2164,9 +2185,42 @@ class depgraph(object):
dep.collapsed_priority.ignored):
# This is an unnecessary build-time dep.
return 1
+
+ # NOTE: For removal actions, allow_unsatisfied is always
+ # True since all existing removal actions traverse all
+ # installed deps deeply via the _complete_graph method,
+ # which calls _create_graph with allow_unsatisfied = True.
if allow_unsatisfied:
self._dynamic_config._unsatisfied_deps.append(dep)
return 1
+
+ # The following case occurs when
+ # _solve_non_slot_operator_slot_conflicts calls
+ # _create_graph. In this case, ignore unsatisfied deps for
+ # installed packages only if their depth is beyond the depth
+ # requested by the user and the dep was initially
+ # unsatisfied (not broken by a slot conflict in the current
+ # graph). See bug #520950.
+ # NOTE: The value of dep.parent.depth is guaranteed to be
+ # either an integer or _UNREACHABLE_DEPTH, where
+ # _UNREACHABLE_DEPTH indicates that the parent has been
+ # pulled in by the _complete_graph method (rather than by
+ # explicit arguments or their deep dependencies). These
+ # cases must be distinguished because depth is meaningless
+ # for packages that are not reachable as deep dependencies
+ # of arguments.
+ if (self._dynamic_config._complete_mode and
+ isinstance(dep.parent, Package) and
+ dep.parent.installed and
+ (dep.parent.depth is self._UNREACHABLE_DEPTH or
+ (self._frozen_config.requested_depth is not True and
+ dep.parent.depth >= self._frozen_config.requested_depth))):
+ inst_pkg, in_graph = \
+ self._select_pkg_from_installed(dep.root, dep.atom)
+ if inst_pkg is None:
+ self._dynamic_config._initially_unsatisfied_deps.append(dep)
+ return 1
+
self._dynamic_config._unsatisfied_deps_for_display.append(
((dep.root, dep.atom), {"myparent":dep.parent}))
@@ -2411,14 +2465,23 @@ class depgraph(object):
# Installing package A, we need to make sure package A's deps are met.
# emerge --deep <pkgspec>; we need to recursively check dependencies of pkgspec
# If we are in --nodeps (no recursion) mode, we obviously only check 1 level of dependencies.
- if arg_atoms and depth > 0:
+ if arg_atoms and depth != 0:
for parent, atom in arg_atoms:
if parent.reset_depth:
depth = 0
break
- if previously_added and pkg.depth is not None:
- depth = min(pkg.depth, depth)
+ if previously_added and depth != 0 and \
+ isinstance(pkg.depth, int):
+ # Use pkg.depth if it is less than depth.
+ if isinstance(depth, int):
+ depth = min(pkg.depth, depth)
+ else:
+ # depth is _UNREACHABLE_DEPTH and pkg.depth is
+ # an int, so use the int because it's considered
+ # to be less than _UNREACHABLE_DEPTH.
+ depth = pkg.depth
+
pkg.depth = depth
deep = self._dynamic_config.myparams.get("deep", 0)
update = "--update" in self._frozen_config.myopts
@@ -2716,7 +2779,11 @@ class depgraph(object):
def _wrapped_add_pkg_dep_string(self, pkg, dep_root, dep_priority,
dep_string, allow_unsatisfied):
- depth = pkg.depth + 1
+ if isinstance(pkg.depth, int):
+ depth = pkg.depth + 1
+ else:
+ depth = pkg.depth
+
deep = self._dynamic_config.myparams.get("deep", 0)
recurse_satisfied = deep is True or depth <= deep
debug = "--debug" in self._frozen_config.myopts
@@ -3544,9 +3611,9 @@ class depgraph(object):
if not self._add_pkg(arg.package, dep) or \
not self._create_graph():
if not self.need_restart():
- sys.stderr.write(("\n\n!!! Problem " + \
+ writemsg(("\n\n!!! Problem " + \
"resolving dependencies for %s\n") % \
- arg.arg)
+ arg.arg, noiselevel=-1)
return 0, myfavorites
continue
if debug:
@@ -3947,10 +4014,14 @@ class depgraph(object):
# Recursively traversed virtual dependencies, and their
# direct dependencies, are considered to have the same
# depth as direct dependencies.
- if parent.depth is None:
- virt_depth = None
- else:
+ if isinstance(parent.depth, int):
virt_depth = parent.depth + 1
+ else:
+ # The depth may be None when called via
+ # _select_atoms_probe, or it may be
+ # _UNREACHABLE_DEPTH for complete mode.
+ virt_depth = parent.depth
+
chosen_atom_ids = frozenset(id(atom) for atom in mycheck[1])
selected_atoms = OrderedDict()
node_stack = [(parent, None, None)]
@@ -5833,14 +5904,14 @@ class depgraph(object):
pset = root_config.sets[s]
atom = SETPREFIX + s
args.append(SetArg(arg=atom, pset=pset,
- root_config=root_config))
+ reset_depth=False, root_config=root_config))
self._set_args(args)
for arg in self._expand_set_args(args, add_to_digraph=True):
for atom in arg.pset.getAtoms():
self._dynamic_config._dep_stack.append(
Dependency(atom=atom, root=arg.root_config.root,
- parent=arg))
+ parent=arg, depth=self._UNREACHABLE_DEPTH))
if True:
if self._dynamic_config._ignored_deps:
@@ -7786,18 +7857,23 @@ class depgraph(object):
break
if world_problems:
- sys.stderr.write("\n!!! Problems have been " + \
- "detected with your world file\n")
- sys.stderr.write("!!! Please run " + \
- green("emaint --check world")+"\n\n")
+ writemsg("\n!!! Problems have been " + \
+ "detected with your world file\n",
+ noiselevel=-1)
+ writemsg("!!! Please run " + \
+ green("emaint --check world")+"\n\n",
+ noiselevel=-1)
if self._dynamic_config._missing_args:
- sys.stderr.write("\n" + colorize("BAD", "!!!") + \
- " Ebuilds for the following packages are either all\n")
- sys.stderr.write(colorize("BAD", "!!!") + \
- " masked or don't exist:\n")
- sys.stderr.write(" ".join(str(atom) for arg, atom in \
- self._dynamic_config._missing_args) + "\n")
+ writemsg("\n" + colorize("BAD", "!!!") + \
+ " Ebuilds for the following packages are either all\n",
+ noiselevel=-1)
+ writemsg(colorize("BAD", "!!!") + \
+ " masked or don't exist:\n",
+ noiselevel=-1)
+ writemsg(" ".join(str(atom) for arg, atom in \
+ self._dynamic_config._missing_args) + "\n",
+ noiselevel=-1)
if self._dynamic_config._pprovided_args:
arg_refs = {}
@@ -7837,7 +7913,7 @@ class depgraph(object):
msg.append(" C) Remove offending entries from package.provided.\n\n")
msg.append("The best course of action depends on the reason that an offending\n")
msg.append("package.provided entry exists.\n\n")
- sys.stderr.write("".join(msg))
+ writemsg("".join(msg), noiselevel=-1)
masked_packages = []
for pkg in self._dynamic_config._masked_license_updates:
@@ -8487,7 +8563,7 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, myaction, myfiles, sp
backtracked = 0
frozen_config = _frozen_depgraph_config(settings, trees,
- myopts, spinner)
+ myopts, myparams, spinner)
while backtracker:
@@ -8569,7 +8645,7 @@ def _resume_depgraph(settings, trees, mtimedb, myopts, myparams, spinner):
mergelist = mtimedb["resume"]["mergelist"]
dropped_tasks = {}
frozen_config = _frozen_depgraph_config(settings, trees,
- myopts, spinner)
+ myopts, myparams, spinner)
while True:
mydepgraph = depgraph(settings, trees,
myopts, myparams, spinner, frozen_config=frozen_config)
diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py
index 9ee1d5e..3476aba 100644
--- a/pym/portage/tests/resolver/ResolverPlayground.py
+++ b/pym/portage/tests/resolver/ResolverPlayground.py
@@ -662,7 +662,8 @@ class ResolverPlaygroundTestCase(object):
str((node1, node2))) + \
", got: " + str(got))
- elif key in ("unstable_keywords", "needed_p_mask_changes") and expected is not None:
+ elif key in ("unstable_keywords", "needed_p_mask_changes",
+ "unsatisfied_deps") and expected is not None:
expected = set(expected)
if got != expected:
@@ -678,9 +679,10 @@ class ResolverPlaygroundResult(object):
checks = (
"success", "mergelist", "use_changes", "license_changes", "unstable_keywords", "slot_collision_solutions",
- "circular_dependency_solutions", "needed_p_mask_changes",
+ "circular_dependency_solutions", "needed_p_mask_changes", "unsatisfied_deps",
)
optional_checks = (
+ "unsatisfied_deps"
)
def __init__(self, atoms, success, mydepgraph, favorites):
@@ -695,6 +697,7 @@ class ResolverPlaygroundResult(object):
self.needed_p_mask_changes = None
self.slot_collision_solutions = None
self.circular_dependency_solutions = None
+ self.unsatisfied_deps = frozenset()
if self.depgraph._dynamic_config._serialized_tasks_cache is not None:
self.mergelist = []
@@ -754,6 +757,10 @@ class ResolverPlaygroundResult(object):
sol = handler.solutions
self.circular_dependency_solutions = dict(zip([x.cpv for x in sol.keys()], sol.values()))
+ if self.depgraph._dynamic_config._unsatisfied_deps_for_display:
+ self.unsatisfied_deps = set(dep_info[0][1]
+ for dep_info in self.depgraph._dynamic_config._unsatisfied_deps_for_display)
+
class ResolverPlaygroundDepcleanResult(object):
checks = (
diff --git a/pym/portage/tests/resolver/test_slot_conflict_unsatisfied_deep_deps.py b/pym/portage/tests/resolver/test_slot_conflict_unsatisfied_deep_deps.py
new file mode 100644
index 0000000..13f7e67
--- /dev/null
+++ b/pym/portage/tests/resolver/test_slot_conflict_unsatisfied_deep_deps.py
@@ -0,0 +1,115 @@
+# 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 SlotConflictUnsatisfiedDeepDepsTestCase(TestCase):
+
+ def testSlotConflictUnsatisfiedDeepDeps(self):
+
+ ebuilds = {
+ "dev-libs/A-1": { },
+ "dev-libs/A-2": { "KEYWORDS": "~x86" },
+ "dev-libs/B-1": { "DEPEND": "dev-libs/A" },
+ "dev-libs/C-1": { "DEPEND": ">=dev-libs/A-2" },
+ "dev-libs/D-1": { "DEPEND": "dev-libs/A" },
+ }
+
+ installed = {
+ "dev-libs/broken-1": {
+ "RDEPEND": "dev-libs/A dev-libs/initially-unsatisfied"
+ },
+ }
+
+ world = (
+ "dev-libs/A",
+ "dev-libs/B",
+ "dev-libs/C",
+ "dev-libs/D",
+ "dev-libs/broken"
+ )
+
+ test_cases = (
+ # Test bug #520950, where unsatisfied deps of installed
+ # packages are supposed to be ignored when they are beyond
+ # the depth requested by the user.
+ ResolverPlaygroundTestCase(
+ ["dev-libs/B", "dev-libs/C", "dev-libs/D"],
+ all_permutations=True,
+ options={
+ "--autounmask": "y",
+ "--complete-graph": True
+ },
+ mergelist=["dev-libs/A-2", "dev-libs/B-1", "dev-libs/C-1", "dev-libs/D-1"],
+ ignore_mergelist_order=True,
+ unstable_keywords=["dev-libs/A-2"],
+ unsatisfied_deps=[],
+ success=False),
+
+ ResolverPlaygroundTestCase(
+ ["@world"],
+ options={
+ "--autounmask": "y",
+ "--complete-graph": True
+ },
+ mergelist=["dev-libs/A-2", "dev-libs/B-1", "dev-libs/C-1", "dev-libs/D-1"],
+ ignore_mergelist_order=True,
+ unstable_keywords=["dev-libs/A-2"],
+ unsatisfied_deps=["dev-libs/broken"],
+ success=False),
+
+ # Test --selective with --deep = 0
+ ResolverPlaygroundTestCase(
+ ["@world"],
+ options={
+ "--autounmask": "y",
+ "--complete-graph": True,
+ "--selective": True,
+ "--deep": 0
+ },
+ mergelist=["dev-libs/A-2", "dev-libs/B-1", "dev-libs/C-1", "dev-libs/D-1"],
+ ignore_mergelist_order=True,
+ unstable_keywords=["dev-libs/A-2"],
+ unsatisfied_deps=[],
+ success=False),
+
+ # Test --deep = 1
+ ResolverPlaygroundTestCase(
+ ["@world"],
+ options={
+ "--autounmask": "y",
+ "--complete-graph": True,
+ "--selective": True,
+ "--deep": 1
+ },
+ mergelist=["dev-libs/A-2", "dev-libs/B-1", "dev-libs/C-1", "dev-libs/D-1"],
+ ignore_mergelist_order=True,
+ unstable_keywords=["dev-libs/A-2"],
+ unsatisfied_deps=["dev-libs/initially-unsatisfied"],
+ success=False),
+
+ # Test --deep = True
+ ResolverPlaygroundTestCase(
+ ["@world"],
+ options={
+ "--autounmask": "y",
+ "--complete-graph": True,
+ "--selective": True,
+ "--deep": True
+ },
+ mergelist=["dev-libs/A-2", "dev-libs/B-1", "dev-libs/C-1", "dev-libs/D-1"],
+ ignore_mergelist_order=True,
+ unstable_keywords=["dev-libs/A-2"],
+ unsatisfied_deps=["dev-libs/initially-unsatisfied"],
+ success=False),
+ )
+
+ 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()
next reply other threads:[~2014-09-11 21:37 UTC|newest]
Thread overview: 56+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-09-11 21:37 Zac Medico [this message]
-- strict thread matches above, loose matches on Subject: below --
2018-05-04 17:12 [gentoo-commits] proj/portage:master commit in: pym/portage/tests/resolver/, pym/_emerge/ Zac Medico
2018-04-12 2:45 Zac Medico
2017-09-29 17:24 Zac Medico
2017-06-02 5:41 Zac Medico
2017-04-01 5:48 Zac Medico
2017-03-22 8:59 Zac Medico
2017-03-16 4:51 Zac Medico
2017-03-09 19:36 Zac Medico
2016-08-07 17:55 Zac Medico
2015-11-24 16:45 Zac Medico
2014-11-16 9:04 Zac Medico
2014-10-27 9:26 Zac Medico
2014-09-19 9:28 Zac Medico
2014-09-19 9:17 Zac Medico
2014-09-17 16:35 Zac Medico
2014-09-16 21:04 Brian Dolbec
2014-04-26 19:44 Sebastian Luther
2014-02-16 17:25 Sebastian Luther
2014-02-15 12:40 Sebastian Luther
2014-02-05 19:42 Sebastian Luther
2014-01-07 22:22 Arfrever Frehtes Taifersar Arahesis
2013-12-05 15:38 Brian Dolbec
2013-12-01 10:19 Brian Dolbec
2013-11-27 7:44 Mike Frysinger
2013-08-02 8:26 Zac Medico
2013-07-07 19:16 Zac Medico
2013-07-06 21:45 Zac Medico
2013-03-19 21:06 Zac Medico
2013-03-05 0:56 Zac Medico
2013-02-14 4:45 Zac Medico
2013-02-12 2:50 Zac Medico
2013-02-11 22:51 Zac Medico
2013-02-11 1:58 Zac Medico
2012-12-01 23:23 Zac Medico
2012-10-26 6:06 Zac Medico
2012-10-26 4:57 Zac Medico
2012-07-05 3:16 Zac Medico
2012-06-15 23:04 Zac Medico
2012-02-26 10:00 Zac Medico
2011-11-18 1:26 Zac Medico
2011-09-30 8:30 Zac Medico
2011-09-19 3:05 Zac Medico
2011-09-18 20:08 Zac Medico
2011-09-18 19:42 Zac Medico
2011-09-15 5:10 Zac Medico
2011-09-11 20:43 Zac Medico
2011-06-12 22:13 Zac Medico
2011-05-24 23:59 Zac Medico
2011-05-23 5:40 Zac Medico
2011-05-22 23:49 Zac Medico
2011-05-21 3:49 Zac Medico
2011-05-03 22:59 Zac Medico
2011-04-27 20:40 Zac Medico
2011-02-13 13:55 Zac Medico
2011-02-13 10:23 Zac Medico
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1410471368.336ab90212c80ce9548362bf4fbdafd388c3515c.zmedico@gentoo \
--to=zmedico@gentoo.org \
--cc=gentoo-commits@lists.gentoo.org \
--cc=gentoo-dev@lists.gentoo.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox