Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 520950 | Differences between
and this patch

Collapse All | Expand All

(-)a/pym/_emerge/depgraph.py (-13 / +84 lines)
Lines 107-113 def _wildcard_set(atoms): Link Here
107
107
108
class _frozen_depgraph_config(object):
108
class _frozen_depgraph_config(object):
109
109
110
	def __init__(self, settings, trees, myopts, spinner):
110
	def __init__(self, settings, trees, myopts, params, spinner):
111
		self.settings = settings
111
		self.settings = settings
112
		self.target_root = settings["EROOT"]
112
		self.target_root = settings["EROOT"]
113
		self.myopts = myopts
113
		self.myopts = myopts
Lines 115-120 class _frozen_depgraph_config(object): Link Here
115
		if settings.get("PORTAGE_DEBUG", "") == "1":
115
		if settings.get("PORTAGE_DEBUG", "") == "1":
116
			self.edebug = 1
116
			self.edebug = 1
117
		self.spinner = spinner
117
		self.spinner = spinner
118
		self.requested_depth = params.get("deep", 0)
118
		self._running_root = trees[trees._running_eroot]["root_config"]
119
		self._running_root = trees[trees._running_eroot]["root_config"]
119
		self.pkgsettings = {}
120
		self.pkgsettings = {}
120
		self.trees = {}
121
		self.trees = {}
Lines 502-514 class _dynamic_depgraph_config(object): Link Here
502
503
503
class depgraph(object):
504
class depgraph(object):
504
505
506
	# Represents the depth of a node that is unreachable from explicit
507
	# user arguments (or their deep dependencies). Such nodes are pulled
508
	# in by the _complete_graph method.
509
	_UNREACHABLE_DEPTH = object()
510
505
	pkg_tree_map = RootConfig.pkg_tree_map
511
	pkg_tree_map = RootConfig.pkg_tree_map
506
512
507
	def __init__(self, settings, trees, myopts, myparams, spinner,
513
	def __init__(self, settings, trees, myopts, myparams, spinner,
508
		frozen_config=None, backtrack_parameters=BacktrackParameter(), allow_backtracking=False):
514
		frozen_config=None, backtrack_parameters=BacktrackParameter(), allow_backtracking=False):
509
		if frozen_config is None:
515
		if frozen_config is None:
510
			frozen_config = _frozen_depgraph_config(settings, trees,
516
			frozen_config = _frozen_depgraph_config(settings, trees,
511
			myopts, spinner)
517
			myopts, myparams, spinner)
512
		self._frozen_config = frozen_config
518
		self._frozen_config = frozen_config
513
		self._dynamic_config = _dynamic_depgraph_config(self, myparams,
519
		self._dynamic_config = _dynamic_depgraph_config(self, myparams,
514
			allow_backtracking, backtrack_parameters)
520
			allow_backtracking, backtrack_parameters)
Lines 2095-2100 class depgraph(object): Link Here
2095
				arg = arg_stack.pop()
2101
				arg = arg_stack.pop()
2096
				if arg in traversed_set_args:
2102
				if arg in traversed_set_args:
2097
					continue
2103
					continue
2104
2105
				# If a node with the same hash already exists in
2106
				# the digraph, preserve the existing instance which
2107
				# may have a different reset_depth attribute
2108
				# (distiguishes user arguments from sets added for
2109
				# another reason such as complete mode).
2110
				arg = self._dynamic_config.digraph.get(arg, arg)
2098
				traversed_set_args.add(arg)
2111
				traversed_set_args.add(arg)
2099
2112
2100
				if add_to_digraph:
2113
				if add_to_digraph:
Lines 2114-2121 class depgraph(object): Link Here
2114
					if nested_set is None:
2127
					if nested_set is None:
2115
						nested_set = root_config.sets.get(s)
2128
						nested_set = root_config.sets.get(s)
2116
					if nested_set is not None:
2129
					if nested_set is not None:
