Merged from trunk 11111:11124
authorFabian Groffen <grobian@gentoo.org>
Fri, 18 Jul 2008 09:02:11 +0000 (09:02 -0000)
committerFabian Groffen <grobian@gentoo.org>
Fri, 18 Jul 2008 09:02:11 +0000 (09:02 -0000)
   | 11112   | Since it's not very interesting, disable the "merges" part   |
   | zmedico | of status display.                                           |

   | 11113   | Also, don't count "merges" as changes in the status display. |
   | zmedico |                                                              |

   | 11114   | Initialize JobStatusDisplay.merges since it's not            |
   | zmedico | initialized with the bound properties anymore.               |

   | 11115   | Also, reset "merges" in JobStatusDisplay.reset().            |
   | zmedico |                                                              |

   | 11116   | When dblink is looping over files for merge/unmerge,         |
   | zmedico | temporarily yield to the scheduler each time a fixed number  |
   |         | of files are processed (currently 20). This gives the        |
   |         | scheduler an opportunity to service pending poll events.     |
   |         | This is implemented with a new                               |
   |         | PollScheduler._schedule_yield() method which calls poll()    |
   |         | exactly once, without blocking, and any services any         |
   |         | resulting poll events.                                       |

   | 11117   | Redirect dblink elog messages from stdout to the log file    |
   | zmedico | when in background mode. This is implemented by adding a new |
   |         | "out" parameter to the portage.elog.e* functions, which can  |
   |         | be used to send output to an arbitary file instead of        |
   |         | stdout.                                                      |

   | 11118   | If an problem is detected in dblink._security_check(), log   |
   | zmedico | it via elog so that it's properly displayed in the           |
   |         | foreground even when in background mode.                     |

   | 11119   | Use dblink._eerror() to eliminate duplicate code.            |
   | zmedico |                                                              |

   | 11120   | In dblink._dblink_display_merge(), when in background mode   |
   | zmedico | and no log is available, suppress output when level <        |
   |         | logging.WARNING. This hides ">>> Regenerating                |
   |         | /etc/ld.so.cache..." messages that appear after postrm when  |
   |         | PORT_LOGDIR is unset (the log file if it exists is removed   |
   |         | after postrm so there's no place to log the message).        |

   | 11121   | Change package status messages to give more accurate         |
   | zmedico | information about what is happening at a given time: *       |
   |         | Change "Emerging" messages to "Building" for ebuilds and     |
   |         | "Extracting" for binary packages. * Change "Merging"         |
   |         | messages to "Installing" and "Uninstalling".                 |

   | 11122   | Add background output redirection and async ebuild phase     |
   | zmedico | support for PackageUninstall (automatic uninstalls that      |
   |         | solve blockers).                                             |

   | 11123   | Suppress counter initialization warning messages in          |
   | zmedico | vardbapi.get_counter_tick_core() in cases when the vdb is    |
   |         | empty. This avoids pointless error messages when merging     |
   |         | packages into a new root, like when building stage 1.        |

   | 11124   | Fix typo.                                                    |
   | zmedico |                                                              |

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

pym/_emerge/__init__.py
pym/portage/__init__.py
pym/portage/dbapi/vartree.py
pym/portage/elog/messages.py

index e9947fc4201eacaecc563b79b71cab63d60cb1ea..a987541247675c0774d87e85d69463c9a0761aa2 100644 (file)
@@ -2864,25 +2864,41 @@ class EbuildMerge(SlotObject):
                logger.log(" ::: completed emerge (%s of %s) %s to %s" % \
                        (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg.root))
 
-class PackageUninstall(Task):
+class PackageUninstall(AsynchronousTask):
 
-       __hash__ = Task.__hash__
-       __slots__ = ("ldpath_mtimes", "opts", "pkg", "settings")
-
-       def _get_hash_key(self):
-               hash_key = getattr(self, "_hash_key", None)
-               if hash_key is None:
-                       self._hash_key = ("PackageUninstall", self.pkg._get_hash_key())
-               return self._hash_key
+       __slots__ = ("ldpath_mtimes", "opts", "pkg", "scheduler", "settings")
 
-       def execute(self):
+       def _start(self):
                try:
                        unmerge(self.pkg.root_config, self.opts, "unmerge",
                                [self.pkg.cpv], self.ldpath_mtimes, clean_world=0,
-                               raise_on_error=1)
+                               clean_delay=0, raise_on_error=1, scheduler=self.scheduler,
+                               writemsg_level=self._writemsg_level)
                except UninstallFailure, e:
