Gentoo Archives: gentoo-commits

From: "Zac Medico (zmedico)" <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] portage r9530 - main/branches/2.1.2/bin
Date: Fri, 28 Mar 2008 09:12:53
Message-Id: E1JfAdm-0004FG-BQ@stork.gentoo.org
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