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