1 |
Author: zmedico |
2 |
Date: 2008-04-25 02:30:39 +0000 (Fri, 25 Apr 2008) |
3 |
New Revision: 9966 |
4 |
|
5 |
Modified: |
6 |
main/branches/2.1.2/bin/emerge |
7 |
main/branches/2.1.2/doc/dependency_resolution/task_scheduling.docbook |
8 |
Log: |
9 |
Bug #172812 - If any Uninstall tasks need to be executed in order |
10 |
to avoid a conflict, complete the graph with any dependencies that |
11 |
may have been initially neglected (to ensure that unsafe Uninstall |
12 |
tasks are properly identified and blocked from execution). |
13 |
(trunk r9962:9965) |
14 |
|
15 |
|
16 |
Modified: main/branches/2.1.2/bin/emerge |
17 |
=================================================================== |
18 |
--- main/branches/2.1.2/bin/emerge 2008-04-25 01:53:44 UTC (rev 9965) |
19 |
+++ main/branches/2.1.2/bin/emerge 2008-04-25 02:30:39 UTC (rev 9966) |
20 |
@@ -365,9 +365,7 @@ |
21 |
# recurse: go into the dependencies |
22 |
# deep: go into the dependencies of already merged packages |
23 |
# empty: pretend nothing is merged |
24 |
- # consistent: ensure that installation of new packages does not break |
25 |
- # any deep dependencies of required sets (args, system, or |
26 |
- # world). |
27 |
+ # complete: completely account for all known dependencies |
28 |
myparams = set(["recurse"]) |
29 |
if "--update" in myopts or \ |
30 |
"--newuse" in myopts or \ |
31 |
@@ -383,7 +381,7 @@ |
32 |
if "--deep" in myopts: |
33 |
myparams.add("deep") |
34 |
if "--complete-graph" in myopts: |
35 |
- myparams.add("consistent") |
36 |
+ myparams.add("complete") |
37 |
return myparams |
38 |
|
39 |
# search functionality |
40 |
@@ -1852,7 +1850,7 @@ |
41 |
# Slot collision nodes are not allowed to block other packages since |
42 |
# blocker validation is only able to account for one package per slot. |
43 |
self._slot_collision_nodes = set() |
44 |
- self._altlist_cache = {} |
45 |
+ self._serialized_tasks_cache = None |
46 |
self._pprovided_args = [] |
47 |
self._missing_args = [] |
48 |
self._masked_installed = [] |
49 |
@@ -1999,7 +1997,6 @@ |
50 |
nodeps = "--nodeps" in self.myopts |
51 |
empty = "empty" in self.myparams |
52 |
deep = "deep" in self.myparams |
53 |
- consistent = "consistent" in self.myparams |
54 |
update = "--update" in self.myopts and dep.depth <= 1 |
55 |
if dep.blocker: |
56 |
if not buildpkgonly and \ |
57 |
@@ -2043,8 +2040,7 @@ |
58 |
# should have been masked. |
59 |
raise |
60 |
if not myarg: |
61 |
- if consistent: |
62 |
- self._ignored_deps.append(dep) |
63 |
+ self._ignored_deps.append(dep) |
64 |
return 1 |
65 |
|
66 |
if not self._add_pkg(dep_pkg, dep.parent, |
67 |
@@ -2191,8 +2187,6 @@ |
68 |
return 1 |
69 |
elif pkg.installed and \ |
70 |
"deep" not in self.myparams: |
71 |
- if "consistent" not in self.myparams: |
72 |
- return 1 |
73 |
dep_stack = self._ignored_deps |
74 |
|
75 |
self.spinner.update() |
76 |
@@ -2716,7 +2710,7 @@ |
77 |
|
78 |
if not self.validate_blockers(): |
79 |
return False, myfavorites |
80 |
- |
81 |
+ |
82 |
# We're true here unless we are missing binaries. |
83 |
return (not missing,myfavorites) |
84 |
|
85 |
@@ -3089,7 +3083,7 @@ |
86 |
Since this method can consume enough time to disturb users, it is |
87 |
currently only enabled by the --complete-graph option. |
88 |
""" |
89 |
- if "consistent" not in self.myparams: |
90 |
+ if "complete" not in self.myparams: |
91 |
# Skip this to avoid consuming enough time to disturb users. |
92 |
return 1 |
93 |
|
94 |
@@ -3382,9 +3376,17 @@ |
95 |
blocker, set()).add(parent) |
96 |
if not self.blocker_parents[blocker]: |
97 |
del self.blocker_parents[blocker] |
98 |
- # Validate blockers that depend on merge order. |
99 |
- if not self.blocker_digraph.empty(): |
100 |
+ |
101 |
+ # This checks whether or not it's possible to resolve blocker |
102 |
+ # conflicts that depend on installation order or require |
103 |
+ # uninstallation of a currently installed package. Note that |
104 |
+ # this can lead to the current method being called recursively |
105 |
+ # if changes to the dependency graph are required. |
106 |
+ try: |
107 |
self.altlist() |
108 |
+ except self._unknown_internal_error: |
109 |
+ return False |
110 |
+ |
111 |
if self._slot_collision_info: |
112 |
# The user is only notified of a slot collision if there are no |
113 |
# unresolvable blocks. |
114 |
@@ -3416,13 +3418,19 @@ |
115 |
mygraph.order.sort(cmp_merge_preference) |
116 |
|
117 |
def altlist(self, reversed=False): |
118 |
- if reversed in self._altlist_cache: |
119 |
- return self._altlist_cache[reversed][:] |
120 |
+ |
121 |
+ while self._serialized_tasks_cache is None: |
122 |
+ try: |
123 |
+ self._serialized_tasks_cache = self._serialize_tasks() |
124 |
+ except self._serialize_tasks_retry: |
125 |
+ pass |
126 |
+ |
127 |
+ retlist = self._serialized_tasks_cache[:] |
128 |
if reversed: |
129 |
- retlist = self.altlist() |
130 |
retlist.reverse() |
131 |
- self._altlist_cache[reversed] = retlist[:] |
132 |
- return retlist |
133 |
+ return retlist |
134 |
+ |
135 |
+ def _serialize_tasks(self): |
136 |
mygraph=self.digraph.copy() |
137 |
# Prune "nomerge" root nodes if nothing depends on them, since |
138 |
# otherwise they slow down merge order calculation. Don't remove |
139 |
@@ -3460,7 +3468,11 @@ |
140 |
# correspond to blocker conflicts that could not be |
141 |
# resolved. |
142 |
ignored_uninstall_tasks = set() |
143 |
- blocker_deps = None |
144 |
+ have_uninstall_task = False |
145 |
+ complete = "complete" in self.myparams |
146 |
+ myblocker_parents = self.blocker_parents.copy() |
147 |
+ for k, v in myblocker_parents.iteritems(): |
148 |
+ myblocker_parents[k] = v.copy() |
149 |
asap_nodes = [] |
150 |
portage_node = None |
151 |
def get_nodes(**kwargs): |
152 |
@@ -3650,43 +3662,54 @@ |
153 |
inst_pkg = self._pkg_cache[ |
154 |
("installed", task.root, task.cpv, "nomerge")] |
155 |
|
156 |
- # For packages in the system set, don't take |
157 |
- # any chances. If the conflict can't be resolved |
158 |
- # by a normal upgrade operation then require |
159 |
- # user intervention. |
160 |
- skip = False |
161 |
- try: |
162 |
- for atom in root_config.sets[ |
163 |
- "system"].iterAtomsForPackage(task): |
164 |
- skip = True |
165 |
- break |
166 |
- except portage_exception.InvalidDependString: |
167 |
- skip = True |
168 |
- if skip: |
169 |
+ if self.digraph.contains(inst_pkg): |
170 |
continue |
171 |
|
172 |
- # For packages in the world set, go ahead an uninstall |
173 |
- # when necessary, as long as the atom will be satisfied |
174 |
- # in the final state. |
175 |
- graph_db = self.mydbapi[task.root] |
176 |
- try: |
177 |
- for atom in root_config.sets[ |
178 |
- "world"].iterAtomsForPackage(task): |
179 |
- satisfied = False |
180 |
- for cpv in graph_db.match(atom): |
181 |
- if cpv == inst_pkg.cpv and \ |
182 |
- inst_pkg in graph_db: |
183 |
- continue |
184 |
- satisfied = True |
185 |
- break |
186 |
- if not satisfied: |
187 |
+ if "/" == task.root: |
188 |
+ # For packages in the system set, don't take |
189 |
+ # any chances. If the conflict can't be resolved |
190 |
+ # by a normal replacement operation then abort. |
191 |
+ skip = False |
192 |
+ try: |
193 |
+ for atom in root_config.sets[ |
194 |
+ "system"].iterAtomsForPackage(task): |
195 |
skip = True |
196 |
break |
197 |
- except portage_exception.InvalidDependString: |
198 |
- skip = True |
199 |
- if skip: |
200 |
- continue |
201 |
+ except portage_exception.InvalidDependString: |
202 |
+ skip = True |
203 |
+ if skip: |
204 |
+ continue |
205 |
|
206 |
+ # Note that the world check isn't always |
207 |
+ # necessary since self._complete_graph() will |
208 |
+ # add all packages from the system and world sets to the |
209 |
+ # graph. This just allows unresolved conflicts to be |
210 |
+ # detected as early as possible, which makes it possible |
211 |
+ # to avoid calling self._complete_graph() when it is |
212 |
+ # unnecessary due to blockers triggering an abortion. |
213 |
+ if not complete: |
214 |
+ # For packages in the world set, go ahead an uninstall |
215 |
+ # when necessary, as long as the atom will be satisfied |
216 |
+ # in the final state. |
217 |
+ graph_db = self.mydbapi[task.root] |
218 |
+ try: |
219 |
+ for atom in root_config.sets[ |
220 |
+ "world"].iterAtomsForPackage(task): |
221 |
+ satisfied = False |
222 |
+ for cpv in graph_db.match(atom): |
223 |
+ if cpv == inst_pkg.cpv and \ |
224 |
+ inst_pkg in graph_db: |
225 |
+ continue |
226 |
+ satisfied = True |
227 |
+ break |
228 |
+ if not satisfied: |
229 |
+ skip = True |
230 |
+ break |
231 |
+ except portage_exception.InvalidDependString: |
232 |
+ skip = True |
233 |
+ if skip: |
234 |
+ continue |
235 |
+ |
236 |
# Check the deps of parent nodes to ensure that |
237 |
# the chosen task produces a leaf node. Maybe |
238 |
# this can be optimized some more to make the |
239 |
@@ -3778,6 +3801,7 @@ |
240 |
# and uninstallation tasks. |
241 |
uninst_task = None |
242 |
if isinstance(node, Uninstall): |
243 |
+ have_uninstall_task = True |
244 |
uninst_task = node |
245 |
else: |
246 |
vardb = self.trees[node.root]["vartree"].dbapi |
247 |
@@ -3803,24 +3827,32 @@ |
248 |
unresolved = \ |
249 |
self._unresolved_blocker_parents.get(blocker) |
250 |
if unresolved: |
251 |
- self.blocker_parents[blocker] = unresolved |
252 |
+ myblocker_parents[blocker] = unresolved |
253 |
else: |
254 |
- del self.blocker_parents[blocker] |
255 |
+ del myblocker_parents[blocker] |
256 |
|
257 |
if node[-1] != "nomerge": |
258 |
retlist.append(list(node)) |
259 |
mygraph.remove(node) |
260 |
|
261 |
- if not reversed: |
262 |
- """Blocker validation does not work with reverse mode, |
263 |
- so self.altlist() should first be called with reverse disabled |
264 |
- so that blockers are properly validated.""" |
265 |
- self.blocker_digraph = myblockers |
266 |
+ for blocker in myblocker_parents: |
267 |
+ retlist.append(list(blocker)) |
268 |
|
269 |
- """ Add any unresolved blocks so that they can be displayed.""" |
270 |
- for blocker in self.blocker_parents: |
271 |
- retlist.append(list(blocker)) |
272 |
- self._altlist_cache[reversed] = retlist[:] |
273 |
+ # If any Uninstall tasks need to be executed in order |
274 |
+ # to avoid a conflict, complete the graph with any |
275 |
+ # dependencies that may have been initially |
276 |
+ # neglected (to ensure that unsafe Uninstall tasks |
277 |
+ # are properly identified and blocked from execution). |
278 |
+ if have_uninstall_task and \ |
279 |
+ not complete and \ |
280 |
+ not myblocker_parents: |
281 |
+ self.myparams.add("complete") |
282 |
+ if not self._complete_graph(): |
283 |
+ raise self._unknown_internal_error("") |
284 |
+ if not self.validate_blockers(): |
285 |
+ raise self._unknown_internal_error("") |
286 |
+ raise self._serialize_tasks_retry("") |
287 |
+ |
288 |
return retlist |
289 |
|
290 |
def display(self, mylist, favorites=[], verbosity=None): |
291 |
@@ -4718,6 +4750,22 @@ |
292 |
fakedb[myroot].cpv_inject(pkg) |
293 |
self.spinner.update() |
294 |
|
295 |
+ class _unknown_internal_error(portage_exception.PortageException): |
296 |
+ """ |
297 |
+ Used by the depgraph internally to terminate graph creation. |
298 |
+ The specific reason for the failure should have been dumped |
299 |
+ to stderr, unfortunately, the exact reason for the failure |
300 |
+ may not be known. |
301 |
+ """ |
302 |
+ |
303 |
+ class _serialize_tasks_retry(portage_exception.PortageException): |
304 |
+ """ |
305 |
+ This is raised by the _serialize_tasks() method when it needs to |
306 |
+ be called again for some reason. The only case that it's currently |
307 |
+ used for is when neglected dependencies need to be added to the |
308 |
+ graph in order to avoid making a potentially unsafe decision. |
309 |
+ """ |
310 |
+ |
311 |
class _dep_check_composite_db(portage.dbapi): |
312 |
""" |
313 |
A dbapi-like interface that is optimized for use in dep_check() calls. |
314 |
|
315 |
Modified: main/branches/2.1.2/doc/dependency_resolution/task_scheduling.docbook |
316 |
=================================================================== |
317 |
--- main/branches/2.1.2/doc/dependency_resolution/task_scheduling.docbook 2008-04-25 01:53:44 UTC (rev 9965) |
318 |
+++ main/branches/2.1.2/doc/dependency_resolution/task_scheduling.docbook 2008-04-25 02:30:39 UTC (rev 9966) |
319 |
@@ -29,13 +29,14 @@ |
320 |
Installed packages that have been pulled into the current dependency |
321 |
graph will not be uninstalled. Due to |
322 |
<link linkend='dependency-resolution-package-modeling-dependency-neglection'> |
323 |
- dependency neglection</link>, other checks may be necessary in order |
324 |
+ dependency neglection</link> and special properties of packages |
325 |
+ in the "system" set, other checks may be necessary in order |
326 |
to protect inappropriate packages from being uninstalled. |
327 |
</listitem> |
328 |
<listitem> |
329 |
An installed package that is matched by a dependency atom from the |
330 |
"system" set will not be uninstalled in advance since it might not |
331 |
- be safe. Such a package will be uninstalled through replacement. |
332 |
+ be safe. Such a package will be only uninstalled through replacement. |
333 |
</listitem> |
334 |
<listitem> |
335 |
An installed package that is matched by a dependency atom from the |
336 |
|
337 |
-- |
338 |
gentoo-commits@l.g.o mailing list |