M7350/oe-core/bitbake/lib/bb/server/process.py

250 lines
8.0 KiB
Python
Raw Normal View History

2024-09-09 08:52:07 +00:00
#
# 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
2024-09-09 08:57:42 +00:00
import select
2024-09-09 08:52:07 +00:00
from Queue import Empty
2024-09-09 08:57:42 +00:00
from multiprocessing import Event, Process, util, Queue, Pipe, queues, Manager
from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
2024-09-09 08:52:07 +00:00
logger = logging.getLogger('BitBake')
class ServerCommunicator():
2024-09-09 08:57:42 +00:00
def __init__(self, connection, event_handle, server):
2024-09-09 08:52:07 +00:00
self.connection = connection
2024-09-09 08:57:42 +00:00
self.event_handle = event_handle
self.server = server
2024-09-09 08:52:07 +00:00
def runCommand(self, command):
# @todo try/except
self.connection.send(command)
2024-09-09 08:57:42 +00:00
if not self.server.is_alive():
raise SystemExit
2024-09-09 08:52:07 +00:00
while True:
# don't let the user ctrl-c while we're waiting for a response
try:
2024-09-09 08:57:42 +00:00
if self.connection.poll(20):
2024-09-09 08:52:07 +00:00
return self.connection.recv()
else:
2024-09-09 08:57:42 +00:00
bb.fatal("Timeout while attempting to communicate with bitbake server")
2024-09-09 08:52:07 +00:00
except KeyboardInterrupt:
pass
2024-09-09 08:57:42 +00:00
def getEventHandle(self):
return self.event_handle.value
2024-09-09 08:52:07 +00:00
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))
2024-09-09 08:57:42 +00:00
class ProcessServer(Process, BaseImplServer):
2024-09-09 08:52:07 +00:00
profile_filename = "profile.log"
profile_processed_filename = "profile.log.processed"
2024-09-09 08:57:42 +00:00
def __init__(self, command_channel, event_queue, featurelist):
BaseImplServer.__init__(self)
2024-09-09 08:52:07 +00:00
Process.__init__(self)
self.command_channel = command_channel
self.event_queue = event_queue
self.event = EventAdapter(event_queue)
2024-09-09 08:57:42 +00:00
self.featurelist = featurelist
2024-09-09 08:52:07 +00:00
self.quit = False
2024-09-09 08:57:42 +00:00
self.quitin, self.quitout = Pipe()
self.event_handle = multiprocessing.Value("i")
2024-09-09 08:52:07 +00:00
def run(self):
for event in bb.event.ui_queue:
self.event_queue.put(event)
2024-09-09 08:57:42 +00:00
self.event_handle.value = bb.event.register_UIHhandler(self)
2024-09-09 08:52:07 +00:00
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
2024-09-09 08:57:42 +00:00
self.quitin.close()
2024-09-09 08:52:07 +00:00
signal.signal(signal.SIGINT, signal.SIG_IGN)
2024-09-09 08:57:42 +00:00
while not self.quit:
2024-09-09 08:52:07 +00:00
try:
if self.command_channel.poll():
command = self.command_channel.recv()
self.runCommand(command)
2024-09-09 08:57:42 +00:00
if self.quitout.poll():
self.quitout.recv()
self.quit = True
2024-09-09 08:52:07 +00:00
2024-09-09 08:57:42 +00:00
self.idle_commands(.1, [self.command_channel, self.quitout])
2024-09-09 08:52:07 +00:00
except Exception:
logger.exception('Running command %s', command)
2024-09-09 08:57:42 +00:00
self.event_queue.close()
bb.event.unregister_UIHhandler(self.event_handle.value)
2024-09-09 08:52:07 +00:00
self.command_channel.close()
2024-09-09 08:57:42 +00:00
self.cooker.shutdown(True)
2024-09-09 08:52:07 +00:00
2024-09-09 08:57:42 +00:00
def idle_commands(self, delay, fds = []):
2024-09-09 08:52:07 +00:00
nextsleep = delay
2024-09-09 08:57:42 +00:00
for function, data in self._idlefuns.items():
2024-09-09 08:52:07 +00:00
try:
retval = function(self, data, False)
if retval is False:
2024-09-09 08:57:42 +00:00
del self._idlefuns[function]
nextsleep = None
2024-09-09 08:52:07 +00:00
elif retval is True:
nextsleep = None
elif nextsleep is None:
continue
2024-09-09 08:57:42 +00:00
else:
fds = fds + retval
2024-09-09 08:52:07 +00:00
except SystemExit:
raise
except Exception:
logger.exception('Running idle function')
2024-09-09 08:57:42 +00:00
del self._idlefuns[function]
self.quit = True
2024-09-09 08:52:07 +00:00
if nextsleep is not None:
2024-09-09 08:57:42 +00:00
select.select(fds,[],[],nextsleep)
2024-09-09 08:52:07 +00:00
def runCommand(self, command):
"""
Run a cooker command on the server
"""
self.command_channel.send(self.cooker.command.runCommand(command))
def stop(self):
2024-09-09 08:57:42 +00:00
self.quitin.send("quit")
self.quitin.close()
2024-09-09 08:52:07 +00:00
2024-09-09 08:57:42 +00:00
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
def sigterm_terminate(self):
bb.error("UI received SIGTERM")
self.terminate()
def terminate(self):
def flushevents():
while True:
try:
event = self.event_queue.get(block=False)
except (Empty, IOError):
break
if isinstance(event, logging.LogRecord):
logger.handle(event)
2024-09-09 08:52:07 +00:00
signal.signal(signal.SIGINT, signal.SIG_IGN)
self.procserver.stop()
2024-09-09 08:57:42 +00:00
while self.procserver.is_alive():
flushevents()
self.procserver.join(0.1)
self.ui_channel.close()
self.event_queue.close()
self.event_queue.setexit()
2024-09-09 08:52:07 +00:00
# Wrap Queue to provide API which isn't server implementation specific
class ProcessEventQueue(multiprocessing.queues.Queue):
2024-09-09 08:57:42 +00:00
def __init__(self, maxsize):
multiprocessing.queues.Queue.__init__(self, maxsize)
self.exit = False
def setexit(self):
self.exit = True
2024-09-09 08:52:07 +00:00
def waitEvent(self, timeout):
2024-09-09 08:57:42 +00:00
if self.exit:
sys.exit(1)
2024-09-09 08:52:07 +00:00
try:
2024-09-09 08:57:42 +00:00
if not self.server.is_alive():
self.setexit()
return None
2024-09-09 08:52:07 +00:00
return self.get(True, timeout)
except Empty:
return None
def getEvent(self):
try:
2024-09-09 08:57:42 +00:00
if not self.server.is_alive():
self.setexit()
return None
2024-09-09 08:52:07 +00:00
return self.get(False)
except Empty:
return None
2024-09-09 08:57:42 +00:00
class BitBakeServer(BitBakeBaseServer):
2024-09-09 08:52:07 +00:00
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)
2024-09-09 08:57:42 +00:00
self.serverImpl = ProcessServer(self.server_channel, self.event_queue, None)
self.event_queue.server = self.serverImpl
2024-09-09 08:52:07 +00:00
2024-09-09 08:57:42 +00:00
def detach(self):
self.serverImpl.start()
2024-09-09 08:52:07 +00:00
return
2024-09-09 08:57:42 +00:00
def establishConnection(self, featureset):
2024-09-09 08:52:07 +00:00
2024-09-09 08:57:42 +00:00
self.connection = BitBakeProcessServerConnection(self.serverImpl, self.ui_channel, self.event_queue)
2024-09-09 08:52:07 +00:00
2024-09-09 08:57:42 +00:00
_, 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