-                       return e.status
-               return os.EX_OK
+                       self.returncode = e.status
+               else:
+                       self.returncode = os.EX_OK
+               self.wait()
+
+       def _writemsg_level(self, msg, level=0, noiselevel=0):
+
+               log_path = self.settings.get("PORTAGE_LOG_FILE")
+               background = self.background
+
+               if log_path is None:
+                       if not (background and level < logging.WARNING):
+                               portage.util.writemsg_level(msg,
+                                       level=level, noiselevel=noiselevel)
+               else:
+                       if not background:
+                               portage.util.writemsg_level(msg,
+                                       level=level, noiselevel=noiselevel)
+
+                       f = open(log_path, 'a')
+                       try:
+                               f.write(msg)
+                       finally:
+                               f.close()
 
 class Binpkg(CompositeTask):
 
@@ -3363,15 +3379,22 @@ class MergeListItem(CompositeTask):
                world_atom = self.world_atom
                ldpath_mtimes = mtimedb["ldpath"]
 
+               action_desc = "Building"
+               preposition = "for"
+               if pkg.type_name == "binary":
+                       action_desc = "Extracting"
+
                if not build_opts.pretend:
                        extra_newline = "\n"
                        if self.background:
                                extra_newline = ""
                        portage.writemsg_stdout(
-                               extra_newline + ">>> Emerging (%s of %s) %s to %s\n" % \
-                               (colorize("MERGE_LIST_PROGRESS", str(pkg_count.curval)),
+                               "%s>>> %s (%s of %s) %s %s %s\n" % \
+                               (extra_newline, action_desc,
+                               colorize("MERGE_LIST_PROGRESS", str(pkg_count.curval)),
                                colorize("MERGE_LIST_PROGRESS", str(pkg_count.maxval)),
-                               colorize("GOOD", pkg.cpv), pkg.root), noiselevel=-1)
+                               colorize("GOOD", pkg.cpv), preposition, pkg.root),
+                               noiselevel=-1)
                        logger.log(" >>> emerge (%s of %s) %s to %s" % \
                                (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg.root))
 
@@ -3435,10 +3458,12 @@ class MergeListItem(CompositeTask):
                        if not (build_opts.buildpkgonly or \
                                build_opts.fetchonly or build_opts.pretend):
 
-                               uninstall = PackageUninstall(ldpath_mtimes=ldpath_mtimes,
-                                       opts=self.emerge_opts, pkg=pkg, settings=settings)
+                               uninstall = PackageUninstall(background=self.background,
+                                       ldpath_mtimes=ldpath_mtimes, opts=self.emerge_opts,
+                                       pkg=pkg, scheduler=scheduler, settings=settings)
 
-                               retval = uninstall.execute()
+                               uninstall.start()
+                               retval = uninstall.wait()
                                if retval != os.EX_OK:
                                        return retval
                        return os.EX_OK
@@ -3459,6 +3484,30 @@ class PackageMerge(AsynchronousTask):
        __slots__ = ("merge",)
 
        def _start(self):
+
+               pkg = self.merge.pkg
+               pkg_count = self.merge.pkg_count
+
+               if pkg.installed:
+
+                       action_desc = "Uninstalling"
+                       preposition = "from"
+
+                       portage.writemsg_stdout(
+                               ">>> %s %s %s %s\n" % \
+                               (action_desc, colorize("GOOD", pkg.cpv),
+                               preposition, pkg.root), noiselevel=-1)
+
+               else:
+
+                       action_desc = "Installing"
+                       preposition = "to"
+
+                       portage.writemsg_stdout(
+                               ">>> %s %s %s %s\n" % \
+                               (action_desc, colorize("GOOD", pkg.cpv),
+                               preposition, pkg.root), noiselevel=-1)
+
                self.returncode = self.merge.merge()
                self.wait()
 
