Merged from trunk 11157:11169
authorFabian Groffen <grobian@gentoo.org>
Wed, 23 Jul 2008 06:54:24 +0000 (06:54 -0000)
committerFabian Groffen <grobian@gentoo.org>
Wed, 23 Jul 2008 06:54:24 +0000 (06:54 -0000)
   | 11158   | Fix --depclean/--prune to add lib providers and their        |
   | zmedico | dependencies to the graph and create a new clean list when   |
   |         | necessary. This completes the fix for bug #230053.           |

   | 11159   | Adjust --fetchonly failure messages to work better with      |
   | zmedico | background mode.                                             |

   | 11160   | Adjust status messages for --fetchonly mode.                 |
   | zmedico |                                                              |

   | 11161   | Redirect dblink._preserve_libs() messages to the log when in |
   | zmedico | background mode.                                             |

   | 11162   | Make --pretend disable background mode and imply --jobs=1.   |
   | zmedico |                                                              |

   | 11163   | Disable "Installing" status messages when in --pretend or    |
   | zmedico | --buildpkgonly mode.                                         |

   | 11164   | Redirect build log eqawarn messages to the log file when in  |
   | zmedico | background mode.                                             |

   | 11165   | Show the number of failed packages in the status display.    |
   | zmedico |                                                              |

   | 11166   | Fix resume_depgraph() so that it doesn't raise an            |
   | zmedico | AssertionError due to unsatisfied PDEPEND.                   |

   | 11167   | In dblink.unmerge(), redirect preserve-libs "!needed"        |
   | zmedico | messages to the log file when in background mode.            |

   | 11168   | Update --depclean and --prune, removing warnings about       |
   | zmedico | libraries since those are now automatically accounted for.   |

   | 11169   | Handle UnsatisfiedResumeDep in                               |
   | zmedico | Scheduler._calc_resume_list().                               |

svn path=/main/branches/prefix/; revision=11172

NEWS
man/emerge.1
pym/_emerge/__init__.py
pym/_emerge/help.py
pym/portage/__init__.py
pym/portage/dbapi/vartree.py

diff --git a/NEWS b/NEWS
index 1f9f308e7bafde2244a546b333a1fa9194f7ce87..2fb033c0b6f75d9c52ef0b415ce58f5945542ebd 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,8 @@ News (mainly features/major bug fixes)
 portage-2.2
 -------------
 
+* Add link level dependency awareness to emerge --depclean and --prune actions
+  in order to protect against uninstallation of required libraries.
 * Add emerge --jobs and --load-average options which specify behavior
   for building packages in parallel or for generating metadata in parallel
   with emerge --regen.
index bc9a7efc8ac6e023c095d5698ed806ceaa5e71a3..59d335f622ba00d1c74a09db30e8b141e761c678 100644 (file)
@@ -103,19 +103,22 @@ setups that the user may wish to run.
 .BR \-\-depclean
 Cleans the system by removing packages that are not associated
 with explicitly merged packages. Depclean works by creating the
-full dependency tree from the system and world sets,
+full dependency tree from the @system and @world sets,
 then comparing it to installed packages. Packages installed, but
 not part of the dependency tree, will be uninstalled by depclean.
-Inexperienced users are advised to use \fB\-\-pretend\fR
-with this option in order to see a preview of which packages
-will be uninstalled.
+See \fB\-\-with\-bdeps\fR for behavior with respect to build time dependencies
+that are not strictly required. Packages that are part of the world set will
+always be kept. They can be manually added to this set with \fIemerge
+\-\-noreplace <atom>\fR. As a safety measure, depclean will not remove any
+packages unless *all* required dependencies have been resolved. As a
+consequence, it is often necessary to run \fIemerge \-\-update \-\-newuse
+\-\-deep \-\-oneshot @system @world\fR prior to depclean. 
 
