mirror of
git://git.yoctoproject.org/poky.git
synced 2025-07-05 05:04:44 +02:00
bitbake: tinfoil: add wait_for decorator and build_file_sync() helper
The bitbake worker/server IPC is asynchronous, but tinfoil only has functionality to wait for a response on the build_targets() call. Extract the bulk of the "wait for events and handle errors" logic to a standalone wait_for wrapper, which is the build_targets code without the extra_events or event_callback arguments (for now). Then use this to create a build_file_sync() helper that just wraps the existing build_file() with @wait_for. (Bitbake rev: bacd125a9da66cd205f6ba2ab17930b976e82150) Signed-off-by: Ross Burton <ross.burton@arm.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
parent
9a6cf0d455
commit
c02b7accd5
|
@ -14,7 +14,7 @@ import time
|
|||
import atexit
|
||||
import re
|
||||
from collections import OrderedDict, defaultdict
|
||||
from functools import partial
|
||||
from functools import partial, wraps
|
||||
from contextlib import contextmanager
|
||||
|
||||
import bb.cache
|
||||
|
@ -27,6 +27,135 @@ import bb.remotedata
|
|||
from bb.main import setup_bitbake, BitBakeConfigParameters
|
||||
import bb.fetch2
|
||||
|
||||
def wait_for(f):
|
||||
"""
|
||||
Wrap a function that makes an asynchronous tinfoil call using
|
||||
self.run_command() and wait for events to say that the call has been
|
||||
successful, or an error has occurred.
|
||||
"""
|
||||
@wraps(f)
|
||||
def wrapper(self, *args, handle_events=True, extra_events=None, event_callback=None, **kwargs):
|
||||
if handle_events:
|
||||
# A reasonable set of default events matching up with those we handle below
|
||||
eventmask = [
|
||||
'bb.event.BuildStarted',
|
||||
'bb.event.BuildCompleted',
|
||||
'logging.LogRecord',
|
||||
'bb.event.NoProvider',
|
||||
'bb.command.CommandCompleted',
|
||||
'bb.command.CommandFailed',
|
||||
'bb.build.TaskStarted',
|
||||
'bb.build.TaskFailed',
|
||||
'bb.build.TaskSucceeded',
|
||||
'bb.build.TaskFailedSilent',
|
||||
'bb.build.TaskProgress',
|
||||
'bb.runqueue.runQueueTaskStarted',
|
||||
'bb.runqueue.sceneQueueTaskStarted',
|
||||
'bb.event.ProcessStarted',
|
||||
'bb.event.ProcessProgress',
|
||||
'bb.event.ProcessFinished',
|
||||
]
|
||||
if extra_events:
|
||||
eventmask.extend(extra_events)
|
||||
ret = self.set_event_mask(eventmask)
|
||||
|
||||
includelogs = self.config_data.getVar('BBINCLUDELOGS')
|
||||
loglines = self.config_data.getVar('BBINCLUDELOGS_LINES')
|
||||
|
||||
# Call actual function
|
||||
ret = f(self, *args, **kwargs)
|
||||
|
||||
if handle_events:
|
||||
lastevent = time.time()
|
||||
result = False
|
||||
# Borrowed from knotty, instead somewhat hackily we use the helper
|
||||
# as the object to store "shutdown" on
|
||||
helper = bb.ui.uihelper.BBUIHelper()
|
||||
helper.shutdown = 0
|
||||
parseprogress = None
|
||||
termfilter = bb.ui.knotty.TerminalFilter(helper, helper, self.logger.handlers, quiet=self.quiet)
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
event = self.wait_event(0.25)
|
||||
if event:
|
||||
lastevent = time.time()
|
||||
if event_callback and event_callback(event):
|
||||
continue
|
||||
if helper.eventHandler(event):
|
||||
if isinstance(event, bb.build.TaskFailedSilent):
|
||||
self.logger.warning("Logfile for failed setscene task is %s" % event.logfile)
|
||||
elif isinstance(event, bb.build.TaskFailed):
|
||||
bb.ui.knotty.print_event_log(event, includelogs, loglines, termfilter)
|
||||
continue
|
||||
if isinstance(event, bb.event.ProcessStarted):
|
||||
if self.quiet > 1:
|
||||
continue
|
||||
parseprogress = bb.ui.knotty.new_progress(event.processname, event.total)
|
||||
parseprogress.start(False)
|
||||
continue
|
||||
if isinstance(event, bb.event.ProcessProgress):
|
||||
if self.quiet > 1:
|
||||
continue
|
||||
if parseprogress:
|
||||
parseprogress.update(event.progress)
|
||||
else:
|
||||
bb.warn("Got ProcessProgress event for something that never started?")
|
||||
continue
|
||||
if isinstance(event, bb.event.ProcessFinished):
|
||||
if self.quiet > 1:
|
||||
continue
|
||||
if parseprogress:
|
||||
parseprogress.finish()
|
||||
parseprogress = None
|
||||
continue
|
||||
if isinstance(event, bb.command.CommandCompleted):
|
||||
result = True
|
||||
break
|
||||
if isinstance(event, (bb.command.CommandFailed, bb.command.CommandExit)):
|
||||
self.logger.error(str(event))
|
||||
result = False
|
||||
break
|
||||
if isinstance(event, logging.LogRecord):
|
||||
if event.taskpid == 0 or event.levelno > logging.INFO:
|
||||
self.logger.handle(event)
|
||||
continue
|
||||
if isinstance(event, bb.event.NoProvider):
|
||||
self.logger.error(str(event))
|
||||
result = False
|
||||
break
|
||||
elif helper.shutdown > 1:
|
||||
break
|
||||
termfilter.updateFooter()
|
||||
if time.time() > (lastevent + (3*60)):
|
||||
if not self.run_command('ping', handle_events=False):
|
||||
print("\nUnable to ping server and no events, closing down...\n")
|
||||
return False
|
||||
except KeyboardInterrupt:
|
||||
termfilter.clearFooter()
|
||||
if helper.shutdown == 1:
|
||||
print("\nSecond Keyboard Interrupt, stopping...\n")
|
||||
ret = self.run_command("stateForceShutdown")
|
||||
if ret and ret[2]:
|
||||
self.logger.error("Unable to cleanly stop: %s" % ret[2])
|
||||
elif helper.shutdown == 0:
|
||||
print("\nKeyboard Interrupt, closing down...\n")
|
||||
interrupted = True
|
||||
ret = self.run_command("stateShutdown")
|
||||
if ret and ret[2]:
|
||||
self.logger.error("Unable to cleanly shutdown: %s" % ret[2])
|
||||
helper.shutdown = helper.shutdown + 1
|
||||
termfilter.clearFooter()
|
||||
finally:
|
||||
termfilter.finish()
|
||||
if helper.failed_tasks:
|
||||
result = False
|
||||
return result
|
||||
else:
|
||||
return ret
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
# We need this in order to shut down the connection to the bitbake server,
|
||||
# otherwise the process will never properly exit
|
||||
|
@ -700,6 +829,10 @@ class Tinfoil:
|
|||
"""
|
||||
return self.run_command('buildFile', buildfile, task, internal)
|
||||
|
||||
@wait_for
|
||||
def build_file_sync(self, *args):
|
||||
self.build_file(*args)
|
||||
|
||||
def build_targets(self, targets, task=None, handle_events=True, extra_events=None, event_callback=None):
|
||||
"""
|
||||
Builds the specified targets. This is equivalent to a normal invocation
|
||||
|
|
Loading…
Reference in New Issue
Block a user