2130
						# Propagate the reset_depth attribute from
2131
						# parent set to nested set.
2117
						nested_arg = SetArg(arg=token, pset=nested_set,
2132
						nested_arg = SetArg(arg=token, pset=nested_set,
2133
							reset_depth=arg.reset_depth,
2118
							root_config=root_config)
2134
							root_config=root_config)
2135
2136
						# Preserve instances already in the graph (same
2137
						# reason as for the "arg" variable above).
2138
						nested_arg = self._dynamic_config.digraph.get(
2139
							nested_arg, nested_arg)
2119
						arg_stack.append(nested_arg)
2140
						arg_stack.append(nested_arg)
2120
						if add_to_digraph:
2141
						if add_to_digraph:
2121
							self._dynamic_config.digraph.add(nested_arg, arg,
2142
							self._dynamic_config.digraph.add(nested_arg, arg,
Lines 2164-2172 class depgraph(object): Link Here
2164
				dep.collapsed_priority.ignored):
2185
				dep.collapsed_priority.ignored):
2165
				# This is an unnecessary build-time dep.
2186
				# This is an unnecessary build-time dep.
2166
				return 1
2187
				return 1
2188
2189
			# NOTE: For removal actions, allow_unsatisfied is always
2190
			# True since all existing removal actions traverse all
2191
			# installed deps deeply via the _complete_graph method,
2192
			# which calls _create_graph with allow_unsatisfied = True.
2167
			if allow_unsatisfied:
2193
			if allow_unsatisfied:
2168
				self._dynamic_config._unsatisfied_deps.append(dep)
2194
				self._dynamic_config._unsatisfied_deps.append(dep)
2169
				return 1
2195
				return 1
2196
2197
			# The following case occurs when
2198
			# _solve_non_slot_operator_slot_conflicts calls
2199
			# _create_graph. In this case, ignore unsatisfied deps for
2200
			# installed packages only if their depth is beyond the depth
2201
			# requested by the user and the dep was initially
2202
			# unsatisfied (not broken by a slot conflict in the current
2203
			# graph). See bug #520950.
2204
			# NOTE: The value of dep.parent.depth is guaranteed to be
2205
			# either an integer or _UNREACHABLE_DEPTH, where
2206
			# _UNREACHABLE_DEPTH indicates that the parent has been
2207
			# pulled in by the _complete_graph method (rather than by
2208
			# explicit arguments or their deep dependencies). These
2209
			# cases must be distinguished because depth is meaningless
2210
			# for packages that are not reachable as deep dependencies
2211
			# of arguments.
2212
			if (self._dynamic_config._complete_mode and
2213
				isinstance(dep.parent, Package) and
2214
				dep.parent.installed and
2215
				(dep.parent.depth is self._UNREACHABLE_DEPTH or
2216
				(self._frozen_config.requested_depth is not True and
2217
				dep.parent.depth >= self._frozen_config.requested_depth))):
2218
				inst_pkg, in_graph = \
2219
					self._select_pkg_from_installed(dep.root, dep.atom)
2220
				if inst_pkg is None:
2221
					self._dynamic_config._initially_unsatisfied_deps.append(dep)
2222
					return 1
2223
2170
			self._dynamic_config._unsatisfied_deps_for_display.append(
2224
			self._dynamic_config._unsatisfied_deps_for_display.append(
2171
				((dep.root, dep.atom), {"myparent":dep.parent}))
2225
				((dep.root, dep.atom), {"myparent":dep.parent}))
2172
2226
Lines 2411-2424 class depgraph(object): Link Here
2411
		# Installing package A, we need to make sure package A's deps are met.
2465
		# Installing package A, we need to make sure package A's deps are met.
2412
		# emerge --deep <pkgspec>; we need to recursively check dependencies of pkgspec
2466
		# emerge --deep <pkgspec>; we need to recursively check dependencies of pkgspec
