Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] [PATCH] _compute_abi_rebuild_info: fix bug #521990
Date: Fri, 12 Sep 2014 18:12:33
Message-Id: 5413378B.902@gentoo.org
1 Since self._dynamic_config._slot_operator_deps only contains deps for
2 packages added to the graph, it doesn't contain potentially relevant
3 deps of installed packages that have not been added to the graph.
4 Therefore, generate pseudo-deps for such installed packages, and use
5 those to generate the rebuild info.
6
7 X-Gentoo-Bug: 521990
8 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=521990
9 ---
10 pym/_emerge/depgraph.py | 100 +++++++++++++++++----
11 pym/portage/tests/resolver/ResolverPlayground.py | 15 +++-
12 .../resolver/test_slot_conflict_force_rebuild.py | 84 +++++++++++++++++
13 3 files changed, 183 insertions(+), 16 deletions(-)
14 create mode 100644 pym/portage/tests/resolver/test_slot_conflict_force_rebuild.py
15
16 diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
17 index cc87d9f..795f4fe 100644
18 --- a/pym/_emerge/depgraph.py
19 +++ b/pym/_emerge/depgraph.py
20 @@ -647,26 +647,96 @@ class depgraph(object):
21 # Go through all slot operator deps and check if one of these deps
22 # has a parent that is matched by one of the atoms from above.
23 forced_rebuilds = {}
24 - for (root, slot_atom), deps in self._dynamic_config._slot_operator_deps.items():
25 - rebuild_atoms = atoms.get(root, set())
26
27 - for dep in deps:
28 - if not isinstance(dep.parent, Package):
29 - continue
30 + for root, rebuild_atoms in atoms.items():
31
32 - if dep.parent.installed or dep.child.installed or \
33 - dep.parent.slot_atom not in rebuild_atoms:
34 - continue
35 + for slot_atom in rebuild_atoms:
36 +
37 + inst_pkg, reinst_pkg = \
38 + self._select_pkg_from_installed(root, slot_atom)
39
40 - # Make sure the child's slot/subslot has changed. If it hasn't,
41 - # then another child has forced this rebuild.
42 - installed_pkg = self._select_pkg_from_installed(root, dep.child.slot_atom)[0]
43 - if installed_pkg and installed_pkg.slot == dep.child.slot and \
44 - installed_pkg.sub_slot == dep.child.sub_slot:
45 + if inst_pkg is reinst_pkg or reinst_pkg is None:
46 continue
47
48 - # The child has forced a rebuild of the parent
49 - forced_rebuilds.setdefault(root, {}).setdefault(dep.child, set()).add(dep.parent)
50 + # Generate pseudo-deps for any slot-operator deps of
51 + # inst_pkg. Its deps aren't in _slot_operator_deps
52 + # because it hasn't been added to the graph, but we
53 + # are interested in any rebuilds that it triggered.
54 + built_slot_op_atoms = []
55 + if inst_pkg is not None:
56 + selected_atoms = self._select_atoms_probe(
57 + inst_pkg.root, inst_pkg)
58 + for atom in selected_atoms:
59 + if atom.slot_operator_built:
60 + built_slot_op_atoms.append(atom)
61 +
62 + if not built_slot_op_atoms:
63 + continue
64 +
65 + # Use a cloned list, since we may append to it below.
66 + deps = self._dynamic_config._slot_operator_deps.get(
67 + (root, slot_atom), [])[:]
68 +
69 + if built_slot_op_atoms and reinst_pkg is not None:
70 + for child in self._dynamic_config.digraph.child_nodes(
71 + reinst_pkg):
72 +
73 + if child.installed:
74 + continue
75 +
76 + for atom in built_slot_op_atoms:
77 + # NOTE: Since atom comes from inst_pkg, and
78 + # reinst_pkg is the replacement parent, there's
79 + # no guarantee that atom will completely match
80 + # child. So, simply use atom.cp and atom.slot
81 + # for matching.
82 + if atom.cp != child.cp:
83 + continue
84 + if atom.slot and atom.slot != child.slot:
85 + continue
86 + deps.append(Dependency(atom=atom, child=child,
87 + root=child.root, parent=reinst_pkg))
88 +
89 + for dep in deps:
90 + if dep.child.installed:
91 + # Find the replacement child.
92 + child = next((pkg for pkg in
93 + self._dynamic_config._package_tracker.match(
94 + dep.root, dep.child.slot_atom)
95 + if not pkg.installed), None)
96 +
97 + if child is None:
98 + continue
99 +
100 + inst_child = dep.child.installed
101 +
102 + else:
103 + child = dep.child
104 + inst_child = self._select_pkg_from_installed(
105 + child.root, child.slot_atom)[0]
106 +
107 + # Make sure the child's slot/subslot has changed. If it
108 + # hasn't, then another child has forced this rebuild.
109 + if inst_child and inst_child.slot == child.slot and \
110 + inst_child.sub_slot == child.sub_slot:
111 + continue
112 +
113 + if dep.parent.installed:
114 + # Find the replacement parent.
115 + parent = next((pkg for pkg in
116 + self._dynamic_config._package_tracker.match(
117 + dep.parent.root, dep.parent.slot_atom)
118 + if not pkg.installed), None)
119 +
120 + if parent is None:
121 + continue
122 +
123 + else:
124 + parent = dep.parent
125 +
126 + # The child has forced a rebuild of the parent
127 + forced_rebuilds.setdefault(root, {}
128 + ).setdefault(child, set()).add(parent)
129
130 if debug:
131 writemsg_level("slot operator dependencies:\n",
132 diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py
133 index 77a5b5c..af54c56 100644
134 --- a/pym/portage/tests/resolver/ResolverPlayground.py
135 +++ b/pym/portage/tests/resolver/ResolverPlayground.py
136 @@ -668,6 +668,9 @@ class ResolverPlaygroundTestCase(object):
137 "unsatisfied_deps") and expected is not None:
138 expected = set(expected)
139
140 + elif key == "forced_rebuilds" and expected is not None:
141 + expected = dict((k, set(v)) for k, v in expected.items())
142 +
143 if got != expected:
144 fail_msgs.append("atoms: (" + ", ".join(result.atoms) + "), key: " + \
145 key + ", expected: " + str(expected) + ", got: " + str(got))
146 @@ -681,9 +684,10 @@ class ResolverPlaygroundResult(object):
147
148 checks = (
149 "success", "mergelist", "use_changes", "license_changes", "unstable_keywords", "slot_collision_solutions",
150 - "circular_dependency_solutions", "needed_p_mask_changes", "unsatisfied_deps",
151 + "circular_dependency_solutions", "needed_p_mask_changes", "unsatisfied_deps", "forced_rebuilds"
152 )
153 optional_checks = (
154 + "forced_rebuilds",
155 "unsatisfied_deps"
156 )
157
158 @@ -700,6 +704,7 @@ class ResolverPlaygroundResult(object):
159 self.slot_collision_solutions = None
160 self.circular_dependency_solutions = None
161 self.unsatisfied_deps = frozenset()
162 + self.forced_rebuilds = None
163
164 if self.depgraph._dynamic_config._serialized_tasks_cache is not None:
165 self.mergelist = []
166 @@ -763,6 +768,14 @@ class ResolverPlaygroundResult(object):
167 self.unsatisfied_deps = set(dep_info[0][1]
168 for dep_info in self.depgraph._dynamic_config._unsatisfied_deps_for_display)
169
170 + if self.depgraph._forced_rebuilds:
171 + self.forced_rebuilds = dict(self._iter_forced_rebuilds())
172 +
173 + def _iter_forced_rebuilds(self):
174 + for child_dict in self.depgraph._forced_rebuilds.values():
175 + for child, parents in child_dict.items():
176 + yield child.cpv, set(parent.cpv for parent in parents)
177 +
178 class ResolverPlaygroundDepcleanResult(object):
179
180 checks = (
181 diff --git a/pym/portage/tests/resolver/test_slot_conflict_force_rebuild.py b/pym/portage/tests/resolver/test_slot_conflict_force_rebuild.py
182 new file mode 100644
183 index 0000000..4170bfd
184 --- /dev/null
185 +++ b/pym/portage/tests/resolver/test_slot_conflict_force_rebuild.py
186 @@ -0,0 +1,84 @@
187 +# Copyright 2014 Gentoo Foundation
188 +# Distributed under the terms of the GNU General Public License v2
189 +
190 +from portage.tests import TestCase
191 +from portage.tests.resolver.ResolverPlayground import (ResolverPlayground,
192 + ResolverPlaygroundTestCase)
193 +
194 +class SlotConflictForceRebuildTestCase(TestCase):
195 +
196 + def testSlotConflictForceRebuild(self):
197 +
198 + ebuilds = {
199 +
200 + "app-misc/A-1" : {
201 + "EAPI": "5",
202 + "SLOT": "0/1"
203 + },
204 +
205 + "app-misc/A-2" : {
206 + "EAPI": "5",
207 + "SLOT": "0/2"
208 + },
209 +
210 + "app-misc/B-0" : {
211 + "EAPI": "5",
212 + "RDEPEND": "app-misc/A:="
213 + },
214 +
215 + "app-misc/C-0" : {
216 + "EAPI": "5",
217 + "RDEPEND": "app-misc/A"
218 + },
219 +
220 + }
221 +
222 + installed = {
223 +
224 + "app-misc/A-1" : {
225 + "EAPI": "5",
226 + "SLOT": "0/1"
227 + },
228 +
229 + "app-misc/B-0" : {
230 + "EAPI": "5",
231 + "RDEPEND": "app-misc/A:0/1="
232 + },
233 +
234 + "app-misc/C-0" : {
235 + "EAPI": "5",
236 + "RDEPEND": "app-misc/A:0/1="
237 + },
238 +
239 + }
240 +
241 + world = ["app-misc/B", "app-misc/C"]
242 +
243 + test_cases = (
244 +
245 + # Test bug #521990, where forced_rebuilds omits ebuilds that
246 + # had have had their slot operator atoms removed from the
247 + # ebuilds, even though the corresponding installed
248 + # instances had really forced rebuilds due to being built
249 + # with slot-operators in their deps.
250 + ResolverPlaygroundTestCase(
251 + ["app-misc/A"],
252 + options = {},
253 + success = True,
254 + ambiguous_merge_order = True,
255 + mergelist = ['app-misc/A-2', ('app-misc/B-0', 'app-misc/C-0')],
256 + forced_rebuilds = {
257 + 'app-misc/A-2': ['app-misc/B-0', 'app-misc/C-0']
258 + }
259 + ),
260 +
261 + )
262 +
263 + playground = ResolverPlayground(ebuilds=ebuilds,
264 + installed=installed, world=world, debug=False)
265 + try:
266 + for test_case in test_cases:
267 + playground.run_TestCase(test_case)
268 + self.assertEqual(test_case.test_success, True, test_case.fail_msg)
269 + finally:
270 + playground.cleanup()
271 --
272 1.8.5.5

Replies