mirror of
git://git.yoctoproject.org/poky.git
synced 2025-07-19 21:09:03 +02:00
222 lines
7.1 KiB
Python
222 lines
7.1 KiB
Python
#
|
|
# 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
|
|
from bb.cooker import BBCooker
|
|
from multiprocessing import Event, Process, util
|
|
|
|
logger = logging.getLogger('BitBake')
|
|
|
|
class ServerCommunicator():
|
|
def __init__(self, connection):
|
|
self.connection = connection
|
|
|
|
def runCommand(self, command):
|
|
# @todo try/except
|
|
self.connection.send(command)
|
|
|
|
while True:
|
|
# don't let the user ctrl-c while we're waiting for a response
|
|
try:
|
|
if self.connection.poll(.5):
|
|
return self.connection.recv()
|
|
else:
|
|
return None
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
|
|
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, err:
|
|
print("EventAdapter puked: %s" % str(err))
|
|
|
|
|
|
class ProcessServer(Process):
|
|
profile_filename = "profile.log"
|
|
profile_processed_filename = "profile.log.processed"
|
|
|
|
def __init__(self, command_channel, event_queue, configuration):
|
|
Process.__init__(self)
|
|
self.command_channel = command_channel
|
|
self.event_queue = event_queue
|
|
self.event = EventAdapter(event_queue)
|
|
self.configuration = configuration
|
|
self.cooker = BBCooker(configuration, self.register_idle_function)
|
|
self._idlefunctions = {}
|
|
self.event_handle = bb.event.register_UIHhandler(self)
|
|
self.quit = False
|
|
|
|
self.keep_running = Event()
|
|
self.keep_running.set()
|
|
|
|
for event in bb.event.ui_queue:
|
|
self.event_queue.put(event)
|
|
|
|
def register_idle_function(self, function, data):
|
|
"""Register a function to be called while the server is idle"""
|
|
assert hasattr(function, '__call__')
|
|
self._idlefunctions[function] = data
|
|
|
|
def run(self):
|
|
if self.configuration.profile:
|
|
return self.profile_main()
|
|
else:
|
|
return self.main()
|
|
|
|
def profile_main(self):
|
|
import cProfile
|
|
profiler = cProfile.Profile()
|
|
try:
|
|
return profiler.runcall(self.main)
|
|
finally:
|
|
profiler.dump_stats(self.profile_filename)
|
|
self.write_profile_stats()
|
|
sys.__stderr__.write("Raw profiling information saved to %s and "
|
|
"processed statistics to %s\n" %
|
|
(self.profile_filename,
|
|
self.profile_processed_filename))
|
|
|
|
def write_profile_stats(self):
|
|
import pstats
|
|
with open(self.profile_processed_filename, 'w') as outfile:
|
|
stats = pstats.Stats(self.profile_filename, stream=outfile)
|
|
stats.sort_stats('time')
|
|
stats.print_stats()
|
|
stats.print_callers()
|
|
stats.sort_stats('cumulative')
|
|
stats.print_stats()
|
|
|
|
def main(self):
|
|
# Ignore SIGINT within the server, as all SIGINT handling is done by
|
|
# the UI and communicated to us
|
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
while self.keep_running.is_set():
|
|
try:
|
|
if self.command_channel.poll():
|
|
command = self.command_channel.recv()
|
|
self.runCommand(command)
|
|
|
|
self.idle_commands(.1)
|
|
except Exception:
|
|
logger.exception('Running command %s', command)
|
|
|
|
self.event_queue.cancel_join_thread()
|
|
bb.event.unregister_UIHhandler(self.event_handle)
|
|
self.command_channel.close()
|
|
self.cooker.stop()
|
|
self.idle_commands(.1)
|
|
|
|
def idle_commands(self, delay):
|
|
nextsleep = delay
|
|
|
|
for function, data in self._idlefunctions.items():
|
|
try:
|
|
retval = function(self, data, False)
|
|
if retval is False:
|
|
del self._idlefunctions[function]
|
|
elif retval is True:
|
|
nextsleep = None
|
|
elif nextsleep is None:
|
|
continue
|
|
elif retval < nextsleep:
|
|
nextsleep = retval
|
|
except SystemExit:
|
|
raise
|
|
except Exception:
|
|
logger.exception('Running idle function')
|
|
|
|
if nextsleep is not None:
|
|
time.sleep(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.keep_running.clear()
|
|
|
|
def bootstrap_2_6_6(self):
|
|
"""Pulled from python 2.6.6. Needed to ensure we have the fix from
|
|
http://bugs.python.org/issue5313 when running on python version 2.6.2
|
|
or lower."""
|
|
|
|
try:
|
|
self._children = set()
|
|
self._counter = itertools.count(1)
|
|
try:
|
|
sys.stdin.close()
|
|
sys.stdin = open(os.devnull)
|
|
except (OSError, ValueError):
|
|
pass
|
|
multiprocessing._current_process = self
|
|
util._finalizer_registry.clear()
|
|
util._run_after_forkers()
|
|
util.info('child process calling self.run()')
|
|
try:
|
|
self.run()
|
|
exitcode = 0
|
|
finally:
|
|
util._exit_function()
|
|
except SystemExit, e:
|
|
if not e.args:
|
|
exitcode = 1
|
|
elif type(e.args[0]) is int:
|
|
exitcode = e.args[0]
|
|
else:
|
|
sys.stderr.write(e.args[0] + '\n')
|
|
sys.stderr.flush()
|
|
exitcode = 1
|
|
except:
|
|
exitcode = 1
|
|
import traceback
|
|
sys.stderr.write('Process %s:\n' % self.name)
|
|
sys.stderr.flush()
|
|
traceback.print_exc()
|
|
|
|
util.info('process exiting with exitcode %d' % exitcode)
|
|
return exitcode
|
|
|
|
# Python versions 2.6.0 through 2.6.2 suffer from a multiprocessing bug
|
|
# which can result in a bitbake server hang during the parsing process
|
|
if (2, 6, 0) <= sys.version_info < (2, 6, 3):
|
|
_bootstrap = bootstrap_2_6_6
|