1 |
Author: grobian |
2 |
Date: 2008-07-23 06:54:24 +0000 (Wed, 23 Jul 2008) |
3 |
New Revision: 11172 |
4 |
|
5 |
Modified: |
6 |
main/branches/prefix/NEWS |
7 |
main/branches/prefix/man/emerge.1 |
8 |
main/branches/prefix/pym/_emerge/__init__.py |
9 |
main/branches/prefix/pym/_emerge/help.py |
10 |
main/branches/prefix/pym/portage/__init__.py |
11 |
main/branches/prefix/pym/portage/dbapi/vartree.py |
12 |
Log: |
13 |
Merged from trunk 11157:11169 |
14 |
|
15 |
| 11158 | Fix --depclean/--prune to add lib providers and their | |
16 |
| zmedico | dependencies to the graph and create a new clean list when | |
17 |
| | necessary. This completes the fix for bug #230053. | |
18 |
|
19 |
| 11159 | Adjust --fetchonly failure messages to work better with | |
20 |
| zmedico | background mode. | |
21 |
|
22 |
| 11160 | Adjust status messages for --fetchonly mode. | |
23 |
| zmedico | | |
24 |
|
25 |
| 11161 | Redirect dblink._preserve_libs() messages to the log when in | |
26 |
| zmedico | background mode. | |
27 |
|
28 |
| 11162 | Make --pretend disable background mode and imply --jobs=1. | |
29 |
| zmedico | | |
30 |
|
31 |
| 11163 | Disable "Installing" status messages when in --pretend or | |
32 |
| zmedico | --buildpkgonly mode. | |
33 |
|
34 |
| 11164 | Redirect build log eqawarn messages to the log file when in | |
35 |
| zmedico | background mode. | |
36 |
|
37 |
| 11165 | Show the number of failed packages in the status display. | |
38 |
| zmedico | | |
39 |
|
40 |
| 11166 | Fix resume_depgraph() so that it doesn't raise an | |
41 |
| zmedico | AssertionError due to unsatisfied PDEPEND. | |
42 |
|
43 |
| 11167 | In dblink.unmerge(), redirect preserve-libs "!needed" | |
44 |
| zmedico | messages to the log file when in background mode. | |
45 |
|
46 |
| 11168 | Update --depclean and --prune, removing warnings about | |
47 |
| zmedico | libraries since those are now automatically accounted for. | |
48 |
|
49 |
| 11169 | Handle UnsatisfiedResumeDep in | |
50 |
| zmedico | Scheduler._calc_resume_list(). | |
51 |
|
52 |
|
53 |
Modified: main/branches/prefix/NEWS |
54 |
=================================================================== |
55 |
--- main/branches/prefix/NEWS 2008-07-23 06:52:10 UTC (rev 11171) |
56 |
+++ main/branches/prefix/NEWS 2008-07-23 06:54:24 UTC (rev 11172) |
57 |
@@ -3,6 +3,8 @@ |
58 |
portage-2.2 |
59 |
------------- |
60 |
|
61 |
+* Add link level dependency awareness to emerge --depclean and --prune actions |
62 |
+ in order to protect against uninstallation of required libraries. |
63 |
* Add emerge --jobs and --load-average options which specify behavior |
64 |
for building packages in parallel or for generating metadata in parallel |
65 |
with emerge --regen. |
66 |
|
67 |
Modified: main/branches/prefix/man/emerge.1 |
68 |
=================================================================== |
69 |
--- main/branches/prefix/man/emerge.1 2008-07-23 06:52:10 UTC (rev 11171) |
70 |
+++ main/branches/prefix/man/emerge.1 2008-07-23 06:54:24 UTC (rev 11172) |
71 |
@@ -103,19 +103,22 @@ |
72 |
.BR \-\-depclean |
73 |
Cleans the system by removing packages that are not associated |
74 |
with explicitly merged packages. Depclean works by creating the |
75 |
-full dependency tree from the system and world sets, |
76 |
+full dependency tree from the @system and @world sets, |
77 |
then comparing it to installed packages. Packages installed, but |
78 |
not part of the dependency tree, will be uninstalled by depclean. |
79 |
-Inexperienced users are advised to use \fB\-\-pretend\fR |
80 |
-with this option in order to see a preview of which packages |
81 |
-will be uninstalled. |
82 |
+See \fB\-\-with\-bdeps\fR for behavior with respect to build time dependencies |
83 |
+that are not strictly required. Packages that are part of the world set will |
84 |
+always be kept. They can be manually added to this set with \fIemerge |
85 |
+\-\-noreplace <atom>\fR. As a safety measure, depclean will not remove any |
86 |
+packages unless *all* required dependencies have been resolved. As a |
87 |
+consequence, it is often necessary to run \fIemerge \-\-update \-\-newuse |
88 |
+\-\-deep \-\-oneshot @system @world\fR prior to depclean. |
89 |
|
90 |
-\fBWARNING: Removing some |
91 |
-packages may cause packages which link to the removed package |
92 |
-to stop working and complain about missing libraries.\fR |
93 |
-Rebuild the complaining package to fix this issue. Also see |
94 |
-\fB\-\-with\-bdeps\fR for behavior with respect to build time dependencies that |
95 |
-are not strictly required. Note that packages listed in |
96 |
+\fBWARNING:\fR |
97 |
+Inexperienced users are advised to use \fB\-\-pretend\fR with this |
98 |
+option in order to see a preview of which packages |
99 |
+will be uninstalled. Always study the list of packages |
100 |
+to be cleaned for any obvious mistakes. Note that packages listed in |
101 |
package.provided (see \fBportage\fR(5)) may be removed by |
102 |
depclean, even if they are part of the world set. |
103 |
|
104 |
@@ -150,11 +153,9 @@ |
105 |
.TP |
106 |
.BR "\-\-prune " (\fB\-P\fR) |
107 |
\fBWARNING: This action can remove important packages!\fR Removes all but the |
108 |
-highest installed version of a package from your system. This action doesn't |
109 |
-verify the possible binary compatibility between versions and can thus remove |
110 |
-essential dependencies from your system. Use \fB\-\-prune\fR together with |
111 |
-\fB\-\-verbose\fR to show reverse dependencies or with \fB\-\-nodeps\fR to |
112 |
-ignore all dependencies. |
113 |
+highest installed version of a package from your system. Use \fB\-\-prune\fR |
114 |
+together with \fB\-\-verbose\fR to show reverse dependencies or with |
115 |
+\fB\-\-nodeps\fR to ignore all dependencies. |
116 |
.TP |
117 |
.BR \-\-regen |
118 |
Causes portage to check and update the dependency cache of all ebuilds in the |
119 |
|
120 |
Modified: main/branches/prefix/pym/_emerge/__init__.py |
121 |
=================================================================== |
122 |
--- main/branches/prefix/pym/_emerge/__init__.py 2008-07-23 06:52:10 UTC (rev 11171) |
123 |
+++ main/branches/prefix/pym/_emerge/__init__.py 2008-07-23 06:54:24 UTC (rev 11172) |
124 |
@@ -29,6 +29,7 @@ |
125 |
import select |
126 |
import shlex |
127 |
import shutil |
128 |
+import textwrap |
129 |
import urlparse |
130 |
import weakref |
131 |
import gc |
132 |
@@ -2429,8 +2430,9 @@ |
133 |
|
134 |
if opts.fetchonly: |
135 |
if self._final_exit(fetcher) != os.EX_OK: |
136 |
- eerror("!!! Fetch for %s failed, continuing..." % pkg.cpv, |
137 |
- phase="unpack", key=pkg.cpv) |
138 |
+ if not self.background: |
139 |
+ eerror("Fetch for %s failed, continuing..." % pkg.cpv, |
140 |
+ phase="unpack", key=pkg.cpv) |
141 |
self.wait() |
142 |
return |
143 |
|
144 |
@@ -2761,7 +2763,17 @@ |
145 |
def _ebuild_exit(self, ebuild_process): |
146 |
|
147 |
if self.phase == "install": |
148 |
- portage._check_build_log(self.settings) |
149 |
+ out = None |
150 |
+ log_path = self.settings.get("PORTAGE_LOG_FILE") |
151 |
+ log_file = None |
152 |
+ if self.background and log_path is not None: |
153 |
+ log_file = open(log_path, 'a') |
154 |
+ out = log_file |
155 |
+ try: |
156 |
+ portage._check_build_log(self.settings, out=out) |
157 |
+ finally: |
158 |
+ if log_file is not None: |
159 |
+ log_file.close() |
160 |
|
161 |
if self._default_exit(ebuild_process) != os.EX_OK: |
162 |
self.wait() |
163 |
@@ -3391,6 +3403,9 @@ |
164 |
if pkg.type_name == "binary": |
165 |
action_desc = "Extracting" |
166 |
|
167 |
+ if build_opts.fetchonly: |
168 |
+ action_desc = "Fetching" |
169 |
+ |
170 |
if not build_opts.pretend: |
171 |
|
172 |
self.statusMessage("%s (%s of %s) %s %s %s" % \ |
173 |
@@ -3499,9 +3514,12 @@ |
174 |
action_desc = "Installing" |
175 |
preposition = "to" |
176 |
|
177 |
- self.merge.statusMessage("%s %s %s %s" % \ |
178 |
- (action_desc, colorize("GOOD", pkg.cpv), |
179 |
- preposition, pkg.root)) |
180 |
+ if not self.merge.build_opts.fetchonly and \ |
181 |
+ not self.merge.build_opts.pretend and \ |
182 |
+ not self.merge.build_opts.buildpkgonly: |
183 |
+ self.merge.statusMessage("%s %s %s %s" % \ |
184 |
+ (action_desc, colorize("GOOD", pkg.cpv), |
185 |
+ preposition, pkg.root)) |
186 |
|
187 |
self.returncode = self.merge.merge() |
188 |
self.wait() |
189 |
@@ -8448,8 +8466,8 @@ |
190 |
|
191 |
class JobStatusDisplay(object): |
192 |
|
193 |
- _bound_properties = ("curval", "running") |
194 |
- _jobs_column_width = 42 |
195 |
+ _bound_properties = ("curval", "failed", "running") |
196 |
+ _jobs_column_width = 48 |
197 |
|
198 |
# Don't update the display unless at least this much |
199 |
# time has passed, in units of seconds. |
200 |
@@ -8577,6 +8595,7 @@ |
201 |
|
202 |
def _property_change(self, name, old_value, new_value): |
203 |
self._changed = True |
204 |
+ self.display() |
205 |
|
206 |
def _load_avg_str(self, digits=2): |
207 |
try: |
208 |
@@ -8613,7 +8632,7 @@ |
209 |
curval_str = str(self.curval) |
210 |
maxval_str = str(self.maxval) |
211 |
running_str = str(self.running) |
212 |
- merges_str = str(self.merges) |
213 |
+ failed_str = str(self.failed) |
214 |
load_avg_str = self._load_avg_str() |
215 |
|
216 |
color_output = StringIO.StringIO() |
217 |
@@ -8642,21 +8661,18 @@ |
218 |
f.pop_style() |
219 |
f.add_literal_data(" running") |
220 |
|
221 |
- #if self.merges: |
222 |
- if False: |
223 |
+ if self.failed: |
224 |
f.add_literal_data(", ") |
225 |
f.push_style(number_style) |
226 |
- f.add_literal_data(merges_str) |
227 |
+ f.add_literal_data(failed_str) |
228 |
f.pop_style() |
229 |
- f.add_literal_data(" merge") |
230 |
- if self.merges != 1: |
231 |
- f.add_literal_data("s") |
232 |
+ f.add_literal_data(" failed") |
233 |
|
234 |
padding = self._jobs_column_width - len(plain_output.getvalue()) |
235 |
if padding > 0: |
236 |
f.add_literal_data(padding * " ") |
237 |
|
238 |
- f.add_literal_data("Load average: ") |
239 |
+ f.add_literal_data("Load avg: ") |
240 |
f.add_literal_data(load_avg_str) |
241 |
|
242 |
self._update(color_output.getvalue()) |
243 |
@@ -8843,7 +8859,8 @@ |
244 |
@rtype: bool |
245 |
@returns: True if background mode is enabled, False otherwise. |
246 |
""" |
247 |
- background = self._max_jobs > 1 or "--quiet" in self.myopts |
248 |
+ background = (self._max_jobs > 1 or "--quiet" in self.myopts) and \ |
249 |
+ "--pretend" not in self.myopts |
250 |
|
251 |
self._status_display.quiet = \ |
252 |
not background or \ |
253 |
@@ -9116,16 +9133,27 @@ |
254 |
"--fetch-all-uri" in self.myopts): |
255 |
return |
256 |
|
257 |
- sys.stderr.write("\n\n!!! Some fetch errors were " + \ |
258 |
- "encountered. Please see above for details.\n\n") |
259 |
+ if self._background: |
260 |
+ msg = "Some fetch errors were " + \ |
261 |
+ "encountered. Please see %s for details." % \ |
262 |
+ self._fetch_log |
263 |
+ else: |
264 |
+ msg = "Some fetch errors were " + \ |
265 |
+ "encountered. Please see above for details." |
266 |
|
267 |
+ prefix = bad(" * ") |
268 |
+ msg = "".join("%s%s\n" % (prefix, line) \ |
269 |
+ for line in textwrap.wrap(msg, 70)) |
270 |
+ writemsg_level(msg, level=logging.ERROR, noiselevel=-1) |
271 |
+ |
272 |
+ msg = [] |
273 |
+ msg.append("") |
274 |
for cpv in failed_fetches: |
275 |
- sys.stderr.write(" ") |
276 |
- sys.stderr.write(cpv) |
277 |
- sys.stderr.write("\n") |
278 |
+ msg.append(" %s" % cpv) |
279 |
+ msg.append("") |
280 |
+ writemsg_level("".join("%s%s\n" % (prefix, line) \ |
281 |
+ for line in msg), level=logging.ERROR, noiselevel=-1) |
282 |
|
283 |
- sys.stderr.write("\n") |
284 |
- |
285 |
def _is_restart_scheduled(self): |
286 |
""" |
287 |
Check if the merge list contains a replacement |
288 |
@@ -9254,6 +9282,9 @@ |
289 |
break |
290 |
|
291 |
dropped_tasks = self._calc_resume_list() |
292 |
+ if dropped_tasks is None: |
293 |
+ break |
294 |
+ |
295 |
clear_caches(self.trees) |
296 |
if not self._mergelist: |
297 |
break |
298 |
@@ -9349,6 +9380,7 @@ |
299 |
pkg = merge.merge.pkg |
300 |
if merge.returncode != os.EX_OK: |
301 |
self._failed_pkgs.append((pkg, merge.returncode)) |
302 |
+ self._status_display.failed = len(self._failed_pkgs) |
303 |
return |
304 |
|
305 |
self._task_complete(pkg) |
306 |
@@ -9383,10 +9415,10 @@ |
307 |
self._status_display.merges = len(self._task_queues.merge) |
308 |
else: |
309 |
self._failed_pkgs.append((build.pkg, build.returncode)) |
310 |
+ self._status_display.failed = len(self._failed_pkgs) |
311 |
self._deallocate_config(build.settings) |
312 |
self._jobs -= 1 |
313 |
self._status_display.running = self._jobs |
314 |
- self._status_display.display() |
315 |
self._schedule() |
316 |
|
317 |
def _extract_exit(self, build): |
318 |
@@ -9498,7 +9530,8 @@ |
319 |
|
320 |
# Only allow 1 job max if a restart is scheduled |
321 |
# due to portage update. |
322 |
- if self._is_restart_scheduled(): |
323 |
+ if self._is_restart_scheduled() or \ |
324 |
+ "--pretend" in self.myopts: |
325 |
self._set_max_jobs(1) |
326 |
|
327 |
merge_queue = self._task_queues.merge |
328 |
@@ -9620,6 +9653,9 @@ |
329 |
""" |
330 |
Use the current resume list to calculate a new one, |
331 |
dropping any packages with unsatisfied deps. |
332 |
+ @rtype: set |
333 |
+ @returns: a possibly empty set of dropped tasks, or |
334 |
+ None if an error occurs. |
335 |
""" |
336 |
print colorize("GOOD", "*** Resuming merge...") |
337 |
|
338 |
@@ -9641,13 +9677,47 @@ |
339 |
print "Calculating dependencies ", |
340 |
|
341 |
myparams = create_depgraph_params(self.myopts, None) |
342 |
- success, mydepgraph, dropped_tasks = resume_depgraph( |
343 |
- self.settings, self.trees, self._mtimedb, self.myopts, |
344 |
- myparams, self._spinner, skip_unsatisfied=True) |
345 |
+ success = False |
346 |
+ e = None |
347 |
+ try: |
348 |
+ success, mydepgraph, dropped_tasks = resume_depgraph( |
349 |
+ self.settings, self.trees, self._mtimedb, self.myopts, |
350 |
+ myparams, self._spinner, skip_unsatisfied=True) |
351 |
+ except depgraph.UnsatisfiedResumeDep, e: |
352 |
+ mydepgraph = e.depgraph |
353 |
+ dropped_tasks = set() |
354 |
|
355 |
if show_spinner: |
356 |
print "\b\b... done!" |
357 |
|
358 |
+ if e is not None: |
359 |
+ mydepgraph.display_problems() |
360 |
+ out = portage.output.EOutput() |
361 |
+ out.eerror("One or packages are either masked or " + \ |
362 |
+ "have missing dependencies:") |
363 |
+ out.eerror("") |
364 |
+ indent = " " |
365 |
+ for dep in e.value: |
366 |
+ if dep.atom is None: |
367 |
+ out.eerror(indent + "Masked package:") |
368 |
+ out.eerror(2 * indent + str(dep.parent)) |
369 |
+ out.eerror("") |
370 |
+ else: |
371 |
+ out.eerror(indent + str(dep.atom) + " pulled in by:") |
372 |
+ out.eerror(2 * indent + str(dep.parent)) |
373 |
+ out.eerror("") |
374 |
+ msg = "The resume list contains packages " + \ |
375 |
+ "that are either masked or have " + \ |
376 |
+ "unsatisfied dependencies. " + \ |
377 |
+ "Please restart/continue " + \ |
378 |
+ "the operation manually, or use --skipfirst " + \ |
379 |
+ "to skip the first package in the list and " + \ |
380 |
+ "any other packages that may be " + \ |
381 |
+ "masked or have missing dependencies." |
382 |
+ for line in textwrap.wrap(msg, 72): |
383 |
+ out.eerror(line) |
384 |
+ return None |
385 |
+ |
386 |
if self._show_list(): |
387 |
mylist = mydepgraph.altlist() |
388 |
if "--tree" in self.myopts: |
389 |
@@ -9656,7 +9726,7 @@ |
390 |
|
391 |
mydepgraph.display_problems() |
392 |
if not success: |
393 |
- return (None, None) |
394 |
+ return None |
395 |
|
396 |
mylist = mydepgraph.altlist() |
397 |
mydepgraph.break_refs(mylist) |
398 |
@@ -11515,8 +11585,10 @@ |
399 |
msg.append("\n") |
400 |
msg.append("As a safety measure, depclean will not remove any packages\n") |
401 |
msg.append("unless *all* required dependencies have been resolved. As a\n") |
402 |
- msg.append("consequence, it is often necessary to run\n") |
403 |
- msg.append(good("`emerge --update --newuse --deep world`") + " prior to depclean.\n") |
404 |
+ msg.append("consequence, it is often necessary to run %s\n" % \ |
405 |
+ good("`emerge --update")) |
406 |
+ msg.append(good("--newuse --deep --oneshot @system @world`") + \ |
407 |
+ " prior to depclean.\n") |
408 |
|
409 |
if action == "depclean" and "--quiet" not in myopts and not myfiles: |
410 |
portage.writemsg_stdout("\n") |
411 |
@@ -11691,30 +11763,46 @@ |
412 |
if not success: |
413 |
return 1 |
414 |
|
415 |
- unresolveable = set() |
416 |
- for dep in resolver._initially_unsatisfied_deps: |
417 |
- if isinstance(dep.parent, Package): |
418 |
- unresolveable.add((dep.atom, dep.parent.cpv)) |
419 |
+ def unresolved_deps(): |
420 |
|
421 |
- if unresolveable and not allow_missing_deps: |
422 |
- print "Dependencies could not be completely resolved due to" |
423 |
- print "the following required packages not being installed:" |
424 |
- print |
425 |
- for atom, parent in unresolveable: |
426 |
- print atom, "required by", str(parent) |
427 |
- if unresolveable and not allow_missing_deps: |
428 |
- print |
429 |
- print "Have you forgotten to run " + good("`emerge --update --newuse --deep world`") + " prior to" |
430 |
- print "%s? It may be necessary to manually uninstall packages that no longer" % action |
431 |
- print "exist in the portage tree since it may not be possible to satisfy their" |
432 |
- print "dependencies. Also, be aware of the --with-bdeps option that is documented" |
433 |
- print "in " + good("`man emerge`") + "." |
434 |
- print |
435 |
- if action == "prune": |
436 |
- print "If you would like to ignore dependencies then use %s." % \ |
437 |
- good("--nodeps") |
438 |
- return |
439 |
+ unresolvable = set() |
440 |
+ for dep in resolver._initially_unsatisfied_deps: |
441 |
+ if isinstance(dep.parent, Package): |
442 |
+ unresolvable.add((dep.atom, dep.parent.cpv)) |
443 |
+ if not unresolvable: |
444 |
+ return False |
445 |
|
446 |
+ if unresolvable and not allow_missing_deps: |
447 |
+ prefix = bad(" * ") |
448 |
+ msg = [] |
449 |
+ msg.append("Dependencies could not be completely resolved due to") |
450 |
+ msg.append("the following required packages not being installed:") |
451 |
+ msg.append("") |
452 |
+ for atom, parent in unresolvable: |
453 |
+ msg.append(" %s pulled in by:" % (atom,)) |
454 |
+ msg.append(" %s" % (parent,)) |
455 |
+ msg.append("") |
456 |
+ msg.append("Have you forgotten to run " + \ |
457 |
+ good("`emerge --update --newuse --deep world`") + " prior to") |
458 |
+ msg.append(("%s? It may be necessary to manually " + \ |
459 |
+ "uninstall packages that no longer") % action) |
460 |
+ msg.append("exist in the portage tree since " + \ |
461 |
+ "it may not be possible to satisfy their") |
462 |
+ msg.append("dependencies. Also, be aware of " + \ |
463 |
+ "the --with-bdeps option that is documented") |
464 |
+ msg.append("in " + good("`man emerge`") + ".") |
465 |
+ if action == "prune": |
466 |
+ msg.append("") |
467 |
+ msg.append("If you would like to ignore " + \ |
468 |
+ "dependencies then use %s." % good("--nodeps")) |
469 |
+ writemsg_level("".join("%s%s\n" % (prefix, line) for line in msg), |
470 |
+ level=logging.ERROR, noiselevel=-1) |
471 |
+ return True |
472 |
+ return False |
473 |
+ |
474 |
+ if unresolved_deps(): |
475 |
+ return 1 |
476 |
+ |
477 |
graph = resolver.digraph.copy() |
478 |
required_pkgs_total = 0 |
479 |
for node in graph: |
480 |
@@ -11739,50 +11827,61 @@ |
481 |
msg.append("\n") |
482 |
portage.writemsg_stdout("".join(msg), noiselevel=-1) |
483 |
|
484 |
- cleanlist = [] |
485 |
- if action == "depclean": |
486 |
- if args_set: |
487 |
- for pkg in vardb: |
488 |
- arg_atom = None |
489 |
- try: |
490 |
- arg_atom = args_set.findAtomForPackage(pkg) |
491 |
- except portage.exception.InvalidDependString: |
492 |
- # this error has already been displayed by now |
493 |
- continue |
494 |
- if arg_atom: |
495 |
+ def create_cleanlist(): |
496 |
+ pkgs_to_remove = [] |
497 |
+ |
498 |
+ if action == "depclean": |
499 |
+ if args_set: |
500 |
+ |
501 |
+ for pkg in vardb: |
502 |
+ arg_atom = None |
503 |
+ try: |
504 |
+ arg_atom = args_set.findAtomForPackage(pkg) |
505 |
+ except portage.exception.InvalidDependString: |
506 |
+ # this error has already been displayed by now |
507 |
+ continue |
508 |
+ |
509 |
+ if arg_atom: |
510 |
+ if pkg not in graph: |
511 |
+ pkgs_to_remove.append(pkg) |
512 |
+ elif "--verbose" in myopts: |
513 |
+ show_parents(pkg) |
514 |
+ |
515 |
+ else: |
516 |
+ for pkg in vardb: |
517 |
if pkg not in graph: |
518 |
- cleanlist.append(pkg) |
519 |
+ pkgs_to_remove.append(pkg) |
520 |
elif "--verbose" in myopts: |
521 |
show_parents(pkg) |
522 |
- else: |
523 |
- for pkg in vardb: |
524 |
- if pkg not in graph: |
525 |
- cleanlist.append(pkg) |
526 |
- elif "--verbose" in myopts: |
527 |
- show_parents(pkg) |
528 |
- elif action == "prune": |
529 |
- # Prune really uses all installed instead of world. It's not a real |
530 |
- # reverse dependency so don't display it as such. |
531 |
- graph.remove(set_args["world"]) |
532 |
- for atom in args_set: |
533 |
- for pkg in vardb.match_pkgs(atom): |
534 |
- if pkg not in graph: |
535 |
- cleanlist.append(pkg) |
536 |
- elif "--verbose" in myopts: |
537 |
- show_parents(pkg) |
538 |
|
539 |
- if not cleanlist: |
540 |
- portage.writemsg_stdout( |
541 |
- ">>> No packages selected for removal by %s\n" % action) |
542 |
- if "--verbose" not in myopts: |
543 |
- portage.writemsg_stdout( |
544 |
- ">>> To see reverse dependencies, use %s\n" % \ |
545 |
- good("--verbose")) |
546 |
- if action == "prune": |
547 |
- portage.writemsg_stdout( |
548 |
- ">>> To ignore dependencies, use %s\n" % \ |
549 |
- good("--nodeps")) |
550 |
+ elif action == "prune": |
551 |
+ # Prune really uses all installed instead of world. It's not |
552 |
+ # a real reverse dependency so don't display it as such. |
553 |
+ graph.remove(set_args["world"]) |
554 |
|
555 |
+ for atom in args_set: |
556 |
+ for pkg in vardb.match_pkgs(atom): |
557 |
+ if pkg not in graph: |
558 |
+ pkgs_to_remove.append(pkg) |
559 |
+ elif "--verbose" in myopts: |
560 |
+ show_parents(pkg) |
561 |
+ |
562 |
+ if not pkgs_to_remove: |
563 |
+ writemsg_level( |
564 |
+ ">>> No packages selected for removal by %s\n" % action) |
565 |
+ if "--verbose" not in myopts: |
566 |
+ writemsg_level( |
567 |
+ ">>> To see reverse dependencies, use %s\n" % \ |
568 |
+ good("--verbose")) |
569 |
+ if action == "prune": |
570 |
+ writemsg_level( |
571 |
+ ">>> To ignore dependencies, use %s\n" % \ |
572 |
+ good("--nodeps")) |
573 |
+ |
574 |
+ return pkgs_to_remove |
575 |
+ |
576 |
+ cleanlist = create_cleanlist() |
577 |
+ |
578 |
if len(cleanlist): |
579 |
clean_set = set(cleanlist) |
580 |
|
581 |
@@ -11933,9 +12032,37 @@ |
582 |
msg.append("") |
583 |
writemsg_level("".join(prefix + "%s\n" % line for line in msg), |
584 |
level=logging.WARNING, noiselevel=-1) |
585 |
- # TODO: Add packages + deps to graph, and calculate new clean list. |
586 |
- return 1 |
587 |
|
588 |
+ # Add lib providers to the graph as children of lib consumers, |
589 |
+ # and also add any dependencies pulled in by the provider. |
590 |
+ writemsg_level(">>> Adding lib providers to graph...\n") |
591 |
+ |
592 |
+ for pkg, consumers in consumer_map.iteritems(): |
593 |
+ for consumer_dblink in set(chain(*consumers.values())): |
594 |
+ consumer_pkg = vardb.get(("installed", myroot, |
595 |
+ consumer_dblink.mycpv, "nomerge")) |
596 |
+ resolver._add_pkg(pkg, consumer_pkg, |
597 |
+ priority=UnmergeDepPriority(runtime=True)) |
598 |
+ |
599 |
+ writemsg_level("\nCalculating dependencies ") |
600 |
+ success = resolver._complete_graph() |
601 |
+ writemsg_level("\b\b... done!\n") |
602 |
+ resolver.display_problems() |
603 |
+ if not success: |
604 |
+ return 1 |
605 |
+ if unresolved_deps(): |
606 |
+ return 1 |
607 |
+ |
608 |
+ graph = resolver.digraph.copy() |
609 |
+ required_pkgs_total = 0 |
610 |
+ for node in graph: |
611 |
+ if isinstance(node, Package): |
612 |
+ required_pkgs_total += 1 |
613 |
+ cleanlist = create_cleanlist() |
614 |
+ if not cleanlist: |
615 |
+ return 0 |
616 |
+ clean_set = set(cleanlist) |
617 |
+ |
618 |
# Use a topological sort to create an unmerge order such that |
619 |
# each package is unmerged before it's dependencies. This is |
620 |
# necessary to avoid breaking things that may need to run |
621 |
@@ -12093,13 +12220,11 @@ |
622 |
if isinstance(x, list) and \ |
623 |
tuple(x) not in unsatisfied_parents] |
624 |
|
625 |
- # It shouldn't happen, but if the size of mergelist |
626 |
- # does not decrease for some reason then the loop |
627 |
- # will be infinite. Therefore, if that case ever |
628 |
- # occurs for some reason, raise the exception to |
629 |
- # break out of the loop. |
630 |
+ # If the mergelist doesn't shrink then this loop is infinite. |
631 |
if len(pruned_mergelist) == len(mergelist): |
632 |
- raise AssertionError("tight loop") |
633 |
+ # This happens if a package can't be dropped because |
634 |
+ # it's already installed, but it has unsatisfied PDEPEND. |
635 |
+ raise |
636 |
mergelist[:] = pruned_mergelist |
637 |
dropped_tasks.update(unsatisfied_parents) |
638 |
del e, graph, traversed_nodes, \ |
639 |
|
640 |
Modified: main/branches/prefix/pym/_emerge/help.py |
641 |
=================================================================== |
642 |
--- main/branches/prefix/pym/_emerge/help.py 2008-07-23 06:52:10 UTC (rev 11171) |
643 |
+++ main/branches/prefix/pym/_emerge/help.py 2008-07-23 06:54:24 UTC (rev 11172) |
644 |
@@ -66,35 +66,42 @@ |
645 |
print " file setup or other similar setups that the user may wish to run." |
646 |
print |
647 |
print " "+green("--depclean") |
648 |
- paragraph = "Cleans the system by removing packages that are not associated " + \ |
649 |
- "with explicitly merged packages. Depclean works by creating the " + \ |
650 |
- "full dependency tree from the system and world sets, " + \ |
651 |
- "then comparing it to installed packages. Packages installed, but " + \ |
652 |
- "not part of the dependency tree, will be uninstalled by depclean. " + \ |
653 |
- "Inexperienced users are advised to use --pretend " + \ |
654 |
- "with this option in order to see a preview of which packages " + \ |
655 |
- "will be uninstalled." |
656 |
+ |
657 |
+ paragraph = "Cleans the system by removing packages that are " + \ |
658 |
+ "not associated with explicitly merged packages. Depclean works " + \ |
659 |
+ "by creating the full dependency tree from the @system and " + \ |
660 |
+ "@world sets, then comparing it to installed packages. Packages " + \ |
661 |
+ "installed, but not part of the dependency tree, will be " + \ |
662 |
+ "uninstalled by depclean. See --with-bdeps for behavior with " + \ |
663 |
+ "respect to build time dependencies that are not strictly " + \ |
664 |
+ "required. Packages that are part of the world set will " + \ |
665 |
+ "always be kept. They can be manually added to this set with " + \ |
666 |
+ "emerge --noreplace <atom>. As a safety measure, depclean " + \ |
667 |
+ "will not remove any packages unless *all* required dependencies " + \ |
668 |
+ "have been resolved. As a consequence, it is often necessary to " + \ |
669 |
+ "run emerge --update --newuse --deep --oneshot @system @world " + \ |
670 |
+ "prior to depclean." |
671 |
+ |
672 |
for line in wrap(paragraph, desc_width): |
673 |
print desc_indent + line |
674 |
print |
675 |
|
676 |
- paragraph = "WARNING: Removing some " + \ |
677 |
- "packages may cause packages which link to the removed package " + \ |
678 |
- "to stop working and complain about missing libraries. " + \ |
679 |
- "Rebuild the complaining package to fix this issue. Also see " + \ |
680 |
- "--with-bdeps for behavior with respect to build time dependencies that " + \ |
681 |
- "are not strictly required. Note that packages listed in " + \ |
682 |
- "package.provided (see portage(5)) may be removed by " + \ |
683 |
- "depclean, even if they are part of the world set." |
684 |
+ paragraph = "WARNING: Inexperienced users are advised to use " + \ |
685 |
+ "--pretend with this option in order to see a preview of which " + \ |
686 |
+ "packages will be uninstalled. Always study the list of packages " + \ |
687 |
+ "to be cleaned for any obvious mistakes. Note that packages " + \ |
688 |
+ "listed in package.provided (see portage(5)) may be removed by " + \ |
689 |
+ "depclean, even if they are part of the world set." |
690 |
+ |
691 |
for line in wrap(paragraph, desc_width): |
692 |
print desc_indent + line |
693 |
print |
694 |
|
695 |
- paragraph = "Depclean serves as a dependency aware " + \ |
696 |
- "version of --unmerge. When given one or more atoms, it will " + \ |
697 |
- "unmerge matched packages that have no reverse dependencies. Use " + \ |
698 |
- "--depclean together with --verbose to show reverse " + \ |
699 |
- "dependencies." |
700 |
+ paragraph = "Depclean serves as a dependency aware version of " + \ |
701 |
+ "--unmerge. When given one or more atoms, it will unmerge " + \ |
702 |
+ "matched packages that have no reverse dependencies. Use " + \ |
703 |
+ "--depclean together with --verbose to show reverse dependencies." |
704 |
+ |
705 |
for line in wrap(paragraph, desc_width): |
706 |
print desc_indent + line |
707 |
print |
708 |
@@ -116,12 +123,13 @@ |
709 |
print |
710 |
print " "+green("--prune")+" ("+green("-P")+" short option)" |
711 |
print " "+turquoise("WARNING: This action can remove important packages!") |
712 |
- print " Removes all but the highest installed version of a package" |
713 |
- print " from your system. This action doesn't verify the possible binary" |
714 |
- print " compatibility between versions and can thus remove essential" |
715 |
- print " dependencies from your system. Use --prune together with" |
716 |
- print " --verbose to show reverse dependencies or with --nodeps to" |
717 |
- print " ignore all dependencies." |
718 |
+ paragraph = "Removes all but the highest installed version of a " + \ |
719 |
+ "package from your system. Use --prune together with " + \ |
720 |
+ "--verbose to show reverse dependencies or with --nodeps " + \ |
721 |
+ "to ignore all dependencies. " |
722 |
+ |
723 |
+ for line in wrap(paragraph, desc_width): |
724 |
+ print desc_indent + line |
725 |
print |
726 |
print " "+green("--regen") |
727 |
print " Causes portage to check and update the dependency cache of all" |
728 |
|
729 |
Modified: main/branches/prefix/pym/portage/__init__.py |
730 |
=================================================================== |
731 |
--- main/branches/prefix/pym/portage/__init__.py 2008-07-23 06:52:10 UTC (rev 11171) |
732 |
+++ main/branches/prefix/pym/portage/__init__.py 2008-07-23 06:54:24 UTC (rev 11172) |
733 |
@@ -4312,7 +4312,7 @@ |
734 |
noiselevel=-1) |
735 |
return retval |
736 |
|
737 |
-def _check_build_log(mysettings): |
738 |
+def _check_build_log(mysettings, out=None): |
739 |
""" |
740 |
Search the content of $PORTAGE_LOG_FILE if it exists |
741 |
and generate the following QA Notices when appropriate: |
742 |
@@ -4357,7 +4357,7 @@ |
743 |
from portage.elog.messages import eqawarn |
744 |
def _eqawarn(lines): |
745 |
for line in lines: |
746 |
- eqawarn(line, phase="install", key=mysettings.mycpv) |
747 |
+ eqawarn(line, phase="install", key=mysettings.mycpv, out=out) |
748 |
from textwrap import wrap |
749 |
wrap_width = 70 |
750 |
|
751 |
|
752 |
Modified: main/branches/prefix/pym/portage/dbapi/vartree.py |
753 |
=================================================================== |
754 |
--- main/branches/prefix/pym/portage/dbapi/vartree.py 2008-07-23 06:52:10 UTC (rev 11171) |
755 |
+++ main/branches/prefix/pym/portage/dbapi/vartree.py 2008-07-23 06:54:24 UTC (rev 11172) |
756 |
@@ -1680,6 +1680,7 @@ |
757 |
The caller must ensure that lockdb() and unlockdb() are called |
758 |
before and after this method. |
759 |
""" |
760 |
+ showMessage = self._display_merge |
761 |
if self.vartree.dbapi._categories is not None: |
762 |
self.vartree.dbapi._categories = None |
763 |
# When others_in_slot is supplied, the security check has already been |
764 |
@@ -1835,7 +1836,7 @@ |
765 |
else: |
766 |
obj_type = "obj" |
767 |
os.unlink(obj) |
768 |
- writemsg_stdout("<<< !needed %s %s\n" % (obj_type, obj)) |
769 |
+ showMessage("<<< !needed %s %s\n" % (obj_type, obj)) |
770 |
except OSError, e: |
771 |
if e.errno == errno.ENOENT: |
772 |
pass |
773 |
@@ -2264,6 +2265,7 @@ |
774 |
return False |
775 |
|
776 |
def _preserve_libs(self, srcroot, destroot, mycontents, counter, inforoot): |
777 |
+ showMessage = self._display_merge |
778 |
# read global reverse NEEDED map |
779 |
linkmap = self.vartree.dbapi.linkmap |
780 |
if ostype == "Darwin": |
781 |
@@ -2360,9 +2362,11 @@ |
782 |
# skip existing files so the 'new' libs aren't overwritten |
783 |
if os.path.exists(os.path.join(srcroot, x.lstrip(os.sep))): |
784 |
continue |
785 |
- print "injecting %s into %s" % (x, srcroot) |
786 |
+ showMessage("injecting %s into %s\n" % (x, srcroot), |
787 |
+ noiselevel=-1) |
788 |
if not os.path.exists(os.path.join(destroot, x.lstrip(os.sep))): |
789 |
- print "%s does not exist so can't be preserved" % x |
790 |
+ showMessage("%s does not exist so can't be preserved\n" % x, |
791 |
+ noiselevel=-1) |
792 |
continue |
793 |
mydir = os.path.join(srcroot, os.path.dirname(x).lstrip(os.sep)) |
794 |
if not os.path.exists(mydir): |