diff options
| author | Ross Burton <ross.burton@arm.com> | 2025-06-27 14:48:37 +0100 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2025-07-01 08:49:37 +0100 |
| commit | c02b7accd54ea657da9d771814b3dc92d0591127 (patch) | |
| tree | d4fbc022131a49823a701e6c9b0ee84e6b8625d7 /bitbake | |
| parent | 9a6cf0d4554e1835f90a746cea0759e2439b71f7 (diff) | |
| download | poky-c02b7accd54ea657da9d771814b3dc92d0591127.tar.gz | |
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>
Diffstat (limited to 'bitbake')
| -rw-r--r-- | bitbake/lib/bb/tinfoil.py | 135 |
1 files changed, 134 insertions, 1 deletions
diff --git a/bitbake/lib/bb/tinfoil.py b/bitbake/lib/bb/tinfoil.py index f48baeb334..e7fbcbca0a 100644 --- a/bitbake/lib/bb/tinfoil.py +++ b/bitbake/lib/bb/tinfoil.py | |||
| @@ -14,7 +14,7 @@ import time | |||
| 14 | import atexit | 14 | import atexit |
| 15 | import re | 15 | import re |
| 16 | from collections import OrderedDict, defaultdict | 16 | from collections import OrderedDict, defaultdict |
| 17 | from functools import partial | 17 | from functools import partial, wraps |
| 18 | from contextlib import contextmanager | 18 | from contextlib import contextmanager |
| 19 | 19 | ||
| 20 | import bb.cache | 20 | import bb.cache |
| @@ -27,6 +27,135 @@ import bb.remotedata | |||
| 27 | from bb.main import setup_bitbake, BitBakeConfigParameters | 27 | from bb.main import setup_bitbake, BitBakeConfigParameters |
| 28 | import bb.fetch2 | 28 | import bb.fetch2 |
| 29 | 29 | ||
| 30 | def wait_for(f): | ||
| 31 | """ | ||
| 32 | Wrap a function that makes an asynchronous tinfoil call using | ||
| 33 | self.run_command() and wait for events to say that the call has been | ||
| 34 | successful, or an error has occurred. | ||
| 35 | """ | ||
| 36 | @wraps(f) | ||
| 37 | def wrapper(self, *args, handle_events=True, extra_events=None, event_callback=None, **kwargs): | ||
| 38 | if handle_events: | ||
| 39 | # A reasonable set of default events matching up with those we handle below | ||
| 40 | eventmask = [ | ||
| 41 | 'bb.event.BuildStarted', | ||
| 42 | 'bb.event.BuildCompleted', | ||
| 43 | 'logging.LogRecord', | ||
| 44 | 'bb.event.NoProvider', | ||
| 45 | 'bb.command.CommandCompleted', | ||
| 46 | 'bb.command.CommandFailed', | ||
| 47 | 'bb.build.TaskStarted', | ||
| 48 | 'bb.build.TaskFailed', | ||
| 49 | 'bb.build.TaskSucceeded', | ||
| 50 | 'bb.build.TaskFailedSilent', | ||
| 51 | 'bb.build.TaskProgress', | ||
| 52 | 'bb.runqueue.runQueueTaskStarted', | ||
| 53 | 'bb.runqueue.sceneQueueTaskStarted', | ||
| 54 | 'bb.event.ProcessStarted', | ||
| 55 | 'bb.event.ProcessProgress', | ||
| 56 | 'bb.event.ProcessFinished', | ||
| 57 | ] | ||
| 58 | if extra_events: | ||
| 59 | eventmask.extend(extra_events) | ||
| 60 | ret = self.set_event_mask(eventmask) | ||
| 61 | |||
| 62 | includelogs = self.config_data.getVar('BBINCLUDELOGS') | ||
| 63 | loglines = self.config_data.getVar('BBINCLUDELOGS_LINES') | ||
| 64 | |||
| 65 | # Call actual function | ||
| 66 | ret = f(self, *args, **kwargs) | ||
| 67 | |||
| 68 | if handle_events: | ||
| 69 | lastevent = time.time() | ||
| 70 | result = False | ||
| 71 | # Borrowed from knotty, instead somewhat hackily we use the helper | ||
| 72 | # as the object to store "shutdown" on | ||
| 73 | helper = bb.ui.uihelper.BBUIHelper() | ||
| 74 | helper.shutdown = 0 | ||
| 75 | parseprogress = None | ||
| 76 | termfilter = bb.ui.knotty.TerminalFilter(helper, helper, self.logger.handlers, quiet=self.quiet) | ||
| 77 | try: | ||
| 78 | while True: | ||
| 79 | try: | ||
| 80 | event = self.wait_event(0.25) | ||
| 81 | if event: | ||
| 82 | lastevent = time.time() | ||
| 83 | if event_callback and event_callback(event): | ||
| 84 | continue | ||
| 85 | if helper.eventHandler(event): | ||
| 86 | if isinstance(event, bb.build.TaskFailedSilent): | ||
| 87 | self.logger.warning("Logfile for failed setscene task is %s" % event.logfile) | ||
| 88 | elif isinstance(event, bb.build.TaskFailed): | ||
| 89 | bb.ui.knotty.print_event_log(event, includelogs, loglines, termfilter) | ||
| 90 | continue | ||
| 91 | if isinstance(event, bb.event.ProcessStarted): | ||
| 92 | if self.quiet > 1: | ||
| 93 | continue | ||
| 94 | parseprogress = bb.ui.knotty.new_progress(event.processname, event.total) | ||
| 95 | parseprogress.start(False) | ||
| 96 | continue | ||
| 97 | if isinstance(event, bb.event.ProcessProgress): | ||
| 98 | if self.quiet > 1: | ||
| 99 | continue | ||
| 100 | if parseprogress: | ||
| 101 | parseprogress.update(event.progress) | ||
| 102 | else: | ||
| 103 | bb.warn("Got ProcessProgress event for something that never started?") | ||
| 104 | continue | ||
| 105 | if isinstance(event, bb.event.ProcessFinished): | ||
| 106 | if self.quiet > 1: | ||
| 107 | continue | ||
| 108 | if parseprogress: | ||
| 109 | parseprogress.finish() | ||
| 110 | parseprogress = None | ||
| 111 | continue | ||
| 112 | if isinstance(event, bb.command.CommandCompleted): | ||
| 113 | result = True | ||
| 114 | break | ||
| 115 | if isinstance(event, (bb.command.CommandFailed, bb.command.CommandExit)): | ||
| 116 | self.logger.error(str(event)) | ||
| 117 | result = False | ||
| 118 | break | ||
| 119 | if isinstance(event, logging.LogRecord): | ||
| 120 | if event.taskpid == 0 or event.levelno > logging.INFO: | ||
| 121 | self.logger.handle(event) | ||
| 122 | continue | ||
| 123 | if isinstance(event, bb.event.NoProvider): | ||
| 124 | self.logger.error(str(event)) | ||
| 125 | result = False | ||
| 126 | break | ||
| 127 | elif helper.shutdown > 1: | ||
| 128 | break | ||
| 129 | termfilter.updateFooter() | ||
| 130 | if time.time() > (lastevent + (3*60)): | ||
| 131 | if not self.run_command('ping', handle_events=False): | ||
| 132 | print("\nUnable to ping server and no events, closing down...\n") | ||
| 133 | return False | ||
| 134 | except KeyboardInterrupt: | ||
| 135 | termfilter.clearFooter() | ||
| 136 | if helper.shutdown == 1: | ||
| 137 | print("\nSecond Keyboard Interrupt, stopping...\n") | ||
| 138 | ret = self.run_command("stateForceShutdown") | ||
| 139 | if ret and ret[2]: | ||
| 140 | self.logger.error("Unable to cleanly stop: %s" % ret[2]) | ||
| 141 | elif helper.shutdown == 0: | ||
| 142 | print("\nKeyboard Interrupt, closing down...\n") | ||
| 143 | interrupted = True | ||
| 144 | ret = self.run_command("stateShutdown") | ||
| 145 | if ret and ret[2]: | ||
| 146 | self.logger.error("Unable to cleanly shutdown: %s" % ret[2]) | ||
| 147 | helper.shutdown = helper.shutdown + 1 | ||
| 148 | termfilter.clearFooter() | ||
| 149 | finally: | ||
| 150 | termfilter.finish() | ||
| 151 | if helper.failed_tasks: | ||
| 152 | result = False | ||
| 153 | return result | ||
| 154 | else: | ||
| 155 | return ret | ||
| 156 | |||
| 157 | return wrapper | ||
| 158 | |||
| 30 | 159 | ||
| 31 | # We need this in order to shut down the connection to the bitbake server, | 160 | # We need this in order to shut down the connection to the bitbake server, |
| 32 | # otherwise the process will never properly exit | 161 | # otherwise the process will never properly exit |
| @@ -700,6 +829,10 @@ class Tinfoil: | |||
| 700 | """ | 829 | """ |
| 701 | return self.run_command('buildFile', buildfile, task, internal) | 830 | return self.run_command('buildFile', buildfile, task, internal) |
| 702 | 831 | ||
| 832 | @wait_for | ||
| 833 | def build_file_sync(self, *args): | ||
| 834 | self.build_file(*args) | ||
| 835 | |||
| 703 | def build_targets(self, targets, task=None, handle_events=True, extra_events=None, event_callback=None): | 836 | def build_targets(self, targets, task=None, handle_events=True, extra_events=None, event_callback=None): |
| 704 | """ | 837 | """ |
| 705 | Builds the specified targets. This is equivalent to a normal invocation | 838 | Builds the specified targets. This is equivalent to a normal invocation |