@@ -7999,7 +8048,19 @@ class PollSelectAdapter(PollConstants):
 
                if timeout is not None:
                        select_args = select_args[:]
-                       select_args.append(timeout)
+                       # Translate poll() timeout args to select() timeout args:
+                       #
+                       #          | units        | value(s) for indefinite block
+                       # ---------|--------------|------------------------------
+                       #   poll   | milliseconds | omitted, negative, or None
+                       # ---------|--------------|------------------------------
+                       #   select | seconds      | omitted
+                       # ---------|--------------|------------------------------
+
+                       if timeout is not None and timeout < 0:
+                               timeout = None
+                       if timeout is not None:
+                               select_args.append(timeout / 1000)
 
                select_events = select.select(*select_args)
                poll_events = []
@@ -8186,7 +8247,7 @@ class PollScheduler(object):
 
                return True
 
-       def _next_poll_event(self):
+       def _next_poll_event(self, timeout=None):
                """
                Since the _schedule_wait() loop is called by event
                handlers from _poll_loop(), maintain a central event
@@ -8194,7 +8255,7 @@ class PollScheduler(object):
                poll() call.
                """
                if not self._poll_event_queue:
-                       self._poll_event_queue.extend(self._poll.poll())
+                       self._poll_event_queue.extend(self._poll.poll(timeout))
                return self._poll_event_queue.pop()
 
        def _poll_loop(self):
@@ -8211,6 +8272,31 @@ class PollScheduler(object):
                if not event_handled:
                        raise AssertionError("tight loop")
 