2413
		# If we are in --nodeps (no recursion) mode, we obviously only check 1 level of dependencies.
2467
		# If we are in --nodeps (no recursion) mode, we obviously only check 1 level of dependencies.
2414
		if arg_atoms and depth > 0:
2468
		if arg_atoms and depth != 0:
2415
			for parent, atom in arg_atoms:
2469
			for parent, atom in arg_atoms:
2416
				if parent.reset_depth:
2470
				if parent.reset_depth:
2417
					depth = 0
2471
					depth = 0
2418
					break
2472
					break
2419
2473
2420
		if previously_added and pkg.depth is not None:
2474
		if previously_added and depth != 0 and \
2421
			depth = min(pkg.depth, depth)
2475
			isinstance(pkg.depth, int):
2476
			# Use pkg.depth if it is less than depth.
2477
			if isinstance(depth, int):
2478
				depth = min(pkg.depth, depth)
2479
			else:
2480
				# depth is _UNREACHABLE_DEPTH and pkg.depth is
2481
				# an int, so use the int because it's considered
2482
				# to be less than _UNREACHABLE_DEPTH.
2483
				depth = pkg.depth
2484
2422
		pkg.depth = depth
2485
		pkg.depth = depth
2423
		deep = self._dynamic_config.myparams.get("deep", 0)
2486
		deep = self._dynamic_config.myparams.get("deep", 0)
2424
		update = "--update" in self._frozen_config.myopts
2487
		update = "--update" in self._frozen_config.myopts
Lines 2716-2722 class depgraph(object): Link Here
2716
2779
2717
	def _wrapped_add_pkg_dep_string(self, pkg, dep_root, dep_priority,
2780
	def _wrapped_add_pkg_dep_string(self, pkg, dep_root, dep_priority,
2718
		dep_string, allow_unsatisfied):
2781
		dep_string, allow_unsatisfied):
2719
		depth = pkg.depth + 1
2782
		if isinstance(pkg.depth, int):
2783
			depth = pkg.depth + 1
2784
		else:
2785
			depth = pkg.depth
2786
2720
		deep = self._dynamic_config.myparams.get("deep", 0)
2787
		deep = self._dynamic_config.myparams.get("deep", 0)
2721
		recurse_satisfied = deep is True or depth <= deep
2788
		recurse_satisfied = deep is True or depth <= deep
2722
		debug = "--debug" in self._frozen_config.myopts
2789
		debug = "--debug" in self._frozen_config.myopts
Lines 3947-3956 class depgraph(object): Link Here
3947
			# Recursively traversed virtual dependencies, and their
4014
			# Recursively traversed virtual dependencies, and their
3948
			# direct dependencies, are considered to have the same
4015
			# direct dependencies, are considered to have the same
3949
			# depth as direct dependencies.
4016
			# depth as direct dependencies.
3950
			if parent.depth is None:
4017
			if isinstance(parent.depth, int):
3951
				virt_depth = None
3952
			else:
3953
				virt_depth = parent.depth + 1
4018
				virt_depth = parent.depth + 1
4019
			else:
4020
				# The depth may be None when called via
4021
				# _select_atoms_probe, or it may be
4022
				# _UNREACHABLE_DEPTH for complete mode.
4023
				virt_depth = parent.depth
4024
3954
			chosen_atom_ids = frozenset(id(atom) for atom in mycheck[1])
4025
			chosen_atom_ids = frozenset(id(atom) for atom in mycheck[1])
3955
			selected_atoms = OrderedDict()
4026
			selected_atoms = OrderedDict()
3956
			node_stack = [(parent, None, None)]
4027
			node_stack = [(parent, None, None)]
Lines 5833-5846 class depgraph(object): Link Here
5833
					pset = root_config.sets[s]
5904
					pset = root_config.sets[s]
5834
				atom = SETPREFIX + s
5905
				atom = SETPREFIX + s