-\fBWARNING: Removing some
-packages may cause packages which link to the removed package 
-to stop working and complain about missing libraries.\fR
-Rebuild the complaining package to fix this issue.  Also see
-\fB\-\-with\-bdeps\fR for behavior with respect to build time dependencies that
-are not strictly required. Note that packages listed in
+\fBWARNING:\fR
+Inexperienced users are advised to use \fB\-\-pretend\fR with this
+option in order to see a preview of which packages
+will be uninstalled. Always study the list of packages
+to be cleaned for any obvious mistakes. Note that packages listed in
 package.provided (see \fBportage\fR(5)) may be removed by
 depclean, even if they are part of the world set.
 
@@ -150,11 +153,9 @@ unless the user has enabled FEATURES="metadata-transfer" in \fBmake.conf\fR(5).
 .TP
 .BR "\-\-prune " (\fB\-P\fR)
 \fBWARNING: This action can remove important packages!\fR  Removes all but the
-highest installed version of a package from your system. This action doesn't
-verify the possible binary compatibility between versions and can thus remove
-essential dependencies from your system. Use \fB\-\-prune\fR together with
-\fB\-\-verbose\fR to show reverse dependencies or with \fB\-\-nodeps\fR to
-ignore all dependencies.
+highest installed version of a package from your system. Use \fB\-\-prune\fR
+together with \fB\-\-verbose\fR to show reverse dependencies or with
+\fB\-\-nodeps\fR to ignore all dependencies.
 .TP
 .BR \-\-regen
 Causes portage to check and update the dependency cache of all ebuilds in the 
index 932824055189814cbd7b9cf46aac942c037a3d92..fc9b9588d66e32c2328dbdc06d9382bde5a0ab16 100644 (file)
@@ -29,6 +29,7 @@ import logging
 import select
 import shlex
 import shutil
+import textwrap
 import urlparse
 import weakref
 import gc
@@ -2429,8 +2430,9 @@ class EbuildBuild(CompositeTask):
 
                if opts.fetchonly:
                        if self._final_exit(fetcher) != os.EX_OK:
-                               eerror("!!! Fetch for %s failed, continuing..." % pkg.cpv,
-                                       phase="unpack", key=pkg.cpv)
+                               if not self.background:
+                                       eerror("Fetch for %s failed, continuing..." % pkg.cpv,
+                                               phase="unpack", key=pkg.cpv)
                        self.wait()
                        return
 
@@ -2761,7 +2763,17 @@ class EbuildPhase(CompositeTask):
        def _ebuild_exit(self, ebuild_process):
 
                if self.phase == "install":
-                       portage._check_build_log(self.settings)
+                       out = None
+                       log_path = self.settings.get("PORTAGE_LOG_FILE")
+                       log_file = None
+                       if self.background and log_path is not None:
+                               log_file = open(log_path, 'a')
+                               out = log_file
+                       try:
+                               portage._check_build_log(self.settings, out=out)
+                       finally:
+                               if log_file is not None:
+                                       log_file.close()
 
                if self._default_exit(ebuild_process) != os.EX_OK:
                        self.wait()
@@ -3391,6 +3403,9 @@ class MergeListItem(CompositeTask):
                if pkg.type_name == "binary":
                        action_desc = "Extracting"
 