+       def _schedule_yield(self):
+               """
+               Schedule for a short period of time chosen by the scheduler based
+               on internal state. Synchronous tasks should call this periodically
+               in order to allow the scheduler to service pending poll events. The
+               scheduler will call poll() exactly once, without blocking, and any
+               resulting poll events will be serviced.
+               """
+               event_handlers = self._poll_event_handlers
+               events_handled = 0
+
+               if not event_handlers:
+                       return bool(events_handled)
+
+               if not self._poll_event_queue:
+                       self._poll_event_queue.extend(self._poll.poll(0))
+
+               while event_handlers and self._poll_event_queue:
+                       f, event = self._next_poll_event()
+                       handler, reg_id = event_handlers[f]
+                       handler(f, event)
+                       events_handled += 1
+
+               return bool(events_handled)
+
        def _register(self, f, eventmask, handler):
                """
                @rtype: Integer
@@ -8352,17 +8438,19 @@ class TaskScheduler(object):
 
 class JobStatusDisplay(object):
 
-       _bound_properties = ("curval", "merges", "running")
+       _bound_properties = ("curval", "running")
        _jobs_column_width = 45
 
        def __init__(self, quiet=False):
                object.__setattr__(self, "quiet", quiet)
                object.__setattr__(self, "maxval", 0)
+               object.__setattr__(self, "merges", 0)
                object.__setattr__(self, "_changed", False)
                self.reset()
 
        def reset(self):
                self.maxval = 0
+               self.merges = 0
                for name in self._bound_properties:
                        object.__setattr__(self, name, 0)
 
@@ -8430,7 +8518,8 @@ class JobStatusDisplay(object):
                        f.pop_style()
                        f.add_literal_data(" running")
 
-               if self.merges:
+               #if self.merges:
+               if False:
                        f.add_literal_data(", ")
                        f.push_style(number_style)
                        f.add_literal_data(merges_str)
@@ -8466,8 +8555,9 @@ class Scheduler(PollScheduler):
        _fetch_log = EPREFIX + "/var/log/emerge-fetch.log"
 
        class _iface_class(SlotObject):
-               __slots__ = ("dblinkEbuildPhase", "dblinkDisplayMerge", "fetch",
-                       "register", "schedule", "unregister")
+               __slots__ = ("dblinkEbuildPhase", "dblinkDisplayMerge",
+                       "dblinkElog", "fetch", "register", "schedule",
+                       "scheduleYield", "unregister")
 
        class _fetch_iface_class(SlotObject):
                __slots__ = ("log_file", "schedule")
@@ -8535,8 +8625,10 @@ class Scheduler(PollScheduler):
                self._sched_iface = self._iface_class(
                        dblinkEbuildPhase=self._dblink_ebuild_phase,
                        dblinkDisplayMerge=self._dblink_display_merge,
+                       dblinkElog=self._dblink_elog,
                        fetch=fetch_iface, register=self._register,
-                       schedule=self._schedule_wait, unregister=self._unregister)
+                       schedule=self._schedule_wait, scheduleYield=self._schedule_yield,
+                       unregister=self._unregister)
 
                self._task_queues = self._task_queues_class()
                for k in self._task_queues.allowed_keys:
@@ -8728,13 +8820,32 @@ class Scheduler(PollScheduler):
                finally:
                        f.close()
 
+       def _dblink_elog(self, pkg_dblink, phase, func, msgs):
+
+               log_path = pkg_dblink.settings.get("PORTAGE_LOG_FILE")
+               log_file = None
+               out = sys.stdout
+               background = self._max_jobs > 1
+
+               if background and log_path is not None:
+                       log_file = open(log_path, 'a')
+                       out = log_file
+
+               try:
+                       for msg in msgs:
+                               func(msg, phase=phase, key=pkg_dblink.mycpv, out=out)
+               finally:
+                       if log_file is not None:
+                               log_file.close()
+
        def _dblink_display_merge(self, pkg_dblink, msg, level=0, noiselevel=0):
                log_path = pkg_dblink.settings.get("PORTAGE_LOG_FILE")
                background = self._max_jobs > 1
 
                if log_path is None:
-                       portage.util.writemsg_level(msg,
-                               level=level, noiselevel=noiselevel)
+                       if not (background and level < logging.WARN):
+                               portage.util.writemsg_level(msg,
+                                       level=level, noiselevel=noiselevel)
                else:
                        if not background:
                                portage.util.writemsg_level(msg,
@@ -8755,27 +8866,12 @@ class Scheduler(PollScheduler):
                background = self._max_jobs > 1
                log_path = settings.get("PORTAGE_LOG_FILE")
 
-               if phase == "preinst":
-                       msg = ">>> Merging %s to %s\n" % (pkg.cpv, pkg.root)
-                       if not background:
-                               portage.writemsg_stdout(msg)
-                       if log_path is not None:
-                               self._append_to_log_path(log_path, msg)
-
                ebuild_phase = EbuildPhase(background=background,
                        pkg=pkg, phase=phase, scheduler=scheduler,
                        settings=settings, tree=pkg_dblink.treetype)
                ebuild_phase.start()
                ebuild_phase.wait()
 
-               if phase == "postinst" and \
-                       ebuild_phase.returncode == os.EX_OK:
-                       msg = ">>> %s %s\n" % (pkg.cpv, "merged.")
-                       if not background:
-                               portage.writemsg_stdout(msg)
-                       if log_path is not None:
-                               self._append_to_log_path(log_path, msg)
-
                return ebuild_phase.returncode
 
        def _check_manifests(self):
@@ -9568,7 +9664,10 @@ class UninstallFailure(portage.exception.PortageException):
 
 def unmerge(root_config, myopts, unmerge_action,
        unmerge_files, ldpath_mtimes, autoclean=0,
-       clean_world=1, ordered=0, raise_on_error=0):
+       clean_world=1, clean_delay=1, ordered=0, raise_on_error=0,
+       scheduler=None, writemsg_level=portage.util.writemsg_level):
+
+       quiet = "--quiet" in myopts
        settings = root_config.settings
        sets = root_config.sets
        vartree = root_config.trees["vartree"]
@@ -9925,18 +10024,22 @@ def unmerge(root_config, myopts, unmerge_action,
                        #avoid cluttering the preview printout with stuff that isn't getting unmerged
                        continue
                if not (pkgmap[x]["protected"] or pkgmap[x]["omitted"]) and cp in syslist:
-                       print colorize("BAD","\a\n\n!!! '%s' is part of your system profile." % cp)
-                       print colorize("WARN","\a!!! Unmerging it may be damaging to your system.\n")
-                       if "--pretend" not in myopts and "--ask" not in myopts:
+                       writemsg_level(colorize("BAD","\a\n\n!!! " + \
+                               "'%s' is part of your system profile.\n" % cp),
+                               level=logging.WARNING, noiselevel=-1)
+                       writemsg_level(colorize("WARN","\a!!! Unmerging it may " + \
+                               "be damaging to your system.\n\n"),
+                               level=logging.WARNING, noiselevel=-1)
+                       if clean_delay and "--pretend" not in myopts and "--ask" not in myopts:
                                countdown(int(settings["EMERGE_WARNING_DELAY"]),
                                        colorize("UNMERGE_WARN", "Press Ctrl-C to Stop"))
-               if "--quiet" not in myopts:
-                       print "\n "+bold(cp)
+               if not quiet:
+                       writemsg_level("\n %s\n" % (bold(cp),), noiselevel=-1)
                else:
-                       print bold(cp)+": ",
+                       writemsg_level(bold(cp) + ": ", noiselevel=-1)
                for mytype in ["selected","protected","omitted"]:
-                       if "--quiet" not in myopts:
-                               portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1)
+                       if not quiet:
+                               writemsg_level((mytype + ": ").rjust(14), noiselevel=-1)
                        if pkgmap[x][mytype]:
                                sorted_pkgs = [portage.catpkgsplit(mypkg)[1:] for mypkg in pkgmap[x][mytype]]
                                sorted_pkgs.sort(portage.pkgcmp)
@@ -9946,21 +10049,22 @@ def unmerge(root_config, myopts, unmerge_action,
                                        else:
                                                myversion = ver + "-" + rev
                                        if mytype == "selected":
-                                               portage.writemsg_stdout(
-                                                       colorize("UNMERGE_WARN", myversion + " "), noiselevel=-1)
+                                               writemsg_level(
+                                                       colorize("UNMERGE_WARN", myversion + " "),
+                                                       noiselevel=-1)
                                        else:
-                                               portage.writemsg_stdout(
+                                               writemsg_level(
                                                        colorize("GOOD", myversion + " "), noiselevel=-1)
                        else:
-                               portage.writemsg_stdout("none ", noiselevel=-1)
-                       if "--quiet" not in myopts:
-                               portage.writemsg_stdout("\n", noiselevel=-1)
-               if "--quiet" in myopts:
-                       portage.writemsg_stdout("\n", noiselevel=-1)
+                               writemsg_level("none ", noiselevel=-1)
+                       if not quiet:
+                               writemsg_level("\n", noiselevel=-1)
+               if quiet:
+                       writemsg_level("\n", noiselevel=-1)
 
-       portage.writemsg_stdout("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \
+       writemsg_level("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \
                " packages are slated for removal.\n")
-       portage.writemsg_stdout(">>> " + colorize("GOOD", "'Protected'") + \
+       writemsg_level(">>> " + colorize("GOOD", "'Protected'") + \
                        " and " + colorize("GOOD", "'omitted'") + \
                        " packages will not be removed.\n\n")
 
@@ -9976,18 +10080,20 @@ def unmerge(root_config, myopts, unmerge_action,
                        print
                        return 0
        #the real unmerging begins, after a short delay....
-       if not autoclean:
+       if clean_delay and not autoclean:
                countdown(int(settings["CLEAN_DELAY"]), ">>> Unmerging")
 
        for x in xrange(len(pkgmap)):
                for y in pkgmap[x]["selected"]:
-                       print ">>> Unmerging "+y+"..."
+                       writemsg_level(">>> Unmerging "+y+"...\n", noiselevel=-1)
                        emergelog(xterm_titles, "=== Unmerging... ("+y+")")
                        mysplit = y.split("/")
                        #unmerge...
                        retval = portage.unmerge(mysplit[0], mysplit[1], settings["ROOT"],
                                mysettings, unmerge_action not in ["clean","prune"],
-                               vartree=vartree, ldpath_mtimes=ldpath_mtimes)
+                               vartree=vartree, ldpath_mtimes=ldpath_mtimes,
+                               scheduler=scheduler)
+
                        if retval != os.EX_OK:
                                emergelog(xterm_titles, " !!! unmerge FAILURE: "+y)
                                if raise_on_error:
index 8aae5b44dd10040cc56933b4df45a3a9bdc030c9..df0a2099b0068d0b7e5d5536e0dd057add492c67 100644 (file)
@@ -4303,7 +4303,7 @@ def _post_phase_userpriv_perms(mysettings):
 def _post_src_install_checks(mysettings):
        _post_src_install_uid_fix(mysettings)
        global _post_phase_cmds
-       retval = _spawn_misc_sh(mysettings, post_phase_cmds["install"])
+       retval = _spawn_misc_sh(mysettings, _post_phase_cmds["install"])
        if retval != os.EX_OK:
                writemsg("!!! install_qa_check failed; exiting.\n",
                        noiselevel=-1)
@@ -5852,9 +5852,10 @@ def merge(mycat, mypkg, pkgloc, infloc, myroot, mysettings, myebuild=None,
        return mylink.merge(pkgloc, infloc, myroot, myebuild,
                mydbapi=mydbapi, prev_mtimes=prev_mtimes)
 
-def unmerge(cat, pkg, myroot, mysettings, mytrimworld=1, vartree=None, ldpath_mtimes=None):
-       mylink = dblink(
-               cat, pkg, myroot, mysettings, treetype="vartree", vartree=vartree)
+def unmerge(cat, pkg, myroot, mysettings, mytrimworld=1, vartree=None,
+       ldpath_mtimes=None, scheduler=None):
+       mylink = dblink(cat, pkg, myroot, mysettings, treetype="vartree",
+               vartree=vartree, scheduler=scheduler)
        try:
                mylink.lockdb()
                if mylink.exists():
index aa3e9a013f44125456cdc89de630710b918f9271..4a7fc7ad3c9b420e4a201a3408c1b7e96c5a0531 100644 (file)
@@ -25,7 +25,6 @@ from portage import listdir, dep_expand, flatten, key_expand, \
        abssymlink, movefile, _movefile, bsd_chflags, cpv_getkey
 
 from portage.elog import elog_process
-from portage.elog.messages import ewarn
 from portage.elog.filtering import filter_mergephases, filter_unmergephases
 
 import os, re, sys, stat, errno, commands, copy, time, subprocess
@@ -987,13 +986,16 @@ class vardbapi(dbapi):
                                if counter > max_counter:
                                        max_counter = counter
 
+               new_vdb = False
                counter = -1
                try:
                        cfile = open(self._counter_path, "r")
                except EnvironmentError, e:
-                       writemsg("!!! Unable to read COUNTER file: '%s'\n" % \
-                               self._counter_path, noiselevel=-1)
-                       writemsg("!!! %s\n" % str(e), noiselevel=-1)
+                       new_vdb = not bool(self.cpv_all())
+                       if not new_vdb:
+                               writemsg("!!! Unable to read COUNTER file: '%s'\n" % \
+                                       self._counter_path, noiselevel=-1)
+                               writemsg("!!! %s\n" % str(e), noiselevel=-1)
                        del e
                else:
                        try:
@@ -1018,7 +1020,7 @@ class vardbapi(dbapi):
                if counter > max_counter:
                        max_counter = counter
 
-               if counter < 0:
+               if counter < 0 and not new_vdb:
                        writemsg("!!! Initializing COUNTER to " + \
                                "value of %d\n" % max_counter, noiselevel=-1)
 
@@ -1423,6 +1425,10 @@ class dblink(object):
                "sym": 5
        }
 
+       # When looping over files for merge/unmerge, temporarily yield to the
+       # scheduler each time this many files are processed.
+       _file_merge_yield_interval = 20
+
        def __init__(self, cat, pkg, myroot, mysettings, treetype=None,
                vartree=None, blockers=None, scheduler=None):
                """
