1 |
Author: zmedico |
2 |
Date: 2008-03-28 09:12:49 +0000 (Fri, 28 Mar 2008) |
3 |
New Revision: 9530 |
4 |
|
5 |
Modified: |
6 |
main/branches/2.1.2/bin/emerge |
7 |
Log: |
8 |
Bug #201045 - Use a topological sort to create an unmerge order such that |
9 |
each package is unmerged before it's dependencies. This is necessary to |
10 |
avoid breaking things that may need to run during pkg_prerm or pkg_postrm |
11 |
phases. (trunk r9337:9341, 9343, 9344:9347, 9350, 9385, and 9483) |
12 |
|
13 |
|
14 |
Modified: main/branches/2.1.2/bin/emerge |
15 |
=================================================================== |
16 |
--- main/branches/2.1.2/bin/emerge 2008-03-28 08:44:36 UTC (rev 9529) |
17 |
+++ main/branches/2.1.2/bin/emerge 2008-03-28 09:12:49 UTC (rev 9530) |
18 |
@@ -942,7 +942,38 @@ |
19 |
else: |
20 |
yield flag |
21 |
|
22 |
-class DepPriority(object): |
23 |
+class AbstractDepPriority(object): |
24 |
+ __slots__ = ("__weakref__", "buildtime", "runtime", "runtime_post") |
25 |
+ def __init__(self, **kwargs): |
26 |
+ for myattr in chain(self.__slots__, AbstractDepPriority.__slots__): |
27 |
+ if myattr == "__weakref__": |
28 |
+ continue |
29 |
+ myvalue = kwargs.get(myattr, False) |
30 |
+ setattr(self, myattr, myvalue) |
31 |
+ |
32 |
+ def __lt__(self, other): |
33 |
+ return self.__int__() < other |
34 |
+ |
35 |
+ def __le__(self, other): |
36 |
+ return self.__int__() <= other |
37 |
+ |
38 |
+ def __eq__(self, other): |
39 |
+ return self.__int__() == other |
40 |
+ |
41 |
+ def __ne__(self, other): |
42 |
+ return self.__int__() != other |
43 |
+ |
44 |
+ def __gt__(self, other): |
45 |
+ return self.__int__() > other |
46 |
+ |
47 |
+ def __ge__(self, other): |
48 |
+ return self.__int__() >= other |
49 |
+ |
50 |
+ def copy(self): |
51 |
+ import copy |
52 |
+ return copy.copy(self) |
53 |
+ |
54 |
+class DepPriority(AbstractDepPriority): |
55 |
""" |
56 |
This class generates an integer priority level based of various |
57 |
attributes of the dependency relationship. Attributes can be assigned |
58 |
@@ -953,13 +984,16 @@ |
59 |
"buildtime", "runtime", and "system". Various combinations of |
60 |
attributes lead to the following priority levels: |
61 |
|
62 |
- Combination of properties Priority level |
63 |
+ Combination of properties Priority Category |
64 |
|
65 |
- not satisfied and buildtime 0 |
66 |
- not satisfied and runtime -1 |
67 |
- satisfied and buildtime -2 |
68 |
- satisfied and runtime -3 |
69 |
- (none of the above) -4 |
70 |
+ not satisfied and buildtime 0 HARD |
71 |
+ not satisfied and runtime -1 MEDIUM |
72 |
+ not satisfied and runtime_post -2 MEDIUM_SOFT |
73 |
+ satisfied and buildtime and rebuild -3 SOFT |
74 |
+ satisfied and buildtime -4 SOFT |
75 |
+ satisfied and runtime -5 SOFT |
76 |
+ satisfied and runtime_post -6 SOFT |
77 |
+ (none of the above) -6 SOFT |
78 |
|
79 |
Several integer constants are defined for categorization of priority |
80 |
levels: |
81 |
@@ -969,17 +1003,12 @@ |
82 |
SOFT The upper boundary for soft dependencies. |
83 |
MIN The lower boundary for soft dependencies. |
84 |
""" |
85 |
- __slots__ = ("__weakref__", "satisfied", "buildtime", "runtime", "runtime_post", "rebuild") |
86 |
+ __slots__ = ("satisfied", "rebuild") |
87 |
MEDIUM = -1 |
88 |
MEDIUM_SOFT = -2 |
89 |
SOFT = -3 |
90 |
MIN = -6 |
91 |
- def __init__(self, **kwargs): |
92 |
- for myattr in self.__slots__: |
93 |
- if myattr == "__weakref__": |
94 |
- continue |
95 |
- myvalue = kwargs.get(myattr, False) |
96 |
- setattr(self, myattr, myvalue) |
97 |
+ |
98 |
def __int__(self): |
99 |
if not self.satisfied: |
100 |
if self.buildtime: |
101 |
@@ -997,21 +1026,7 @@ |
102 |
if self.runtime_post: |
103 |
return -6 |
104 |
return -6 |
105 |
- def __lt__(self, other): |
106 |
- return self.__int__() < other |
107 |
- def __le__(self, other): |
108 |
- return self.__int__() <= other |
109 |
- def __eq__(self, other): |
110 |
- return self.__int__() == other |
111 |
- def __ne__(self, other): |
112 |
- return self.__int__() != other |
113 |
- def __gt__(self, other): |
114 |
- return self.__int__() > other |
115 |
- def __ge__(self, other): |
116 |
- return self.__int__() >= other |
117 |
- def copy(self): |
118 |
- import copy |
119 |
- return copy.copy(self) |
120 |
+ |
121 |
def __str__(self): |
122 |
myvalue = self.__int__() |
123 |
if myvalue > self.MEDIUM: |
124 |
@@ -1022,6 +1037,35 @@ |
125 |
return "medium-soft" |
126 |
return "soft" |
127 |
|
128 |
+class UnmergeDepPriority(AbstractDepPriority): |
129 |
+ """ |
130 |
+ Combination of properties Priority Category |
131 |
+ |
132 |
+ runtime 0 HARD |
133 |
+ runtime_post -1 HARD |
134 |
+ buildtime -2 SOFT |
135 |
+ (none of the above) -2 SOFT |
136 |
+ """ |
137 |
+ |
138 |
+ MAX = 0 |
139 |
+ SOFT = -2 |
140 |
+ MIN = -2 |
141 |
+ |
142 |
+ def __int__(self): |
143 |
+ if self.runtime: |
144 |
+ return 0 |
145 |
+ if self.runtime_post: |
146 |
+ return -1 |
147 |
+ if self.buildtime: |
148 |
+ return -2 |
149 |
+ return -2 |
150 |
+ |
151 |
+ def __str__(self): |
152 |
+ myvalue = self.__int__() |
153 |
+ if myvalue > self.SOFT: |
154 |
+ return "hard" |
155 |
+ return "soft" |
156 |
+ |
157 |
class FakeVartree(portage.vartree): |
158 |
"""This is implements an in-memory copy of a vartree instance that provides |
159 |
all the interfaces required for use by the depgraph. The vardb is locked |
160 |
@@ -4578,8 +4622,12 @@ |
161 |
print darkgreen(newline+\ |
162 |
">>> These are the packages that would be unmerged:") |
163 |
|
164 |
- pkgmap={} |
165 |
- numselected=0 |
166 |
+ # Preservation of order is required for --depclean and --prune so |
167 |
+ # that dependencies are respected. Use all_selected to eliminate |
168 |
+ # duplicate packages since the same package may be selected by |
169 |
+ # multiple atoms. |
170 |
+ pkgmap = [] |
171 |
+ all_selected = set() |
172 |
for x in candidate_catpkgs: |
173 |
# cycle through all our candidate deps and determine |
174 |
# what will and will not get unmerged |
175 |
@@ -4604,16 +4652,14 @@ |
176 |
portage.writemsg("\n--- Couldn't find '%s' to %s.\n" % \ |
177 |
(x, unmerge_action), noiselevel=-1) |
178 |
continue |
179 |
- mykey = portage.key_expand( |
180 |
- portage.dep_getkey( |
181 |
- mymatch[0]), mydb=vartree.dbapi, settings=settings) |
182 |
- if not pkgmap.has_key(mykey): |
183 |
- pkgmap[mykey]={"protected":[], "selected":[], "omitted":[] } |
184 |
+ pkgmap.append( |
185 |
+ {"protected": set(), "selected": set(), "omitted": set()}) |
186 |
+ mykey = len(pkgmap) - 1 |
187 |
if unmerge_action=="unmerge": |
188 |
for y in mymatch: |
189 |
- if y not in pkgmap[mykey]["selected"]: |
190 |
- pkgmap[mykey]["selected"].append(y) |
191 |
- numselected=numselected+len(mymatch) |
192 |
+ if y not in all_selected: |
193 |
+ pkgmap[mykey]["selected"].add(y) |
194 |
+ all_selected.add(y) |
195 |
elif unmerge_action == "prune": |
196 |
if len(mymatch) == 1: |
197 |
continue |
198 |
@@ -4634,10 +4680,10 @@ |
199 |
best_version = mypkg |
200 |
best_slot = myslot |
201 |
best_counter = mycounter |
202 |
- pkgmap[mykey]["protected"].append(best_version) |
203 |
- pkgmap[mykey]["selected"] = [mypkg for mypkg in mymatch \ |
204 |
- if mypkg != best_version] |
205 |
- numselected = numselected + len(pkgmap[mykey]["selected"]) |
206 |
+ pkgmap[mykey]["protected"].add(best_version) |
207 |
+ pkgmap[mykey]["selected"].update(mypkg for mypkg in mymatch \ |
208 |
+ if mypkg != best_version and mypkg not in all_selected) |
209 |
+ all_selected.update(pkgmap[mykey]["selected"]) |
210 |
else: |
211 |
# unmerge_action == "clean" |
212 |
slotmap={} |
213 |
@@ -4662,10 +4708,13 @@ |
214 |
del counterkeys[-1] |
215 |
#be pretty and get them in order of merge: |
216 |
for ckey in counterkeys: |
217 |
- pkgmap[mykey]["selected"].append(slotmap[myslot][ckey]) |
218 |
- numselected=numselected+1 |
219 |
+ mypkg = slotmap[myslot][ckey] |
220 |
+ if mypkg not in all_selected: |
221 |
+ pkgmap[mykey]["selected"].add(mypkg) |
222 |
+ all_selected.add(mypkg) |
223 |
# ok, now the last-merged package |
224 |
# is protected, and the rest are selected |
225 |
+ numselected = len(all_selected) |
226 |
if global_unmerge and not numselected: |
227 |
portage.writemsg_stdout("\n>>> No outdated packages were found on your system.\n") |
228 |
return 0 |
229 |
@@ -4678,25 +4727,34 @@ |
230 |
finally: |
231 |
if vdb_lock: |
232 |
portage_locks.unlockdir(vdb_lock) |
233 |
- for x in pkgmap: |
234 |
- for y in localtree.dep_match(x): |
235 |
+ for x in xrange(len(pkgmap)): |
236 |
+ selected = pkgmap[x]["selected"] |
237 |
+ if not selected: |
238 |
+ continue |
239 |
+ for mytype, mylist in pkgmap[x].iteritems(): |
240 |
+ if mytype == "selected": |
241 |
+ continue |
242 |
+ mylist.difference_update(all_selected) |
243 |
+ cp = portage.cpv_getkey(iter(selected).next()) |
244 |
+ for y in localtree.dep_match(cp): |
245 |
if y not in pkgmap[x]["omitted"] and \ |
246 |
- y not in pkgmap[x]["selected"] and \ |
247 |
- y not in pkgmap[x]["protected"]: |
248 |
- pkgmap[x]["omitted"].append(y) |
249 |
+ y not in pkgmap[x]["selected"] and \ |
250 |
+ y not in pkgmap[x]["protected"] and \ |
251 |
+ y not in all_selected: |
252 |
+ pkgmap[x]["omitted"].add(y) |
253 |
if global_unmerge and not pkgmap[x]["selected"]: |
254 |
#avoid cluttering the preview printout with stuff that isn't getting unmerged |
255 |
continue |
256 |
- if not (pkgmap[x]["protected"] or pkgmap[x]["omitted"]) and (x in syslist): |
257 |
- print colorize("BAD","\a\n\n!!! '%s' is part of your system profile." % x) |
258 |
+ if not (pkgmap[x]["protected"] or pkgmap[x]["omitted"]) and cp in syslist: |
259 |
+ print colorize("BAD","\a\n\n!!! '%s' is part of your system profile." % cp) |
260 |
print colorize("WARN","\a!!! Unmerging it may be damaging to your system.\n") |
261 |
if "--pretend" not in myopts and "--ask" not in myopts: |
262 |
countdown(int(settings["EMERGE_WARNING_DELAY"]), |
263 |
colorize("UNMERGE_WARN", "Press Ctrl-C to Stop")) |
264 |
if "--quiet" not in myopts: |
265 |
- print "\n "+white(x) |
266 |
+ print "\n "+bold(cp) |
267 |
else: |
268 |
- print white(x)+": ", |
269 |
+ print bold(cp)+": ", |
270 |
for mytype in ["selected","protected","omitted"]: |
271 |
if "--quiet" not in myopts: |
272 |
portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1) |
273 |
@@ -4743,7 +4801,7 @@ |
274 |
if not autoclean: |
275 |
countdown(int(settings["CLEAN_DELAY"]), ">>> Unmerging") |
276 |
|
277 |
- for x in pkgmap: |
278 |
+ for x in xrange(len(pkgmap)): |
279 |
for y in pkgmap[x]["selected"]: |
280 |
print ">>> Unmerging "+y+"..." |
281 |
emergelog(xterm_titles, "=== Unmerging... ("+y+")") |
282 |
@@ -5910,23 +5968,32 @@ |
283 |
if "--quiet" not in myopts: |
284 |
print "\nCalculating dependencies ", |
285 |
|
286 |
- soft = 0 |
287 |
- hard = 1 |
288 |
+ runtime = UnmergeDepPriority(runtime=True) |
289 |
+ runtime_post = UnmergeDepPriority(runtime_post=True) |
290 |
+ buildtime = UnmergeDepPriority(buildtime=True) |
291 |
+ |
292 |
+ priority_map = { |
293 |
+ "RDEPEND": runtime, |
294 |
+ "PDEPEND": runtime_post, |
295 |
+ "DEPEND": buildtime, |
296 |
+ } |
297 |
+ |
298 |
remaining_atoms = [] |
299 |
if action == "depclean": |
300 |
for atom in worldlist: |
301 |
if vardb.match(atom): |
302 |
- remaining_atoms.append((atom, 'world', hard)) |
303 |
+ remaining_atoms.append((atom, 'world', runtime)) |
304 |
for atom in syslist: |
305 |
if vardb.match(atom): |
306 |
- remaining_atoms.append((atom, 'system', hard)) |
307 |
+ remaining_atoms.append((atom, 'system', runtime)) |
308 |
elif action == "prune": |
309 |
for atom in syslist: |
310 |
if vardb.match(atom): |
311 |
- remaining_atoms.append((atom, 'system', hard)) |
312 |
+ remaining_atoms.append((atom, 'system', runtime)) |
313 |
# Pull in everything that's installed since we don't want to prune a |
314 |
# package if something depends on it. |
315 |
- remaining_atoms.extend((atom, 'world', hard) for atom in vardb.cp_all()) |
316 |
+ remaining_atoms.extend( |
317 |
+ (atom, 'world', runtime) for atom in vardb.cp_all()) |
318 |
if not myfiles: |
319 |
# Try to prune everything that's slotted. |
320 |
for cp in vardb.cp_all(): |
321 |
@@ -5935,14 +6002,15 @@ |
322 |
|
323 |
unresolveable = {} |
324 |
aux_keys = ["DEPEND", "RDEPEND", "PDEPEND"] |
325 |
- metadata_keys = ["PROVIDE", "SLOT", "USE"] |
326 |
+ metadata_keys = depgraph._mydbapi_keys |
327 |
graph = digraph() |
328 |
+ with_bdeps = myopts.get("--with-bdeps", "y") == "y" |
329 |
|
330 |
while remaining_atoms: |
331 |
atom, parent, priority = remaining_atoms.pop() |
332 |
pkgs = vardb.match(atom) |
333 |
if not pkgs: |
334 |
- if not atom.startswith("!") and priority == hard: |
335 |
+ if priority > UnmergeDepPriority.SOFT: |
336 |
unresolveable.setdefault(atom, []).append(parent) |
337 |
continue |
338 |
if action == "depclean" and parent == "world" and myfiles: |
339 |
@@ -5966,44 +6034,42 @@ |
340 |
filtered_pkgs.append(pkg) |
341 |
pkgs = filtered_pkgs |
342 |
if len(pkgs) > 1: |
343 |
- # Prune all but the best matching slot, since that's all that a |
344 |
- # deep world update would pull in. Don't prune if this atom comes |
345 |
- # directly from world though, since world atoms are greedy when |
346 |
- # they don't specify a slot. |
347 |
- visible_in_portdb = [cpv for cpv in pkgs if portdb.match("="+cpv)] |
348 |
- if visible_in_portdb: |
349 |
- # For consistency with the update algorithm, keep the highest |
350 |
- # visible version and prune any versions that are either masked |
351 |
- # or no longer exist in the portage tree. |
352 |
- pkgs = visible_in_portdb |
353 |
- pkgs = [portage.best(pkgs)] |
354 |
+ # For consistency with the update algorithm, keep the highest |
355 |
+ # visible version and prune any versions that are old or masked. |
356 |
+ for cpv in reversed(pkgs): |
357 |
+ metadata = dict(izip(metadata_keys, |
358 |
+ vardb.aux_get(cpv, metadata_keys))) |
359 |
+ if visible(settings, cpv, metadata, |
360 |
+ built=True, installed=True): |
361 |
+ pkgs = [cpv] |
362 |
+ break |
363 |
+ if len(pkgs) > 1: |
364 |
+ # They're all masked, so just keep the highest version. |
365 |
+ pkgs = [pkgs[-1]] |
366 |
for pkg in pkgs: |
367 |
- graph.add(pkg, parent) |
368 |
+ graph.add(pkg, parent, priority=priority) |
369 |
if fakedb.cpv_exists(pkg): |
370 |
continue |
371 |
spinner.update() |
372 |
fakedb.cpv_inject(pkg) |
373 |
myaux = dict(izip(aux_keys, vardb.aux_get(pkg, aux_keys))) |
374 |
mydeps = [] |
375 |
- if myopts.get("--with-bdeps", "y") == "y": |
376 |
- mydeps.append((myaux["DEPEND"], soft)) |
377 |
- del myaux["DEPEND"] |
378 |
- mydeps.append((" ".join(myaux.values()), hard)) |
379 |
+ |
380 |
usedef = vardb.aux_get(pkg, ["USE"])[0].split() |
381 |
- for depstr, priority in mydeps: |
382 |
+ for dep_type, depstr in myaux.iteritems(): |
383 |
|
384 |
if not depstr: |
385 |
continue |
386 |
|
387 |
+ if not with_bdeps and dep_type == "DEPEND": |
388 |
+ continue |
389 |
+ |
390 |
+ priority = priority_map[dep_type] |
391 |
if "--debug" in myopts: |
392 |
print |
393 |
print "Parent: ", pkg |
394 |
print "Depstring:", depstr |
395 |
- print "Priority:", |
396 |
- if priority == soft: |
397 |
- print "soft" |
398 |
- else: |
399 |
- print "hard" |
400 |
+ print "Priority:", priority |
401 |
|
402 |
try: |
403 |
portage_dep._dep_check_strict = False |
404 |
@@ -6021,6 +6087,8 @@ |
405 |
print "Candidates:", atoms |
406 |
|
407 |
for atom in atoms: |
408 |
+ if atom.startswith("!"): |
409 |
+ continue |
410 |
remaining_atoms.append((atom, pkg, priority)) |
411 |
|
412 |
if "--quiet" not in myopts: |
413 |
@@ -6108,6 +6176,81 @@ |
414 |
good("--nodeps")) |
415 |
|
416 |
if len(cleanlist): |
417 |
+ # Use a topological sort to create an unmerge order such that |
418 |
+ # each package is unmerged before it's dependencies. This is |
419 |
+ # necessary to avoid breaking things that may need to run |
420 |
+ # during pkg_prerm or pkg_postrm phases. |
421 |
+ |
422 |
+ # Create a new graph to account for dependencies between the |
423 |
+ # packages being unmerged. |
424 |
+ graph = digraph() |
425 |
+ clean_set = set(cleanlist) |
426 |
+ del cleanlist[:] |
427 |
+ for node in clean_set: |
428 |
+ graph.add(node, None) |
429 |
+ myaux = dict(izip(aux_keys, vardb.aux_get(node, aux_keys))) |
430 |
+ mydeps = [] |
431 |
+ usedef = vardb.aux_get(pkg, ["USE"])[0].split() |
432 |
+ for dep_type, depstr in myaux.iteritems(): |
433 |
+ if not depstr: |
434 |
+ continue |
435 |
+ try: |
436 |
+ portage_dep._dep_check_strict = False |
437 |
+ success, atoms = portage.dep_check(depstr, None, settings, |
438 |
+ myuse=usedef, trees=dep_check_trees, myroot=myroot) |
439 |
+ finally: |
440 |
+ portage_dep._dep_check_strict = True |
441 |
+ if not success: |
442 |
+ show_invalid_depstring_notice( |
443 |
+ ("installed", myroot, node, "nomerge"), |
444 |
+ depstr, atoms) |
445 |
+ return |
446 |
+ |
447 |
+ priority = priority_map[dep_type] |
448 |
+ for atom in atoms: |
449 |
+ if atom.startswith("!"): |
450 |
+ continue |
451 |
+ matches = vardb.match(atom) |
452 |
+ if not matches: |
453 |
+ continue |
454 |
+ for cpv in matches: |
455 |
+ if cpv in clean_set: |
456 |
+ graph.add(cpv, node, priority=priority) |
457 |
+ |
458 |
+ if len(graph.order) == len(graph.root_nodes()): |
459 |
+ # If there are no dependencies between packages |
460 |
+ # then just unmerge them alphabetically. |
461 |
+ cleanlist = graph.order[:] |
462 |
+ cleanlist.sort() |
463 |
+ else: |
464 |
+ # Order nodes from lowest to highest overall reference count for |
465 |
+ # optimal root node selection. |
466 |
+ node_refcounts = {} |
467 |
+ for node in graph.order: |
468 |
+ node_refcounts[node] = len(graph.parent_nodes(node)) |
469 |
+ def cmp_reference_count(node1, node2): |
470 |
+ return node_refcounts[node1] - node_refcounts[node2] |
471 |
+ graph.order.sort(cmp_reference_count) |
472 |
+ |
473 |
+ ignore_priority_range = [None] |
474 |
+ ignore_priority_range.extend( |
475 |
+ xrange(UnmergeDepPriority.MIN, UnmergeDepPriority.MAX + 1)) |
476 |
+ while not graph.empty(): |
477 |
+ for ignore_priority in ignore_priority_range: |
478 |
+ nodes = graph.root_nodes(ignore_priority=ignore_priority) |
479 |
+ if nodes: |
480 |
+ break |
481 |
+ if not nodes: |
482 |
+ raise AssertionError("no root nodes") |
483 |
+ if ignore_priority is not None: |
484 |
+ # Some deps have been dropped due to circular dependencies, |
485 |
+ # so only pop one node in order do minimize the number that |
486 |
+ # are dropped. |
487 |
+ del nodes[1:] |
488 |
+ for node in nodes: |
489 |
+ graph.remove(node) |
490 |
+ cleanlist.append(node) |
491 |
+ |
492 |
unmerge(settings, myopts, trees[settings["ROOT"]]["vartree"], |
493 |
"unmerge", cleanlist, ldpath_mtimes) |
494 |
|
495 |
|
496 |
-- |
497 |
gentoo-commits@l.g.o mailing list |