+               if build_opts.fetchonly:
+                       action_desc = "Fetching"
+
                if not build_opts.pretend:
 
                        self.statusMessage("%s (%s of %s) %s %s %s" % \
@@ -3499,9 +3514,12 @@ class PackageMerge(AsynchronousTask):
                        action_desc = "Installing"
                        preposition = "to"
 
-               self.merge.statusMessage("%s %s %s %s" % \
-                       (action_desc, colorize("GOOD", pkg.cpv),
-                       preposition, pkg.root))
+               if not self.merge.build_opts.fetchonly and \
+                       not self.merge.build_opts.pretend and \
+                       not self.merge.build_opts.buildpkgonly:
+                       self.merge.statusMessage("%s %s %s %s" % \
+                               (action_desc, colorize("GOOD", pkg.cpv),
+                               preposition, pkg.root))
 
                self.returncode = self.merge.merge()
                self.wait()
@@ -8448,8 +8466,8 @@ class TaskScheduler(object):
 
 class JobStatusDisplay(object):
 
-       _bound_properties = ("curval", "running")
-       _jobs_column_width = 42
+       _bound_properties = ("curval", "failed", "running")
+       _jobs_column_width = 48
 
        # Don't update the display unless at least this much
        # time has passed, in units of seconds.
@@ -8577,6 +8595,7 @@ class JobStatusDisplay(object):
 
        def _property_change(self, name, old_value, new_value):
                self._changed = True
+               self.display()
 
        def _load_avg_str(self, digits=2):
                try:
@@ -8613,7 +8632,7 @@ class JobStatusDisplay(object):
                curval_str = str(self.curval)
                maxval_str = str(self.maxval)
                running_str = str(self.running)
-               merges_str = str(self.merges)
+               failed_str = str(self.failed)
                load_avg_str = self._load_avg_str()
 
                color_output = StringIO.StringIO()
@@ -8642,21 +8661,18 @@ class JobStatusDisplay(object):
                        f.pop_style()
                        f.add_literal_data(" running")
 
-               #if self.merges:
-               if False:
+               if self.failed:
                        f.add_literal_data(", ")
                        f.push_style(number_style)
-                       f.add_literal_data(merges_str)
+                       f.add_literal_data(failed_str)
                        f.pop_style()
-                       f.add_literal_data(" merge")
-                       if self.merges != 1:
-                               f.add_literal_data("s")
+                       f.add_literal_data(" failed")
 
                padding = self._jobs_column_width - len(plain_output.getvalue())
                if padding > 0:
                        f.add_literal_data(padding * " ")
 
-               f.add_literal_data("Load average: ")
+               f.add_literal_data("Load avg: ")
                f.add_literal_data(load_avg_str)
 
                self._update(color_output.getvalue())
@@ -8843,7 +8859,8 @@ class Scheduler(PollScheduler):
                @rtype: bool
                @returns: True if background mode is enabled, False otherwise.
                """
-               background = self._max_jobs > 1 or "--quiet" in self.myopts
+               background = (self._max_jobs > 1 or "--quiet" in self.myopts) and \
+                       "--pretend" not in self.myopts
 
                self._status_display.quiet = \
                        not background or \
@@ -9116,15 +9133,26 @@ class Scheduler(PollScheduler):
                        "--fetch-all-uri" in self.myopts):
                        return
 
-               sys.stderr.write("\n\n!!! Some fetch errors were " + \
-                       "encountered.  Please see above for details.\n\n")
+               if self._background:
+                       msg = "Some fetch errors were " + \
+                               "encountered. Please see %s for details." % \
+                               self._fetch_log
+               else:
+                       msg = "Some fetch errors were " + \
+                               "encountered. Please see above for details."
 
-               for cpv in failed_fetches:
-                       sys.stderr.write("   ")
-                       sys.stderr.write(cpv)
-                       sys.stderr.write("\n")
+               prefix = bad(" * ")
+               msg = "".join("%s%s\n" % (prefix, line) \
+                       for line in textwrap.wrap(msg, 70))
+               writemsg_level(msg, level=logging.ERROR, noiselevel=-1)
 
-               sys.stderr.write("\n")
+               msg = []
+               msg.append("")
+               for cpv in failed_fetches:
+                       msg.append("  %s" % cpv)
+               msg.append("")
+               writemsg_level("".join("%s%s\n" % (prefix, line) \
+                       for line in msg), level=logging.ERROR, noiselevel=-1)
 
        def _is_restart_scheduled(self):
                """
@@ -9254,6 +9282,9 @@ class Scheduler(PollScheduler):
                                break
 
                        dropped_tasks = self._calc_resume_list()
+                       if dropped_tasks is None:
+                               break
+
                        clear_caches(self.trees)
                        if not self._mergelist:
                                break
@@ -9349,6 +9380,7 @@ class Scheduler(PollScheduler):
                pkg = merge.merge.pkg
                if merge.returncode != os.EX_OK:
                        self._failed_pkgs.append((pkg, merge.returncode))
+                       self._status_display.failed = len(self._failed_pkgs)
                        return
 
                self._task_complete(pkg)
@@ -9383,10 +9415,10 @@ class Scheduler(PollScheduler):
                        self._status_display.merges = len(self._task_queues.merge)
                else:
                        self._failed_pkgs.append((build.pkg, build.returncode))
+                       self._status_display.failed = len(self._failed_pkgs)
                        self._deallocate_config(build.settings)
                self._jobs -= 1
                self._status_display.running = self._jobs
-               self._status_display.display()
                self._schedule()
 
        def _extract_exit(self, build):
@@ -9498,7 +9530,8 @@ class Scheduler(PollScheduler):
 
                # Only allow 1 job max if a restart is scheduled
                # due to portage update.
-               if self._is_restart_scheduled():
+               if self._is_restart_scheduled() or \
+                       "--pretend" in self.myopts:
                        self._set_max_jobs(1)
 
                merge_queue = self._task_queues.merge
@@ -9620,6 +9653,9 @@ class Scheduler(PollScheduler):
                """
                Use the current resume list to calculate a new one,
                dropping any packages with unsatisfied deps.
+               @rtype: set
+               @returns: a possibly empty set of dropped tasks, or
+                       None if an error occurs.
                """
                print colorize("GOOD", "*** Resuming merge...")
 
@@ -9641,13 +9677,47 @@ class Scheduler(PollScheduler):
                        print "Calculating dependencies  ",
 
                myparams = create_depgraph_params(self.myopts, None)
-               success, mydepgraph, dropped_tasks = resume_depgraph(
-                       self.settings, self.trees, self._mtimedb, self.myopts,
-                       myparams, self._spinner, skip_unsatisfied=True)
+               success = False
+               e = None
+               try:
+                       success, mydepgraph, dropped_tasks = resume_depgraph(
+                               self.settings, self.trees, self._mtimedb, self.myopts,
+                               myparams, self._spinner, skip_unsatisfied=True)
+               except depgraph.UnsatisfiedResumeDep, e:
+                       mydepgraph = e.depgraph
+                       dropped_tasks = set()
 
                if show_spinner:
                        print "\b\b... done!"
 
+               if e is not None:
+                       mydepgraph.display_problems()
+                       out = portage.output.EOutput()
+                       out.eerror("One or packages are either masked or " + \
+                               "have missing dependencies:")
+                       out.eerror("")
+                       indent = "  "
+                       for dep in e.value:
+                               if dep.atom is None:
+                                       out.eerror(indent + "Masked package:")
+                                       out.eerror(2 * indent + str(dep.parent))
+                                       out.eerror("")
+                               else:
+                                       out.eerror(indent + str(dep.atom) + " pulled in by:")
+                                       out.eerror(2 * indent + str(dep.parent))
+                                       out.eerror("")
+                       msg = "The resume list contains packages " + \
+                               "that are either masked or have " + \
+                               "unsatisfied dependencies. " + \
+                               "Please restart/continue " + \
+                               "the operation manually, or use --skipfirst " + \
+                               "to skip the first package in the list and " + \
+                               "any other packages that may be " + \
+                               "masked or have missing dependencies."
+                       for line in textwrap.wrap(msg, 72):
+                               out.eerror(line)
+                       return None
+
                if self._show_list():
                        mylist = mydepgraph.altlist()
                        if "--tree" in self.myopts:
@@ -9656,7 +9726,7 @@ class Scheduler(PollScheduler):
 
                mydepgraph.display_problems()
                if not success:
-                       return (None, None)
+                       return None
 
                mylist = mydepgraph.altlist()
                mydepgraph.break_refs(mylist)
@@ -11515,8 +11585,10 @@ def action_depclean(settings, trees, ldpath_mtimes,
        msg.append("\n")
        msg.append("As a safety measure, depclean will not remove any packages\n")
        msg.append("unless *all* required dependencies have been resolved.  As a\n")
-       msg.append("consequence, it is often necessary to run\n")
-       msg.append(good("`emerge --update --newuse --deep world`") + " prior to depclean.\n")
+       msg.append("consequence, it is often necessary to run %s\n" % \
+               good("`emerge --update"))
+       msg.append(good("--newuse --deep --oneshot @system @world`") + \
+               " prior to depclean.\n")
 
        if action == "depclean" and "--quiet" not in myopts and not myfiles:
                portage.writemsg_stdout("\n")
@@ -11691,29 +11763,45 @@ def action_depclean(settings, trees, ldpath_mtimes,
        if not success:
                return 1
 
-       unresolveable = set()
-       for dep in resolver._initially_unsatisfied_deps:
-               if isinstance(dep.parent, Package):
-                       unresolveable.add((dep.atom, dep.parent.cpv))
+       def unresolved_deps():
 
-       if unresolveable and not allow_missing_deps:
-               print "Dependencies could not be completely resolved due to"
-               print "the following required packages not being installed:"
-               print
-               for atom, parent in unresolveable:
-                       print atom, "required by", str(parent)
-       if unresolveable and not allow_missing_deps:
-               print
-               print "Have you forgotten to run " + good("`emerge --update --newuse --deep world`") + " prior to"
-               print "%s?  It may be necessary to manually uninstall packages that no longer" % action
-               print "exist in the portage tree since it may not be possible to satisfy their"
-               print "dependencies.  Also, be aware of the --with-bdeps option that is documented"
-               print "in " + good("`man emerge`") + "."
-               print
-               if action == "prune":
-                       print "If you would like to ignore dependencies then use %s." % \
-                               good("--nodeps")
-               return
+               unresolvable = set()
+               for dep in resolver._initially_unsatisfied_deps:
+                       if isinstance(dep.parent, Package):
+                               unresolvable.add((dep.atom, dep.parent.cpv))
+               if not unresolvable:
+                       return False
+
+               if unresolvable and not allow_missing_deps:
+                       prefix = bad(" * ")
+                       msg = []
+                       msg.append("Dependencies could not be completely resolved due to")
+                       msg.append("the following required packages not being installed:")
+                       msg.append("")
+                       for atom, parent in unresolvable:
+                               msg.append("  %s pulled in by:" % (atom,))
+                               msg.append("    %s" % (parent,))
+                               msg.append("")
+                       msg.append("Have you forgotten to run " + \
+                               good("`emerge --update --newuse --deep world`") + " prior to")
+                       msg.append(("%s?  It may be necessary to manually " + \
+                               "uninstall packages that no longer") % action)
+                       msg.append("exist in the portage tree since " + \
+                               "it may not be possible to satisfy their")
+                       msg.append("dependencies.  Also, be aware of " + \
+                               "the --with-bdeps option that is documented")
+                       msg.append("in " + good("`man emerge`") + ".")
+                       if action == "prune":
+                               msg.append("")
+                               msg.append("If you would like to ignore " + \
+                                       "dependencies then use %s." % good("--nodeps"))
+                       writemsg_level("".join("%s%s\n" % (prefix, line) for line in msg),
+                               level=logging.ERROR, noiselevel=-1)
+                       return True
+               return False
+
+       if unresolved_deps():
+               return 1
 
        graph = resolver.digraph.copy()
        required_pkgs_total = 0
@@ -11739,49 +11827,60 @@ def action_depclean(settings, trees, ldpath_mtimes,
                msg.append("\n")
                portage.writemsg_stdout("".join(msg), noiselevel=-1)
 
-       cleanlist = []
-       if action == "depclean":
-               if args_set:
-                       for pkg in vardb:
-                               arg_atom = None
-                               try:
-                                       arg_atom = args_set.findAtomForPackage(pkg)
-                               except portage.exception.InvalidDependString:
-                                       # this error has already been displayed by now
-                                       continue
-                               if arg_atom:
+       def create_cleanlist():
+               pkgs_to_remove = []
+
+               if action == "depclean":
+                       if args_set:
+
+                               for pkg in vardb:
+                                       arg_atom = None
+                                       try:
+                                               arg_atom = args_set.findAtomForPackage(pkg)
+                                       except portage.exception.InvalidDependString:
+                                               # this error has already been displayed by now
+                                               continue
+
+                                       if arg_atom:
+                                               if pkg not in graph:
+                                                       pkgs_to_remove.append(pkg)
+                                               elif "--verbose" in myopts:
+                                                       show_parents(pkg)
+
+                       else:
+                               for pkg in vardb:
                                        if pkg not in graph:
-                                               cleanlist.append(pkg)
+                                               pkgs_to_remove.append(pkg)
                                        elif "--verbose" in myopts:
                                                show_parents(pkg)
-               else:
-                       for pkg in vardb:
-                               if pkg not in graph:
-                                       cleanlist.append(pkg)
-                               elif "--verbose" in myopts:
-                                       show_parents(pkg)
-       elif action == "prune":
-               # Prune really uses all installed instead of world.  It's not a real
-               # reverse dependency so don't display it as such.
-               graph.remove(set_args["world"])
-               for atom in args_set:
-                       for pkg in vardb.match_pkgs(atom):
-                               if pkg not in graph:
-                                       cleanlist.append(pkg)
-                               elif "--verbose" in myopts:
-                                       show_parents(pkg)
-
-       if not cleanlist:
-               portage.writemsg_stdout(
-                       ">>> No packages selected for removal by %s\n" % action)
-               if "--verbose" not in myopts:
-                       portage.writemsg_stdout(
-                               ">>> To see reverse dependencies, use %s\n" % \
-                                       good("--verbose"))
-               if action == "prune":
-                       portage.writemsg_stdout(
-                               ">>> To ignore dependencies, use %s\n" % \
-                                       good("--nodeps"))
+
+               elif action == "prune":
+                       # Prune really uses all installed instead of world. It's not
+                       # a real reverse dependency so don't display it as such.
+                       graph.remove(set_args["world"])
+
+                       for atom in args_set:
+                               for pkg in vardb.match_pkgs(atom):
+                                       if pkg not in graph:
+                                               pkgs_to_remove.append(pkg)
+                                       elif "--verbose" in myopts:
+                                               show_parents(pkg)
+
+               if not pkgs_to_remove:
+                       writemsg_level(
+                               ">>> No packages selected for removal by %s\n" % action)
+                       if "--verbose" not in myopts:
+                               writemsg_level(
+                                       ">>> To see reverse dependencies, use %s\n" % \
+                                               good("--verbose"))
+                       if action == "prune":
+                               writemsg_level(
+                                       ">>> To ignore dependencies, use %s\n" % \
+                                               good("--nodeps"))
+
+               return pkgs_to_remove
+
+       cleanlist = create_cleanlist()
 
        if len(cleanlist):
                clean_set = set(cleanlist)
@@ -11933,8 +12032,36 @@ def action_depclean(settings, trees, ldpath_mtimes,
                        msg.append("")
                        writemsg_level("".join(prefix + "%s\n" % line for line in msg),
                                level=logging.WARNING, noiselevel=-1)
-                       # TODO: Add packages + deps to graph, and calculate new clean list.
-                       return 1
+
+                       # Add lib providers to the graph as children of lib consumers,
+                       # and also add any dependencies pulled in by the provider.
+                       writemsg_level(">>> Adding lib providers to graph...\n")
+
+                       for pkg, consumers in consumer_map.iteritems():
+                               for consumer_dblink in set(chain(*consumers.values())):
+                                       consumer_pkg = vardb.get(("installed", myroot,
+                                               consumer_dblink.mycpv, "nomerge"))
+                                       resolver._add_pkg(pkg, consumer_pkg,
+                                               priority=UnmergeDepPriority(runtime=True))
+
+                       writemsg_level("\nCalculating dependencies  ")
+                       success = resolver._complete_graph()
+                       writemsg_level("\b\b... done!\n")
+                       resolver.display_problems()
+                       if not success:
+                               return 1
+                       if unresolved_deps():
+                               return 1
+
+                       graph = resolver.digraph.copy()
+                       required_pkgs_total = 0
+                       for node in graph:
+                               if isinstance(node, Package):
+                                       required_pkgs_total += 1
+                       cleanlist = create_cleanlist()
+                       if not cleanlist:
+                               return 0
+                       clean_set = set(cleanlist)
 
                # Use a topological sort to create an unmerge order such that
                # each package is unmerged before it's dependencies. This is
@@ -12093,13 +12220,11 @@ def resume_depgraph(settings, trees, mtimedb, myopts, myparams, spinner,
                                if isinstance(x, list) and \
                                tuple(x) not in unsatisfied_parents]
 
-                       # It shouldn't happen, but if the size of mergelist
-                       # does not decrease for some reason then the loop
-                       # will be infinite. Therefore, if that case ever
-                       # occurs for some reason, raise the exception to
-                       # break out of the loop.
+                       # If the mergelist doesn't shrink then this loop is infinite.
                        if len(pruned_mergelist) == len(mergelist):
-                               raise AssertionError("tight loop")
+                               # This happens if a package can't be dropped because
+                               # it's already installed, but it has unsatisfied PDEPEND.
+                               raise
                        mergelist[:] = pruned_mergelist
                        dropped_tasks.update(unsatisfied_parents)
                        del e, graph, traversed_nodes, \
index bf6a1383462a84783410434d8c7ab507b3c19f67..b2c032b21318c74124950b340d36af1f7c8d1d1b 100644 (file)
@@ -66,35 +66,42 @@ def help(myaction,myopts,havecolor=1):
                print "              file setup or other similar setups that the user may wish to run."
                print
                print "       "+green("--depclean")
-               paragraph = "Cleans the system by removing packages that are not associated " + \
-                       "with explicitly merged packages. Depclean works by creating the " + \
-                       "full dependency tree from the system and world sets, " + \
-                       "then comparing it to installed packages. Packages installed, but " + \
-                       "not part of the dependency tree, will be uninstalled by depclean. " + \
-                       "Inexperienced users are advised to use --pretend " + \
-                       "with this option in order to see a preview of which packages " + \
-                       "will be uninstalled."
+
+               paragraph = "Cleans the system by removing packages that are " + \
+               "not associated with explicitly merged packages. Depclean works " + \
+               "by creating the full dependency tree from the @system and " + \
+               "@world sets, then comparing it to installed packages. Packages " + \
+               "installed, but not part of the dependency tree, will be " + \
+               "uninstalled by depclean. See --with-bdeps for behavior with " + \
+               "respect to build time dependencies that are not strictly " + \
+               "required. Packages that are part of the world set will " + \
+               "always be kept. They can be manually added to this set with " + \
+               "emerge --noreplace <atom>. As a safety measure, depclean " + \
+               "will not remove any packages unless *all* required dependencies " + \
+               "have been resolved. As a consequence, it is often necessary to " + \
+               "run emerge --update --newuse --deep --oneshot @system @world " + \
+               "prior to depclean."
+
                for line in wrap(paragraph, desc_width):
                        print desc_indent + line
                print
 
-               paragraph =  "WARNING: Removing some " + \
-                       "packages may cause packages which link to the removed package  " + \
-                       "to stop working and complain about missing libraries. " + \
-                       "Rebuild the complaining package to fix this issue.  Also see " + \
-                       "--with-bdeps for behavior with respect to build time dependencies that " + \
-                       "are not strictly required. Note that packages listed in " + \
-                       "package.provided (see portage(5)) may be removed by " + \
-                       "depclean, even if they are part of the world set."
+               paragraph =  "WARNING: Inexperienced users are advised to use " + \
+               "--pretend with this option in order to see a preview of which " + \
+               "packages will be uninstalled. Always study the list of packages " + \
+               "to be cleaned for any obvious mistakes. Note that packages " + \
+               "listed in package.provided (see portage(5)) may be removed by " + \
+               "depclean, even if they are part of the world set."
+
                for line in wrap(paragraph, desc_width):
                        print desc_indent + line
                print
 
-               paragraph = "Depclean serves as a dependency aware " + \
-                       "version of --unmerge. When given one or more atoms, it will " + \
-                       "unmerge matched packages that have no reverse dependencies. Use " + \
-                       "--depclean together with --verbose to show reverse " + \
-                       "dependencies."
+               paragraph = "Depclean serves as a dependency aware version of " + \
+                       "--unmerge. When given one or more atoms, it will unmerge " + \
+                       "matched packages that have no reverse dependencies. Use " + \
+                       "--depclean together with --verbose to show reverse dependencies."
+
                for line in wrap(paragraph, desc_width):
                        print desc_indent + line
                print
@@ -116,12 +123,13 @@ def help(myaction,myopts,havecolor=1):
                print
                print "       "+green("--prune")+" ("+green("-P")+" short option)"
                print "              "+turquoise("WARNING: This action can remove important packages!")
-               print "              Removes all but the highest installed version of a package"
-               print "              from your system. This action doesn't verify the possible binary"
-               print "              compatibility between versions and can thus remove essential"
-               print "              dependencies from your system. Use --prune together with"
-               print "              --verbose to show reverse dependencies or with --nodeps to"
-               print "              ignore all dependencies."
+               paragraph = "Removes all but the highest installed version of a " + \
+                       "package from your system. Use --prune together with " + \
+                       "--verbose to show reverse dependencies or with --nodeps " + \
+                       "to ignore all dependencies. "
+
+               for line in wrap(paragraph, desc_width):
+                       print desc_indent + line
                print
                print "       "+green("--regen")
                print "              Causes portage to check and update the dependency cache of all"
index b40041ffa621ff24611ac3073dd523a66d58c266..e9651184fece2bc00f7ec6a9b3a8c144def1d0bf 100644 (file)
@@ -4312,7 +4312,7 @@ def _post_src_install_checks(mysettings):
                        noiselevel=-1)
        return retval
 
-def _check_build_log(mysettings):
+def _check_build_log(mysettings, out=None):
        """
        Search the content of $PORTAGE_LOG_FILE if it exists
        and generate the following QA Notices when appropriate:
@@ -4357,7 +4357,7 @@ def _check_build_log(mysettings):
        from portage.elog.messages import eqawarn
        def _eqawarn(lines):
                for line in lines:
-                       eqawarn(line, phase="install", key=mysettings.mycpv)
+                       eqawarn(line, phase="install", key=mysettings.mycpv, out=out)
        from textwrap import wrap
        wrap_width = 70
 
index bfd972684c85771854cbf3ad89adb826a415a555..7321a3a8a17d7c5832eff600f6e4c1787bc4d0e7 100644 (file)
@@ -1680,6 +1680,7 @@ class dblink(object):
                The caller must ensure that lockdb() and unlockdb() are called
                before and after this method.
                """
+               showMessage = self._display_merge
                if self.vartree.dbapi._categories is not None:
                        self.vartree.dbapi._categories = None
                # When others_in_slot is supplied, the security check has already been
@@ -1835,7 +1836,7 @@ class dblink(object):
                                                        else:
                                                                obj_type = "obj"
                                                        os.unlink(obj)
-                                                       writemsg_stdout("<<< !needed   %s %s\n" % (obj_type, obj))
+                                                       showMessage("<<< !needed   %s %s\n" % (obj_type, obj))
                                                except OSError, e:
                                                        if e.errno == errno.ENOENT:
                                                                pass
@@ -2264,6 +2265,7 @@ class dblink(object):
                return False
 
        def _preserve_libs(self, srcroot, destroot, mycontents, counter, inforoot):
+               showMessage = self._display_merge
                # read global reverse NEEDED map
                linkmap = self.vartree.dbapi.linkmap
                if ostype == "Darwin":
@@ -2360,9 +2362,11 @@ class dblink(object):
                        # skip existing files so the 'new' libs aren't overwritten
                        if os.path.exists(os.path.join(srcroot, x.lstrip(os.sep))):
                                continue
-                       print "injecting %s into %s" % (x, srcroot)
+                       showMessage("injecting %s into %s\n" % (x, srcroot),
+                               noiselevel=-1)
                        if not os.path.exists(os.path.join(destroot, x.lstrip(os.sep))):
-                               print "%s does not exist so can't be preserved" % x
+                               showMessage("%s does not exist so can't be preserved\n" % x,
+                                       noiselevel=-1)
                                continue
                        mydir = os.path.join(srcroot, os.path.dirname(x).lstrip(os.sep))
                        if not os.path.exists(mydir):