@@ -1851,9 +1857,8 @@ class dblink(object):
                                                        "pkg_prerm() and pkg_postrm() removal " + \
                                                        "phases to be skipped entirely."
                                                        msg_lines.extend(wrap(msg, 72))
-                                                       from portage.elog.messages import eerror
-                                                       for l in msg_lines:
-                                                               eerror(l, phase=ebuild_phase, key=self.mycpv)
+
+                                                       self._eerror(ebuild_phase, msg_lines)
 
                                                # process logs created during pre/postrm
                                                elog_process(self.mycpv, self.settings, phasefilter=filter_unmergephases)
@@ -1915,6 +1920,7 @@ class dblink(object):
                """
 
                showMessage = self._display_merge
+               scheduler = self._scheduler
 
                if not pkgfiles:
                        showMessage("No package files given... Grabbing a set.\n")
@@ -1983,7 +1989,12 @@ class dblink(object):
                        def show_unmerge(zing, desc, file_type, file_name):
                                        showMessage("%s %s %s %s\n" % \
                                                (zing, desc.ljust(8), file_type, file_name))
-                       for objkey in mykeys:
+                       for i, objkey in enumerate(mykeys):
+
+                               if scheduler is not None and \
+                                       0 == i % self._file_merge_yield_interval:
+                                       scheduler.scheduleYield()
+
                                obj = normalize_path(objkey)
                                file_data = pkgfiles[objkey]
                                file_type = file_data[0]
@@ -2360,17 +2371,21 @@ class dblink(object):
                                self.settings.get("COLLISION_IGNORE", "").split()])
 
                        showMessage = self._display_merge
+                       scheduler = self._scheduler
                        stopmerge = False
-                       i=0
                        collisions = []
                        destroot = normalize_path(destroot).rstrip(os.path.sep) + \
                                os.path.sep
                        showMessage("%s checking %d files for package collisions\n" % \
                                (green("*"), len(mycontents)))
-                       for f in mycontents:
-                               i = i + 1
+                       for i, f in enumerate(mycontents):
                                if i % 1000 == 0:
                                        showMessage("%d files checked ...\n" % i)
+
+                               if scheduler is not None and \
+                                       0 == i % self._file_merge_yield_interval:
+                                       scheduler.scheduleYield()
+
                                dest_path = normalize_path(
                                        os.path.join(destroot, f.lstrip(os.path.sep)))
                                try:
@@ -2430,13 +2445,19 @@ class dblink(object):
                        return 0
 
                showMessage = self._display_merge
+               scheduler = self._scheduler
 
                file_paths = set()
                for dblnk in installed_instances:
                        file_paths.update(dblnk.getcontents())
                inode_map = {}
                real_paths = set()
-               for path in file_paths:
+               for i, path in enumerate(file_paths):
+
+                       if scheduler is not None and \
+                               0 == i % self._file_merge_yield_interval:
+                               scheduler.scheduleYield()
+
                        try:
                                s = os.lstat(path)
                        except OSError, e:
@@ -2463,20 +2484,31 @@ class dblink(object):
                        suspicious_hardlinks.append(path_list)
                if not suspicious_hardlinks:
                        return 0
-               from portage.output import colorize
-               prefix = colorize("SECURITY_WARN", "*") + " WARNING: "
-               showMessage(prefix + "suid/sgid file(s) " + \
-                       "with suspicious hardlink(s):\n",
-                       level=logging.ERROR, noiselevel=-1)
+
+               msg = []
+               msg.append("suid/sgid file(s) " + \
+                       "with suspicious hardlink(s):")
+               msg.append("")
                for path_list in suspicious_hardlinks:
                        for path, s in path_list:
-                               showMessage(prefix + "  '%s'\n" % path,
-                                       level=logging.ERROR, noiselevel=-1)
-               showMessage(prefix + "See the Gentoo Security Handbook " + \
-                       "guide for advice on how to proceed.\n",
-                       level=logging.ERROR, noiselevel=-1)
+                               msg.append("\t%s" % path)
+               msg.append("")
+               msg.append("See the Gentoo Security Handbook " + \
+                       "guide for advice on how to proceed.")
+
+               self._eerror("preinst", msg)
+
                return 1
 
+       def _eerror(self, phase, lines):
+               from portage.elog.messages import eerror as _eerror
+               if self._scheduler is None:
+                       for l in lines:
+                               _eerror(l, phase=phase, key=self.settings.mycpv)
+               else:
+                       self._scheduler.dblinkElog(self,
+                               phase, _eerror, lines)
+
        def treewalk(self, srcroot, destroot, inforoot, myebuild, cleanup=0,
                mydbapi=None, prev_mtimes=None):
                """