5835
				args.append(SetArg(arg=atom, pset=pset,
5906
				args.append(SetArg(arg=atom, pset=pset,
5836
					root_config=root_config))
5907
					reset_depth=False, root_config=root_config))
5837
5908
5838
		self._set_args(args)
5909
		self._set_args(args)
5839
		for arg in self._expand_set_args(args, add_to_digraph=True):
5910
		for arg in self._expand_set_args(args, add_to_digraph=True):
5840
			for atom in arg.pset.getAtoms():
5911
			for atom in arg.pset.getAtoms():
5841
				self._dynamic_config._dep_stack.append(
5912
				self._dynamic_config._dep_stack.append(
5842
					Dependency(atom=atom, root=arg.root_config.root,
5913
					Dependency(atom=atom, root=arg.root_config.root,
5843
						parent=arg))
5914
						parent=arg, depth=self._UNREACHABLE_DEPTH))
5844
5915
5845
		if True:
5916
		if True:
5846
			if self._dynamic_config._ignored_deps:
5917
			if self._dynamic_config._ignored_deps:
Lines 8487-8493 def _backtrack_depgraph(settings, trees, myopts, myparams, myaction, myfiles, sp Link Here
8487
	backtracked = 0
8558
	backtracked = 0
8488
8559
8489
	frozen_config = _frozen_depgraph_config(settings, trees,
8560
	frozen_config = _frozen_depgraph_config(settings, trees,
8490
		myopts, spinner)
8561
		myopts, myparams, spinner)
8491
8562
8492
	while backtracker:
8563
	while backtracker:
8493
8564
Lines 8569-8575 def _resume_depgraph(settings, trees, mtimedb, myopts, myparams, spinner): Link Here
8569
	mergelist = mtimedb["resume"]["mergelist"]
8640
	mergelist = mtimedb["resume"]["mergelist"]
8570
	dropped_tasks = {}
8641
	dropped_tasks = {}
8571
	frozen_config = _frozen_depgraph_config(settings, trees,
8642
	frozen_config = _frozen_depgraph_config(settings, trees,
8572
		myopts, spinner)
8643
		myopts, myparams, spinner)
8573
	while True:
8644
	while True:
8574
		mydepgraph = depgraph(settings, trees,
8645
		mydepgraph = depgraph(settings, trees,
8575
			myopts, myparams, spinner, frozen_config=frozen_config)
8646
			myopts, myparams, spinner, frozen_config=frozen_config)
(-)a/pym/portage/tests/resolver/ResolverPlayground.py (-2 / +9 lines)
Lines 659-665 class ResolverPlaygroundTestCase(object): Link Here
659
									str((node1, node2))) + \
659
									str((node1, node2))) + \
660
									", got: " + str(got))
660
									", got: " + str(got))
661
661
662
			elif key in ("unstable_keywords", "needed_p_mask_changes") and expected is not None:
662
			elif key in ("unstable_keywords", "needed_p_mask_changes",
663
				"unsatisfied_deps") and expected is not None:
663
				expected = set(expected)
664
				expected = set(expected)
664
665
665
			if got != expected:
666
			if got != expected:
Lines 675-683 class ResolverPlaygroundResult(object): Link Here
675
676
676
	checks = (
677
	checks = (
677
		"success", "mergelist", "use_changes", "license_changes", "unstable_keywords", "slot_collision_solutions",
678
		"success", "mergelist", "use_changes", "license_changes", "unstable_keywords", "slot_collision_solutions",
678
		"circular_dependency_solutions", "needed_p_mask_changes",
679
		"circular_dependency_solutions", "needed_p_mask_changes", "unsatisfied_deps",
679
		)
680
		)
680
	optional_checks = (
681
	optional_checks = (
682
		"unsatisfied_deps"
681
		)
683
		)
682
684
683
	def __init__(self, atoms, success, mydepgraph, favorites):
685
	def __init__(self, atoms, success, mydepgraph, favorites):
