Squashed 'yocto-poky/' content from commit ea562de

git-subtree-dir: yocto-poky
git-subtree-split: ea562de57590c966cd5a75fda8defecd397e6436
diff --git a/bitbake/lib/bb/server/process.py b/bitbake/lib/bb/server/process.py
new file mode 100644
index 0000000..5fca350
--- /dev/null
+++ b/bitbake/lib/bb/server/process.py
@@ -0,0 +1,264 @@
+#
+# BitBake Process based server.
+#
+# Copyright (C) 2010 Bob Foerster <robert@erafx.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""
+    This module implements a multiprocessing.Process based server for bitbake.
+"""
+
+import bb
+import bb.event
+import itertools
+import logging
+import multiprocessing
+import os
+import signal
+import sys
+import time
+import select
+from Queue import Empty
+from multiprocessing import Event, Process, util, Queue, Pipe, queues, Manager
+
+from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
+
+logger = logging.getLogger('BitBake')
+
+class ServerCommunicator():
+    def __init__(self, connection, event_handle, server):
+        self.connection = connection
+        self.event_handle = event_handle
+        self.server = server
+
+    def runCommand(self, command):
+        # @todo try/except
+        self.connection.send(command)
+
+        if not self.server.is_alive():
+            raise SystemExit
+
+        while True:
+            # don't let the user ctrl-c while we're waiting for a response
+            try:
+                if self.connection.poll(20):
+                    return self.connection.recv()
+                else:
+                    bb.fatal("Timeout while attempting to communicate with bitbake server")
+            except KeyboardInterrupt:
+                pass
+
+    def getEventHandle(self):
+        return self.event_handle.value
+
+class EventAdapter():
+    """
+    Adapter to wrap our event queue since the caller (bb.event) expects to
+    call a send() method, but our actual queue only has put()
+    """
+    def __init__(self, queue):
+        self.queue = queue
+
+    def send(self, event):
+        try:
+            self.queue.put(event)
+        except Exception as err:
+            print("EventAdapter puked: %s" % str(err))
+
+
+class ProcessServer(Process, BaseImplServer):
+    profile_filename = "profile.log"
+    profile_processed_filename = "profile.log.processed"
+
+    def __init__(self, command_channel, event_queue, featurelist):
+        BaseImplServer.__init__(self)
+        Process.__init__(self)
+        self.command_channel = command_channel
+        self.event_queue = event_queue
+        self.event = EventAdapter(event_queue)
+        self.featurelist = featurelist
+        self.quit = False
+
+        self.quitin, self.quitout = Pipe()
+        self.event_handle = multiprocessing.Value("i")
+
+    def run(self):
+        for event in bb.event.ui_queue:
+            self.event_queue.put(event)
+        self.event_handle.value = bb.event.register_UIHhandler(self, True)
+
+        bb.cooker.server_main(self.cooker, self.main)
+
+    def main(self):
+        # Ignore SIGINT within the server, as all SIGINT handling is done by
+        # the UI and communicated to us
+        self.quitin.close()
+        signal.signal(signal.SIGINT, signal.SIG_IGN)
+        while not self.quit:
+            try:
+                if self.command_channel.poll():
+                    command = self.command_channel.recv()
+                    self.runCommand(command)
+                if self.quitout.poll():
+                    self.quitout.recv()
+                    self.quit = True
+                    try:
+                        self.runCommand(["stateForceShutdown"])
+                    except:
+                        pass
+
+                self.idle_commands(.1, [self.command_channel, self.quitout])
+            except Exception:
+                logger.exception('Running command %s', command)
+
+        self.event_queue.close()
+        bb.event.unregister_UIHhandler(self.event_handle.value)
+        self.command_channel.close()
+        self.cooker.shutdown(True)
+        self.quitout.close()
+
+    def idle_commands(self, delay, fds=None):
+        nextsleep = delay
+        if not fds:
+            fds = []
+
+        for function, data in self._idlefuns.items():
+            try:
+                retval = function(self, data, False)
+                if retval is False:
+                    del self._idlefuns[function]
+                    nextsleep = None
+                elif retval is True:
+                    nextsleep = None
+                elif isinstance(retval, float):
+                    if (retval < nextsleep):
+                        nextsleep = retval
+                elif nextsleep is None:
+                    continue
+                else:
+                    fds = fds + retval
+            except SystemExit:
+                raise
+            except Exception as exc:
+                if not isinstance(exc, bb.BBHandledException):
+                    logger.exception('Running idle function')
+                del self._idlefuns[function]
+                self.quit = True
+
+        if nextsleep is not None:
+            select.select(fds,[],[],nextsleep)
+
+    def runCommand(self, command):
+        """
+        Run a cooker command on the server
+        """
+        self.command_channel.send(self.cooker.command.runCommand(command))
+
+    def stop(self):
+        self.quitin.send("quit")
+        self.quitin.close()
+
+class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
+    def __init__(self, serverImpl, ui_channel, event_queue):
+        self.procserver = serverImpl
+        self.ui_channel = ui_channel
+        self.event_queue = event_queue
+        self.connection = ServerCommunicator(self.ui_channel, self.procserver.event_handle, self.procserver)
+        self.events = self.event_queue
+        self.terminated = False
+
+    def sigterm_terminate(self):
+        bb.error("UI received SIGTERM")
+        self.terminate()
+
+    def terminate(self):
+        if self.terminated:
+            return
+        self.terminated = True
+        def flushevents():
+            while True:
+                try:
+                    event = self.event_queue.get(block=False)
+                except (Empty, IOError):
+                    break
+                if isinstance(event, logging.LogRecord):
+                    logger.handle(event)
+
+        signal.signal(signal.SIGINT, signal.SIG_IGN)
+        self.procserver.stop()
+
+        while self.procserver.is_alive():
+            flushevents()
+            self.procserver.join(0.1)
+
+        self.ui_channel.close()
+        self.event_queue.close()
+        self.event_queue.setexit()
+
+# Wrap Queue to provide API which isn't server implementation specific
+class ProcessEventQueue(multiprocessing.queues.Queue):
+    def __init__(self, maxsize):
+        multiprocessing.queues.Queue.__init__(self, maxsize)
+        self.exit = False
+
+    def setexit(self):
+        self.exit = True
+
+    def waitEvent(self, timeout):
+        if self.exit:
+            sys.exit(1)
+        try:
+            if not self.server.is_alive():
+                self.setexit()
+                return None
+            return self.get(True, timeout)
+        except Empty:
+            return None
+
+    def getEvent(self):
+        try:
+            if not self.server.is_alive():
+                self.setexit()
+                return None
+            return self.get(False)
+        except Empty:
+            return None
+
+
+class BitBakeServer(BitBakeBaseServer):
+    def initServer(self):
+        # establish communication channels.  We use bidirectional pipes for
+        # ui <--> server command/response pairs
+        # and a queue for server -> ui event notifications
+        #
+        self.ui_channel, self.server_channel = Pipe()
+        self.event_queue = ProcessEventQueue(0)
+        self.serverImpl = ProcessServer(self.server_channel, self.event_queue, None)
+        self.event_queue.server = self.serverImpl
+
+    def detach(self):
+        self.serverImpl.start()
+        return
+
+    def establishConnection(self, featureset):
+
+        self.connection = BitBakeProcessServerConnection(self.serverImpl, self.ui_channel, self.event_queue)
+
+        _, error = self.connection.connection.runCommand(["setFeatures", featureset])
+        if error:
+            logger.error("Unable to set the cooker to the correct featureset: %s" % error)
+            raise BaseException(error)
+        signal.signal(signal.SIGTERM, lambda i, s: self.connection.sigterm_terminate())
+        return self.connection