@@ -2514,6 +2546,7 @@ class dblink(object):
                """
 
                showMessage = self._display_merge
+               scheduler = self._scheduler
 
                srcroot = normalize_path(srcroot).rstrip(os.path.sep) + os.path.sep
                destroot = normalize_path(destroot).rstrip(os.path.sep) + os.path.sep
@@ -2539,10 +2572,8 @@ class dblink(object):
                if slot is None:
                        slot = ""
 
-               from portage.elog.messages import eerror as _eerror
                def eerror(lines):
-                       for l in lines:
-                               _eerror(l, phase="preinst", key=self.settings.mycpv)
+                       self._eerror("preinst", lines)
 
                if slot != self.settings["SLOT"]:
                        showMessage("!!! WARNING: Expected SLOT='%s', got '%s'\n" % \
@@ -2759,8 +2790,6 @@ class dblink(object):
                self.delete()
                ensure_dirs(self.dbtmpdir)
 
-               scheduler = self._scheduler
-
                # run preinst script
                if scheduler is None:
                        showMessage(">>> Merging %s to %s\n" % (self.mycpv, destroot))
@@ -2985,6 +3014,7 @@ class dblink(object):
                """
 
                showMessage = self._display_merge
+               scheduler = self._scheduler
 
                from os.path import sep, join
                srcroot = normalize_path(srcroot).rstrip(sep) + sep