Lines 692-697 class ResolverPlaygroundResult(object): Link Here
692
		self.needed_p_mask_changes = None
694
		self.needed_p_mask_changes = None
693
		self.slot_collision_solutions = None
695
		self.slot_collision_solutions = None
694
		self.circular_dependency_solutions = None
696
		self.circular_dependency_solutions = None
697
		self.unsatisfied_deps = frozenset()
695
698
696
		if self.depgraph._dynamic_config._serialized_tasks_cache is not None:
699
		if self.depgraph._dynamic_config._serialized_tasks_cache is not None:
697
			self.mergelist = []
700
			self.mergelist = []
Lines 751-756 class ResolverPlaygroundResult(object): Link Here
751
			sol = handler.solutions
754
			sol = handler.solutions
752
			self.circular_dependency_solutions = dict(zip([x.cpv for x in sol.keys()], sol.values()))
755
			self.circular_dependency_solutions = dict(zip([x.cpv for x in sol.keys()], sol.values()))
753
756
757
		if self.depgraph._dynamic_config._unsatisfied_deps_for_display:
758
			self.unsatisfied_deps = set(dep_info[0][1]
759
				for dep_info in self.depgraph._dynamic_config._unsatisfied_deps_for_display)
760
754
class ResolverPlaygroundDepcleanResult(object):
761
class ResolverPlaygroundDepcleanResult(object):
755
762
756
	checks = (
763
	checks = (
(-)a/pym/portage/tests/resolver/test_slot_conflict_unsatisfied_deep_deps.py (-1 / +55 lines)
Line 0 Link Here
0
- 
1
# Copyright 2014 Gentoo Foundation
2
# Distributed under the terms of the GNU General Public License v2
3
4
from portage.tests import TestCase
5
from portage.tests.resolver.ResolverPlayground import ResolverPlayground, ResolverPlaygroundTestCase
6
7
class SlotConflictUnsatisfiedDeepDepsTestCase(TestCase):
8
9
	def testSlotConflictUnsatisfiedDeepDeps(self):
10
11
		ebuilds = {
12
			"dev-libs/A-1": { },
13
			"dev-libs/A-2": { "KEYWORDS": "~x86" },
14
			"dev-libs/B-1": { "DEPEND": "dev-libs/A" },
15
			"dev-libs/C-1": { "DEPEND": ">=dev-libs/A-2" },
16
			"dev-libs/D-1": { "DEPEND": "dev-libs/A" },
17
		}
18
19
		installed = {
20
			"dev-libs/broken-1": {
21
				"RDEPEND": "dev-libs/A dev-libs/initially-unsatisfied"
22
			},
23
		}
24
25
		world = (
26
			"dev-libs/A",
27
			"dev-libs/B",
28
			"dev-libs/C",
29
			"dev-libs/D",
30
			"dev-libs/broken"
31
		)
32
33
		test_cases = (
34
			# Test bug #520950, where unsatisfied deps of installed
35
			# packages are supposed to be ignored when they are beyond
36
			# the depth requested by the user.
37
			ResolverPlaygroundTestCase(
38
				["dev-libs/B", "dev-libs/C", "dev-libs/D"],
39
				all_permutations=True,
40
				options={"--autounmask": "y", "--complete-graph": True},
41
				mergelist=["dev-libs/A-2", "dev-libs/B-1", "dev-libs/C-1", "dev-libs/D-1"],
42
				ignore_mergelist_order=True,
43
				unstable_keywords=["dev-libs/A-2"],
44
				unsatisfied_deps=[],
45
				success=False),
46
		)
47
48
		playground = ResolverPlayground(ebuilds=ebuilds, installed=installed,
49
			world=world, debug=False)
50
		try:
51
			for test_case in test_cases:
52
				playground.run_TestCase(test_case)
53
				self.assertEqual(test_case.test_success, True, test_case.fail_msg)
54
		finally:
55
			playground.cleanup()

Return to bug 520950