diff options
author | Paul Eggleton <paul.eggleton@linux.intel.com> | 2016-06-23 22:59:05 +1200 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-07-08 09:57:26 +0100 |
commit | ac5e720575dd3c8f86514a7a646de82c8cc28e17 (patch) | |
tree | 9c0cec5d764f1b9fb8f33d5f094d87a8fbc89ab5 /bitbake/lib/bb/ui | |
parent | 1cf6e14a6c5ddb4daec1c1e0cce113eea8570545 (diff) | |
download | poky-ac5e720575dd3c8f86514a7a646de82c8cc28e17.tar.gz |
bitbake: lib: implement basic task progress support
For long-running tasks where we have some output from the task that
gives us some idea of the progress of the task (such as a percentage
complete), provide the means to scrape the output for that progress
information and show it to the user in the default knotty terminal
output in the form of a progress bar. This is implemented using a new
TaskProgress event as well as some code we can insert to do output
scanning/filtering.
Any task can fire TaskProgress events; however, if you have a shell task
whose output you wish to scan for progress information, you just need to
set the "progress" varflag on the task. This can be set to:
* "percent" to just look for a number followed by a % sign
* "percent:<regex>" to specify your own regex matching a percentage
value (must have a single group which matches the percentage number)
* "outof:<regex>" to look for the specified regex matching x out of y
items completed (must have two groups - first group needs to be x,
second y).
We can potentially extend this in future but this should be a good
start.
Part of the implementation for [YOCTO #5383].
(Bitbake rev: 0d275fc5b6531957a6189069b04074065bb718a0)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb/ui')
-rw-r--r-- | bitbake/lib/bb/ui/knotty.py | 74 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/uihelper.py | 7 |
2 files changed, 71 insertions, 10 deletions
diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py index 6a6f6888e3..2513501500 100644 --- a/bitbake/lib/bb/ui/knotty.py +++ b/bitbake/lib/bb/ui/knotty.py | |||
@@ -40,10 +40,13 @@ logger = logging.getLogger("BitBake") | |||
40 | interactive = sys.stdout.isatty() | 40 | interactive = sys.stdout.isatty() |
41 | 41 | ||
42 | class BBProgress(progressbar.ProgressBar): | 42 | class BBProgress(progressbar.ProgressBar): |
43 | def __init__(self, msg, maxval): | 43 | def __init__(self, msg, maxval, widgets=None): |
44 | self.msg = msg | 44 | self.msg = msg |
45 | widgets = [progressbar.Percentage(), ' ', progressbar.Bar(), ' ', | 45 | self.extrapos = -1 |
46 | progressbar.ETA()] | 46 | if not widgets: |
47 | widgets = [progressbar.Percentage(), ' ', progressbar.Bar(), ' ', | ||
48 | progressbar.ETA()] | ||
49 | self.extrapos = 4 | ||
47 | 50 | ||
48 | try: | 51 | try: |
49 | self._resize_default = signal.getsignal(signal.SIGWINCH) | 52 | self._resize_default = signal.getsignal(signal.SIGWINCH) |
@@ -55,11 +58,31 @@ class BBProgress(progressbar.ProgressBar): | |||
55 | progressbar.ProgressBar._handle_resize(self, signum, frame) | 58 | progressbar.ProgressBar._handle_resize(self, signum, frame) |
56 | if self._resize_default: | 59 | if self._resize_default: |
57 | self._resize_default(signum, frame) | 60 | self._resize_default(signum, frame) |
61 | |||
58 | def finish(self): | 62 | def finish(self): |
59 | progressbar.ProgressBar.finish(self) | 63 | progressbar.ProgressBar.finish(self) |
60 | if self._resize_default: | 64 | if self._resize_default: |
61 | signal.signal(signal.SIGWINCH, self._resize_default) | 65 | signal.signal(signal.SIGWINCH, self._resize_default) |
62 | 66 | ||
67 | def setmessage(self, msg): | ||
68 | self.msg = msg | ||
69 | self.widgets[0] = msg | ||
70 | |||
71 | def setextra(self, extra): | ||
72 | if extra: | ||
73 | extrastr = str(extra) | ||
74 | if extrastr[0] != ' ': | ||
75 | extrastr = ' ' + extrastr | ||
76 | if extrastr[-1] != ' ': | ||
77 | extrastr += ' ' | ||
78 | else: | ||
79 | extrastr = ' ' | ||
80 | self.widgets[self.extrapos] = extrastr | ||
81 | |||
82 | def _need_update(self): | ||
83 | # We always want the bar to print when update() is called | ||
84 | return True | ||
85 | |||
63 | class NonInteractiveProgress(object): | 86 | class NonInteractiveProgress(object): |
64 | fobj = sys.stdout | 87 | fobj = sys.stdout |
65 | 88 | ||
@@ -195,15 +218,31 @@ class TerminalFilter(object): | |||
195 | activetasks = self.helper.running_tasks | 218 | activetasks = self.helper.running_tasks |
196 | failedtasks = self.helper.failed_tasks | 219 | failedtasks = self.helper.failed_tasks |
197 | runningpids = self.helper.running_pids | 220 | runningpids = self.helper.running_pids |
198 | if self.footer_present and (self.lastcount == self.helper.tasknumber_current) and (self.lastpids == runningpids): | 221 | if self.footer_present and not self.helper.needUpdate: |
199 | return | 222 | return |
223 | self.helper.needUpdate = False | ||
200 | if self.footer_present: | 224 | if self.footer_present: |
201 | self.clearFooter() | 225 | self.clearFooter() |
202 | if (not self.helper.tasknumber_total or self.helper.tasknumber_current == self.helper.tasknumber_total) and not len(activetasks): | 226 | if (not self.helper.tasknumber_total or self.helper.tasknumber_current == self.helper.tasknumber_total) and not len(activetasks): |
203 | return | 227 | return |
204 | tasks = [] | 228 | tasks = [] |
205 | for t in runningpids: | 229 | for t in runningpids: |
206 | tasks.append("%s (pid %s)" % (activetasks[t]["title"], t)) | 230 | progress = activetasks[t].get("progress", None) |
231 | if progress is not None: | ||
232 | pbar = activetasks[t].get("progressbar", None) | ||
233 | rate = activetasks[t].get("rate", None) | ||
234 | start_time = activetasks[t].get("starttime", None) | ||
235 | if not pbar or pbar.bouncing != (progress < 0): | ||
236 | if progress < 0: | ||
237 | pbar = BBProgress("0: %s (pid %s) " % (activetasks[t]["title"], t), 100, widgets=[progressbar.BouncingSlider()]) | ||
238 | pbar.bouncing = True | ||
239 | else: | ||
240 | pbar = BBProgress("0: %s (pid %s) " % (activetasks[t]["title"], t), 100) | ||
241 | pbar.bouncing = False | ||
242 | activetasks[t]["progressbar"] = pbar | ||
243 | tasks.append((pbar, progress, rate, start_time)) | ||
244 | else: | ||
245 | tasks.append("%s (pid %s)" % (activetasks[t]["title"], t)) | ||
207 | 246 | ||
208 | if self.main.shutdown: | 247 | if self.main.shutdown: |
209 | content = "Waiting for %s running tasks to finish:" % len(activetasks) | 248 | content = "Waiting for %s running tasks to finish:" % len(activetasks) |
@@ -214,8 +253,23 @@ class TerminalFilter(object): | |||
214 | print(content) | 253 | print(content) |
215 | lines = 1 + int(len(content) / (self.columns + 1)) | 254 | lines = 1 + int(len(content) / (self.columns + 1)) |
216 | for tasknum, task in enumerate(tasks[:(self.rows - 2)]): | 255 | for tasknum, task in enumerate(tasks[:(self.rows - 2)]): |
217 | content = "%s: %s" % (tasknum, task) | 256 | if isinstance(task, tuple): |
218 | print(content) | 257 | pbar, progress, rate, start_time = task |
258 | if not pbar.start_time: | ||
259 | pbar.start(False) | ||
260 | if start_time: | ||
261 | pbar.start_time = start_time | ||
262 | pbar.setmessage('%s:%s' % (tasknum, pbar.msg.split(':', 1)[1])) | ||
263 | if progress > -1: | ||
264 | pbar.setextra(rate) | ||
265 | output = pbar.update(progress) | ||
266 | else: | ||
267 | output = pbar.update(1) | ||
268 | if not output or (len(output) <= pbar.term_width): | ||
269 | print('') | ||
270 | else: | ||
271 | content = "%s: %s" % (tasknum, task) | ||
272 | print(content) | ||
219 | lines = lines + 1 + int(len(content) / (self.columns + 1)) | 273 | lines = lines + 1 + int(len(content) / (self.columns + 1)) |
220 | self.footer_present = lines | 274 | self.footer_present = lines |
221 | self.lastpids = runningpids[:] | 275 | self.lastpids = runningpids[:] |
@@ -249,7 +303,8 @@ _evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.Lo | |||
249 | "bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit", | 303 | "bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit", |
250 | "bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted", | 304 | "bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted", |
251 | "bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed", | 305 | "bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed", |
252 | "bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent"] | 306 | "bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent", |
307 | "bb.build.TaskProgress"] | ||
253 | 308 | ||
254 | def main(server, eventHandler, params, tf = TerminalFilter): | 309 | def main(server, eventHandler, params, tf = TerminalFilter): |
255 | 310 | ||
@@ -535,7 +590,8 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
535 | bb.event.OperationStarted, | 590 | bb.event.OperationStarted, |
536 | bb.event.OperationCompleted, | 591 | bb.event.OperationCompleted, |
537 | bb.event.OperationProgress, | 592 | bb.event.OperationProgress, |
538 | bb.event.DiskFull)): | 593 | bb.event.DiskFull, |
594 | bb.build.TaskProgress)): | ||
539 | continue | 595 | continue |
540 | 596 | ||
541 | logger.error("Unknown event: %s", event) | 597 | logger.error("Unknown event: %s", event) |
diff --git a/bitbake/lib/bb/ui/uihelper.py b/bitbake/lib/bb/ui/uihelper.py index db70b763f3..1915e47703 100644 --- a/bitbake/lib/bb/ui/uihelper.py +++ b/bitbake/lib/bb/ui/uihelper.py | |||
@@ -18,6 +18,7 @@ | |||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | 19 | ||
20 | import bb.build | 20 | import bb.build |
21 | import time | ||
21 | 22 | ||
22 | class BBUIHelper: | 23 | class BBUIHelper: |
23 | def __init__(self): | 24 | def __init__(self): |
@@ -31,7 +32,7 @@ class BBUIHelper: | |||
31 | 32 | ||
32 | def eventHandler(self, event): | 33 | def eventHandler(self, event): |
33 | if isinstance(event, bb.build.TaskStarted): | 34 | if isinstance(event, bb.build.TaskStarted): |
34 | self.running_tasks[event.pid] = { 'title' : "%s %s" % (event._package, event._task) } | 35 | self.running_tasks[event.pid] = { 'title' : "%s %s" % (event._package, event._task), 'starttime' : time.time() } |
35 | self.running_pids.append(event.pid) | 36 | self.running_pids.append(event.pid) |
36 | self.needUpdate = True | 37 | self.needUpdate = True |
37 | if isinstance(event, bb.build.TaskSucceeded): | 38 | if isinstance(event, bb.build.TaskSucceeded): |
@@ -52,6 +53,10 @@ class BBUIHelper: | |||
52 | self.tasknumber_current = event.stats.completed + event.stats.active + event.stats.failed + 1 | 53 | self.tasknumber_current = event.stats.completed + event.stats.active + event.stats.failed + 1 |
53 | self.tasknumber_total = event.stats.total | 54 | self.tasknumber_total = event.stats.total |
54 | self.needUpdate = True | 55 | self.needUpdate = True |
56 | if isinstance(event, bb.build.TaskProgress): | ||
57 | self.running_tasks[event.pid]['progress'] = event.progress | ||
58 | self.running_tasks[event.pid]['rate'] = event.rate | ||
59 | self.needUpdate = True | ||
55 | 60 | ||
56 | def getTasks(self): | 61 | def getTasks(self): |
57 | self.needUpdate = False | 62 | self.needUpdate = False |