@@ -2998,7 +3028,13 @@ class dblink(object):
                else:
                        mergelist = stufftomerge
                        offset = ""
-               for x in mergelist:
+
+               for i, x in enumerate(mergelist):
+
+                       if scheduler is not None and \
+                               0 == i % self._file_merge_yield_interval:
+                               scheduler.scheduleYield()
+
                        mysrc = join(srcroot, offset, x)
                        mydest = join(destroot, offset, x)
                        # myrealdest is mydest without the $ROOT prefix (makes a difference if ROOT!="/")
index ac8d701601a9246fe2142ec575f8697cf8de316e..5455e17f4017bbc48394246bd7cfcda45af559a7 100644 (file)
@@ -8,6 +8,7 @@ from portage.const import EBUILD_PHASES
 from portage.util import writemsg
 
 import os
+import sys
 
 def collect_ebuild_messages(path):
        """ Collect elog messages generated by the bash logging function stored 
@@ -66,7 +67,7 @@ def collect_ebuild_messages(path):
        return logentries
 
 _msgbuffer = {}
-def _elog_base(level, msg, phase="other", key=None, color=None):
+def _elog_base(level, msg, phase="other", key=None, color=None, out=None):
        """ Backend for the other messaging functions, should not be called 
            directly.
        """
@@ -75,7 +76,14 @@ def _elog_base(level, msg, phase="other", key=None, color=None):
 
        if color == None:
                color = "GOOD"
-       print colorize(color, " * ")+msg
+
+       formatted_msg = colorize(color, " * ") + msg + "\n"
+
+       if out is None:
+               sys.stdout.write(formatted_msg)
+       else:
+               out.write(formatted_msg)
+
        if key not in _msgbuffer:
                _msgbuffer[key] = {}
        if phase not in _msgbuffer[key]:
@@ -108,11 +116,11 @@ _functions = { "einfo": ("INFO", "GOOD"),
 }
 
 def _make_msgfunction(level, color):
-       def _elog(msg, phase="other", key=None):
+       def _elog(msg, phase="other", key=None, out=None):
                """ Display and log a message assigned to the given key/cpv 
                    (or unassigned if no key is given).
                """ 
-               _elog_base(level, msg,  phase=phase, key=key, color=color)
+               _elog_base(level, msg,  phase=phase, key=key, color=color, out=out)
        return _elog
 
 import sys