diff options
Diffstat (limited to 'bitbake/lib/bb/ui/knotty.py')
-rw-r--r-- | bitbake/lib/bb/ui/knotty.py | 246 |
1 files changed, 179 insertions, 67 deletions
diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py index 0efa614dfc..9a589a5c8e 100644 --- a/bitbake/lib/bb/ui/knotty.py +++ b/bitbake/lib/bb/ui/knotty.py | |||
@@ -21,10 +21,17 @@ import fcntl | |||
21 | import struct | 21 | import struct |
22 | import copy | 22 | import copy |
23 | import atexit | 23 | import atexit |
24 | from itertools import groupby | ||
24 | 25 | ||
25 | from bb.ui import uihelper | 26 | from bb.ui import uihelper |
27 | import bb.build | ||
28 | import bb.command | ||
29 | import bb.cooker | ||
30 | import bb.event | ||
31 | import bb.runqueue | ||
32 | import bb.utils | ||
26 | 33 | ||
27 | featureSet = [bb.cooker.CookerFeatures.SEND_SANITYEVENTS] | 34 | featureSet = [bb.cooker.CookerFeatures.SEND_SANITYEVENTS, bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING] |
28 | 35 | ||
29 | logger = logging.getLogger("BitBake") | 36 | logger = logging.getLogger("BitBake") |
30 | interactive = sys.stdout.isatty() | 37 | interactive = sys.stdout.isatty() |
@@ -102,7 +109,7 @@ def new_progress(msg, maxval): | |||
102 | return NonInteractiveProgress(msg, maxval) | 109 | return NonInteractiveProgress(msg, maxval) |
103 | 110 | ||
104 | def pluralise(singular, plural, qty): | 111 | def pluralise(singular, plural, qty): |
105 | if(qty == 1): | 112 | if qty == 1: |
106 | return singular % qty | 113 | return singular % qty |
107 | else: | 114 | else: |
108 | return plural % qty | 115 | return plural % qty |
@@ -111,6 +118,7 @@ def pluralise(singular, plural, qty): | |||
111 | class InteractConsoleLogFilter(logging.Filter): | 118 | class InteractConsoleLogFilter(logging.Filter): |
112 | def __init__(self, tf): | 119 | def __init__(self, tf): |
113 | self.tf = tf | 120 | self.tf = tf |
121 | super().__init__() | ||
114 | 122 | ||
115 | def filter(self, record): | 123 | def filter(self, record): |
116 | if record.levelno == bb.msg.BBLogFormatter.NOTE and (record.msg.startswith("Running") or record.msg.startswith("recipe ")): | 124 | if record.levelno == bb.msg.BBLogFormatter.NOTE and (record.msg.startswith("Running") or record.msg.startswith("recipe ")): |
@@ -178,7 +186,7 @@ class TerminalFilter(object): | |||
178 | new[3] = new[3] & ~termios.ECHO | 186 | new[3] = new[3] & ~termios.ECHO |
179 | termios.tcsetattr(fd, termios.TCSADRAIN, new) | 187 | termios.tcsetattr(fd, termios.TCSADRAIN, new) |
180 | curses.setupterm() | 188 | curses.setupterm() |
181 | if curses.tigetnum("colors") > 2: | 189 | if curses.tigetnum("colors") > 2 and os.environ.get('NO_COLOR', '') == '': |
182 | for h in handlers: | 190 | for h in handlers: |
183 | try: | 191 | try: |
184 | h.formatter.enable_color() | 192 | h.formatter.enable_color() |
@@ -227,7 +235,9 @@ class TerminalFilter(object): | |||
227 | 235 | ||
228 | def keepAlive(self, t): | 236 | def keepAlive(self, t): |
229 | if not self.cuu: | 237 | if not self.cuu: |
230 | print("Bitbake still alive (%ds)" % t) | 238 | print("Bitbake still alive (no events for %ds). Active tasks:" % t) |
239 | for t in self.helper.running_tasks: | ||
240 | print(t) | ||
231 | sys.stdout.flush() | 241 | sys.stdout.flush() |
232 | 242 | ||
233 | def updateFooter(self): | 243 | def updateFooter(self): |
@@ -249,58 +259,68 @@ class TerminalFilter(object): | |||
249 | return | 259 | return |
250 | tasks = [] | 260 | tasks = [] |
251 | for t in runningpids: | 261 | for t in runningpids: |
262 | start_time = activetasks[t].get("starttime", None) | ||
263 | if start_time: | ||
264 | msg = "%s - %s (pid %s)" % (activetasks[t]["title"], self.elapsed(currenttime - start_time), activetasks[t]["pid"]) | ||
265 | else: | ||
266 | msg = "%s (pid %s)" % (activetasks[t]["title"], activetasks[t]["pid"]) | ||
252 | progress = activetasks[t].get("progress", None) | 267 | progress = activetasks[t].get("progress", None) |
253 | if progress is not None: | 268 | if progress is not None: |
254 | pbar = activetasks[t].get("progressbar", None) | 269 | pbar = activetasks[t].get("progressbar", None) |
255 | rate = activetasks[t].get("rate", None) | 270 | rate = activetasks[t].get("rate", None) |
256 | start_time = activetasks[t].get("starttime", None) | ||
257 | if not pbar or pbar.bouncing != (progress < 0): | 271 | if not pbar or pbar.bouncing != (progress < 0): |
258 | if progress < 0: | 272 | if progress < 0: |
259 | pbar = BBProgress("0: %s (pid %s)" % (activetasks[t]["title"], activetasks[t]["pid"]), 100, widgets=[' ', progressbar.BouncingSlider(), ''], extrapos=3, resize_handler=self.sigwinch_handle) | 273 | pbar = BBProgress("0: %s" % msg, 100, widgets=[' ', progressbar.BouncingSlider(), ''], extrapos=3, resize_handler=self.sigwinch_handle) |
260 | pbar.bouncing = True | 274 | pbar.bouncing = True |
261 | else: | 275 | else: |
262 | pbar = BBProgress("0: %s (pid %s)" % (activetasks[t]["title"], activetasks[t]["pid"]), 100, widgets=[' ', progressbar.Percentage(), ' ', progressbar.Bar(), ''], extrapos=5, resize_handler=self.sigwinch_handle) | 276 | pbar = BBProgress("0: %s" % msg, 100, widgets=[' ', progressbar.Percentage(), ' ', progressbar.Bar(), ''], extrapos=5, resize_handler=self.sigwinch_handle) |
263 | pbar.bouncing = False | 277 | pbar.bouncing = False |
264 | activetasks[t]["progressbar"] = pbar | 278 | activetasks[t]["progressbar"] = pbar |
265 | tasks.append((pbar, progress, rate, start_time)) | 279 | tasks.append((pbar, msg, progress, rate, start_time)) |
266 | else: | 280 | else: |
267 | start_time = activetasks[t].get("starttime", None) | 281 | tasks.append(msg) |
268 | if start_time: | ||
269 | tasks.append("%s - %s (pid %s)" % (activetasks[t]["title"], self.elapsed(currenttime - start_time), activetasks[t]["pid"])) | ||
270 | else: | ||
271 | tasks.append("%s (pid %s)" % (activetasks[t]["title"], activetasks[t]["pid"])) | ||
272 | 282 | ||
273 | if self.main.shutdown: | 283 | if self.main.shutdown: |
274 | content = "Waiting for %s running tasks to finish:" % len(activetasks) | 284 | content = pluralise("Waiting for %s running task to finish", |
285 | "Waiting for %s running tasks to finish", len(activetasks)) | ||
286 | if not self.quiet: | ||
287 | content += ':' | ||
275 | print(content) | 288 | print(content) |
276 | else: | 289 | else: |
290 | scene_tasks = "%s of %s" % (self.helper.setscene_current, self.helper.setscene_total) | ||
291 | cur_tasks = "%s of %s" % (self.helper.tasknumber_current, self.helper.tasknumber_total) | ||
292 | |||
293 | content = '' | ||
294 | if not self.quiet: | ||
295 | msg = "Setscene tasks: %s" % scene_tasks | ||
296 | content += msg + "\n" | ||
297 | print(msg) | ||
298 | |||
277 | if self.quiet: | 299 | if self.quiet: |
278 | content = "Running tasks (%s of %s)" % (self.helper.tasknumber_current, self.helper.tasknumber_total) | 300 | msg = "Running tasks (%s, %s)" % (scene_tasks, cur_tasks) |
279 | elif not len(activetasks): | 301 | elif not len(activetasks): |
280 | content = "No currently running tasks (%s of %s)" % (self.helper.tasknumber_current, self.helper.tasknumber_total) | 302 | msg = "No currently running tasks (%s)" % cur_tasks |
281 | else: | 303 | else: |
282 | content = "Currently %2s running tasks (%s of %s)" % (len(activetasks), self.helper.tasknumber_current, self.helper.tasknumber_total) | 304 | msg = "Currently %2s running tasks (%s)" % (len(activetasks), cur_tasks) |
283 | maxtask = self.helper.tasknumber_total | 305 | maxtask = self.helper.tasknumber_total |
284 | if not self.main_progress or self.main_progress.maxval != maxtask: | 306 | if not self.main_progress or self.main_progress.maxval != maxtask: |
285 | widgets = [' ', progressbar.Percentage(), ' ', progressbar.Bar()] | 307 | widgets = [' ', progressbar.Percentage(), ' ', progressbar.Bar()] |
286 | self.main_progress = BBProgress("Running tasks", maxtask, widgets=widgets, resize_handler=self.sigwinch_handle) | 308 | self.main_progress = BBProgress("Running tasks", maxtask, widgets=widgets, resize_handler=self.sigwinch_handle) |
287 | self.main_progress.start(False) | 309 | self.main_progress.start(False) |
288 | self.main_progress.setmessage(content) | 310 | self.main_progress.setmessage(msg) |
289 | progress = self.helper.tasknumber_current - 1 | 311 | progress = max(0, self.helper.tasknumber_current - 1) |
290 | if progress < 0: | 312 | content += self.main_progress.update(progress) |
291 | progress = 0 | ||
292 | content = self.main_progress.update(progress) | ||
293 | print('') | 313 | print('') |
294 | lines = 1 + int(len(content) / (self.columns + 1)) | 314 | lines = self.getlines(content) |
295 | if self.quiet == 0: | 315 | if not self.quiet: |
296 | for tasknum, task in enumerate(tasks[:(self.rows - 2)]): | 316 | for tasknum, task in enumerate(tasks[:(self.rows - 1 - lines)]): |
297 | if isinstance(task, tuple): | 317 | if isinstance(task, tuple): |
298 | pbar, progress, rate, start_time = task | 318 | pbar, msg, progress, rate, start_time = task |
299 | if not pbar.start_time: | 319 | if not pbar.start_time: |
300 | pbar.start(False) | 320 | pbar.start(False) |
301 | if start_time: | 321 | if start_time: |
302 | pbar.start_time = start_time | 322 | pbar.start_time = start_time |
303 | pbar.setmessage('%s:%s' % (tasknum, pbar.msg.split(':', 1)[1])) | 323 | pbar.setmessage('%s: %s' % (tasknum, msg)) |
304 | pbar.setextra(rate) | 324 | pbar.setextra(rate) |
305 | if progress > -1: | 325 | if progress > -1: |
306 | content = pbar.update(progress) | 326 | content = pbar.update(progress) |
@@ -310,11 +330,17 @@ class TerminalFilter(object): | |||
310 | else: | 330 | else: |
311 | content = "%s: %s" % (tasknum, task) | 331 | content = "%s: %s" % (tasknum, task) |
312 | print(content) | 332 | print(content) |
313 | lines = lines + 1 + int(len(content) / (self.columns + 1)) | 333 | lines = lines + self.getlines(content) |
314 | self.footer_present = lines | 334 | self.footer_present = lines |
315 | self.lastpids = runningpids[:] | 335 | self.lastpids = runningpids[:] |
316 | self.lastcount = self.helper.tasknumber_current | 336 | self.lastcount = self.helper.tasknumber_current |
317 | 337 | ||
338 | def getlines(self, content): | ||
339 | lines = 0 | ||
340 | for line in content.split("\n"): | ||
341 | lines = lines + 1 + int(len(line) / (self.columns + 1)) | ||
342 | return lines | ||
343 | |||
318 | def finish(self): | 344 | def finish(self): |
319 | if self.stdinbackup: | 345 | if self.stdinbackup: |
320 | fd = sys.stdin.fileno() | 346 | fd = sys.stdin.fileno() |
@@ -327,7 +353,7 @@ def print_event_log(event, includelogs, loglines, termfilter): | |||
327 | termfilter.clearFooter() | 353 | termfilter.clearFooter() |
328 | bb.error("Logfile of failure stored in: %s" % logfile) | 354 | bb.error("Logfile of failure stored in: %s" % logfile) |
329 | if includelogs and not event.errprinted: | 355 | if includelogs and not event.errprinted: |
330 | print("Log data follows:") | 356 | bb.plain("Log data follows:") |
331 | f = open(logfile, "r") | 357 | f = open(logfile, "r") |
332 | lines = [] | 358 | lines = [] |
333 | while True: | 359 | while True: |
@@ -340,11 +366,11 @@ def print_event_log(event, includelogs, loglines, termfilter): | |||
340 | if len(lines) > int(loglines): | 366 | if len(lines) > int(loglines): |
341 | lines.pop(0) | 367 | lines.pop(0) |
342 | else: | 368 | else: |
343 | print('| %s' % l) | 369 | bb.plain('| %s' % l) |
344 | f.close() | 370 | f.close() |
345 | if lines: | 371 | if lines: |
346 | for line in lines: | 372 | for line in lines: |
347 | print(line) | 373 | bb.plain(line) |
348 | 374 | ||
349 | def _log_settings_from_server(server, observe_only): | 375 | def _log_settings_from_server(server, observe_only): |
350 | # Get values of variables which control our output | 376 | # Get values of variables which control our output |
@@ -401,6 +427,11 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
401 | except bb.BBHandledException: | 427 | except bb.BBHandledException: |
402 | drain_events_errorhandling(eventHandler) | 428 | drain_events_errorhandling(eventHandler) |
403 | return 1 | 429 | return 1 |
430 | except Exception as e: | ||
431 | # bitbake-server comms failure | ||
432 | early_logger = bb.msg.logger_create('bitbake', sys.stdout) | ||
433 | early_logger.fatal("Attempting to set server environment: %s", e) | ||
434 | return 1 | ||
404 | 435 | ||
405 | if params.options.quiet == 0: | 436 | if params.options.quiet == 0: |
406 | console_loglevel = loglevel | 437 | console_loglevel = loglevel |
@@ -531,13 +562,30 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
531 | } | 562 | } |
532 | }) | 563 | }) |
533 | 564 | ||
534 | bb.utils.mkdirhier(os.path.dirname(consolelogfile)) | 565 | consolelogdirname = os.path.dirname(consolelogfile) |
535 | loglink = os.path.join(os.path.dirname(consolelogfile), 'console-latest.log') | 566 | # `bb.utils.mkdirhier` has this check, but it reports failure using bb.fatal, which logs |
567 | # to the very logger we are trying to set up. | ||
568 | if '${' in str(consolelogdirname): | ||
569 | print( | ||
570 | "FATAL: Directory name {} contains unexpanded bitbake variable. This may cause build failures and WORKDIR pollution.".format( | ||
571 | consolelogdirname)) | ||
572 | if '${MACHINE}' in consolelogdirname: | ||
573 | print("HINT: It looks like you forgot to set MACHINE in local.conf.") | ||
574 | |||
575 | bb.utils.mkdirhier(consolelogdirname) | ||
576 | loglink = os.path.join(consolelogdirname, 'console-latest.log') | ||
536 | bb.utils.remove(loglink) | 577 | bb.utils.remove(loglink) |
537 | try: | 578 | try: |
538 | os.symlink(os.path.basename(consolelogfile), loglink) | 579 | os.symlink(os.path.basename(consolelogfile), loglink) |
539 | except OSError: | 580 | except OSError: |
540 | pass | 581 | pass |
582 | |||
583 | # Add the logging domains specified by the user on the command line | ||
584 | for (domainarg, iterator) in groupby(params.debug_domains): | ||
585 | dlevel = len(tuple(iterator)) | ||
586 | l = logconfig["loggers"].setdefault("BitBake.%s" % domainarg, {}) | ||
587 | l["level"] = logging.DEBUG - dlevel + 1 | ||
588 | l.setdefault("handlers", []).extend(["BitBake.verbconsole"]) | ||
541 | 589 | ||
542 | conf = bb.msg.setLoggingConfig(logconfig, logconfigfile) | 590 | conf = bb.msg.setLoggingConfig(logconfig, logconfigfile) |
543 | 591 | ||
@@ -546,6 +594,8 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
546 | else: | 594 | else: |
547 | log_exec_tty = False | 595 | log_exec_tty = False |
548 | 596 | ||
597 | should_print_hyperlinks = sys.stdout.isatty() and os.environ.get('NO_COLOR', '') == '' | ||
598 | |||
549 | helper = uihelper.BBUIHelper() | 599 | helper = uihelper.BBUIHelper() |
550 | 600 | ||
551 | # Look for the specially designated handlers which need to be passed to the | 601 | # Look for the specially designated handlers which need to be passed to the |
@@ -559,7 +609,12 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
559 | return | 609 | return |
560 | 610 | ||
561 | llevel, debug_domains = bb.msg.constructLogOptions() | 611 | llevel, debug_domains = bb.msg.constructLogOptions() |
562 | server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list]) | 612 | try: |
613 | server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list]) | ||
614 | except (BrokenPipeError, EOFError) as e: | ||
615 | # bitbake-server comms failure | ||
616 | logger.fatal("Attempting to set event mask: %s", e) | ||
617 | return 1 | ||
563 | 618 | ||
564 | # The logging_tree module is *extremely* helpful in debugging logging | 619 | # The logging_tree module is *extremely* helpful in debugging logging |
565 | # domains. Uncomment here to dump the logging tree when bitbake starts | 620 | # domains. Uncomment here to dump the logging tree when bitbake starts |
@@ -568,7 +623,11 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
568 | 623 | ||
569 | universe = False | 624 | universe = False |
570 | if not params.observe_only: | 625 | if not params.observe_only: |
571 | params.updateFromServer(server) | 626 | try: |
627 | params.updateFromServer(server) | ||
628 | except Exception as e: | ||
629 | logger.fatal("Fetching command line: %s", e) | ||
630 | return 1 | ||
572 | cmdline = params.parseActions() | 631 | cmdline = params.parseActions() |
573 | if not cmdline: | 632 | if not cmdline: |
574 | print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.") | 633 | print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.") |
@@ -579,7 +638,12 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
579 | if cmdline['action'][0] == "buildTargets" and "universe" in cmdline['action'][1]: | 638 | if cmdline['action'][0] == "buildTargets" and "universe" in cmdline['action'][1]: |
580 | universe = True | 639 | universe = True |
581 | 640 | ||
582 | ret, error = server.runCommand(cmdline['action']) | 641 | try: |
642 | ret, error = server.runCommand(cmdline['action']) | ||
643 | except (BrokenPipeError, EOFError) as e: | ||
644 | # bitbake-server comms failure | ||
645 | logger.fatal("Command '{}' failed: %s".format(cmdline), e) | ||
646 | return 1 | ||
583 | if error: | 647 | if error: |
584 | logger.error("Command '%s' failed: %s" % (cmdline, error)) | 648 | logger.error("Command '%s' failed: %s" % (cmdline, error)) |
585 | return 1 | 649 | return 1 |
@@ -595,28 +659,42 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
595 | return_value = 0 | 659 | return_value = 0 |
596 | errors = 0 | 660 | errors = 0 |
597 | warnings = 0 | 661 | warnings = 0 |
598 | taskfailures = [] | 662 | taskfailures = {} |
599 | 663 | ||
600 | printinterval = 5000 | 664 | printintervaldelta = 10 * 60 # 10 minutes |
601 | lastprint = time.time() | 665 | printinterval = printintervaldelta |
666 | pinginterval = 1 * 60 # 1 minute | ||
667 | lastevent = lastprint = time.time() | ||
602 | 668 | ||
603 | termfilter = tf(main, helper, console_handlers, params.options.quiet) | 669 | termfilter = tf(main, helper, console_handlers, params.options.quiet) |
604 | atexit.register(termfilter.finish) | 670 | atexit.register(termfilter.finish) |
605 | 671 | ||
606 | while True: | 672 | # shutdown levels |
673 | # 0 - normal operation | ||
674 | # 1 - no new task execution, let current running tasks finish | ||
675 | # 2 - interrupting currently executing tasks | ||
676 | # 3 - we're done, exit | ||
677 | while main.shutdown < 3: | ||
607 | try: | 678 | try: |
608 | if (lastprint + printinterval) <= time.time(): | 679 | if (lastprint + printinterval) <= time.time(): |
609 | termfilter.keepAlive(printinterval) | 680 | termfilter.keepAlive(printinterval) |
610 | printinterval += 5000 | 681 | printinterval += printintervaldelta |
611 | event = eventHandler.waitEvent(0) | 682 | event = eventHandler.waitEvent(0) |
612 | if event is None: | 683 | if event is None: |
613 | if main.shutdown > 1: | 684 | if (lastevent + pinginterval) <= time.time(): |
614 | break | 685 | ret, error = server.runCommand(["ping"]) |
686 | if error or not ret: | ||
687 | termfilter.clearFooter() | ||
688 | print("No reply after pinging server (%s, %s), exiting." % (str(error), str(ret))) | ||
689 | return_value = 3 | ||
690 | main.shutdown = 3 | ||
691 | lastevent = time.time() | ||
615 | if not parseprogress: | 692 | if not parseprogress: |
616 | termfilter.updateFooter() | 693 | termfilter.updateFooter() |
617 | event = eventHandler.waitEvent(0.25) | 694 | event = eventHandler.waitEvent(0.25) |
618 | if event is None: | 695 | if event is None: |
619 | continue | 696 | continue |
697 | lastevent = time.time() | ||
620 | helper.eventHandler(event) | 698 | helper.eventHandler(event) |
621 | if isinstance(event, bb.runqueue.runQueueExitWait): | 699 | if isinstance(event, bb.runqueue.runQueueExitWait): |
622 | if not main.shutdown: | 700 | if not main.shutdown: |
@@ -638,8 +716,8 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
638 | 716 | ||
639 | if isinstance(event, logging.LogRecord): | 717 | if isinstance(event, logging.LogRecord): |
640 | lastprint = time.time() | 718 | lastprint = time.time() |
641 | printinterval = 5000 | 719 | printinterval = printintervaldelta |
642 | if event.levelno >= bb.msg.BBLogFormatter.ERROR: | 720 | if event.levelno >= bb.msg.BBLogFormatter.ERRORONCE: |
643 | errors = errors + 1 | 721 | errors = errors + 1 |
644 | return_value = 1 | 722 | return_value = 1 |
645 | elif event.levelno == bb.msg.BBLogFormatter.WARNING: | 723 | elif event.levelno == bb.msg.BBLogFormatter.WARNING: |
@@ -653,10 +731,10 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
653 | continue | 731 | continue |
654 | 732 | ||
655 | # Prefix task messages with recipe/task | 733 | # Prefix task messages with recipe/task |
656 | if event.taskpid in helper.pidmap and event.levelno != bb.msg.BBLogFormatter.PLAIN: | 734 | if event.taskpid in helper.pidmap and event.levelno not in [bb.msg.BBLogFormatter.PLAIN, bb.msg.BBLogFormatter.WARNONCE, bb.msg.BBLogFormatter.ERRORONCE]: |
657 | taskinfo = helper.running_tasks[helper.pidmap[event.taskpid]] | 735 | taskinfo = helper.running_tasks[helper.pidmap[event.taskpid]] |
658 | event.msg = taskinfo['title'] + ': ' + event.msg | 736 | event.msg = taskinfo['title'] + ': ' + event.msg |
659 | if hasattr(event, 'fn'): | 737 | if hasattr(event, 'fn') and event.levelno not in [bb.msg.BBLogFormatter.WARNONCE, bb.msg.BBLogFormatter.ERRORONCE]: |
660 | event.msg = event.fn + ': ' + event.msg | 738 | event.msg = event.fn + ': ' + event.msg |
661 | logging.getLogger(event.name).handle(event) | 739 | logging.getLogger(event.name).handle(event) |
662 | continue | 740 | continue |
@@ -667,6 +745,8 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
667 | if isinstance(event, bb.build.TaskFailed): | 745 | if isinstance(event, bb.build.TaskFailed): |
668 | return_value = 1 | 746 | return_value = 1 |
669 | print_event_log(event, includelogs, loglines, termfilter) | 747 | print_event_log(event, includelogs, loglines, termfilter) |
748 | k = "{}:{}".format(event._fn, event._task) | ||
749 | taskfailures[k] = event.logfile | ||
670 | if isinstance(event, bb.build.TaskBase): | 750 | if isinstance(event, bb.build.TaskBase): |
671 | logger.info(event._message) | 751 | logger.info(event._message) |
672 | continue | 752 | continue |
@@ -721,15 +801,15 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
721 | if event.error: | 801 | if event.error: |
722 | errors = errors + 1 | 802 | errors = errors + 1 |
723 | logger.error(str(event)) | 803 | logger.error(str(event)) |
724 | main.shutdown = 2 | 804 | main.shutdown = 3 |
725 | continue | 805 | continue |
726 | if isinstance(event, bb.command.CommandExit): | 806 | if isinstance(event, bb.command.CommandExit): |
727 | if not return_value: | 807 | if not return_value: |
728 | return_value = event.exitcode | 808 | return_value = event.exitcode |
729 | main.shutdown = 2 | 809 | main.shutdown = 3 |
730 | continue | 810 | continue |
731 | if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)): | 811 | if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)): |
732 | main.shutdown = 2 | 812 | main.shutdown = 3 |
733 | continue | 813 | continue |
734 | if isinstance(event, bb.event.MultipleProviders): | 814 | if isinstance(event, bb.event.MultipleProviders): |
735 | logger.info(str(event)) | 815 | logger.info(str(event)) |
@@ -745,7 +825,7 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
745 | continue | 825 | continue |
746 | 826 | ||
747 | if isinstance(event, bb.runqueue.sceneQueueTaskStarted): | 827 | if isinstance(event, bb.runqueue.sceneQueueTaskStarted): |
748 | logger.info("Running setscene task %d of %d (%s)" % (event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskstring)) | 828 | logger.info("Running setscene task %d of %d (%s)" % (event.stats.setscene_covered + event.stats.setscene_active + event.stats.setscene_notcovered + 1, event.stats.setscene_total, event.taskstring)) |
749 | continue | 829 | continue |
750 | 830 | ||
751 | if isinstance(event, bb.runqueue.runQueueTaskStarted): | 831 | if isinstance(event, bb.runqueue.runQueueTaskStarted): |
@@ -762,7 +842,7 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
762 | 842 | ||
763 | if isinstance(event, bb.runqueue.runQueueTaskFailed): | 843 | if isinstance(event, bb.runqueue.runQueueTaskFailed): |
764 | return_value = 1 | 844 | return_value = 1 |
765 | taskfailures.append(event.taskstring) | 845 | taskfailures.setdefault(event.taskstring) |
766 | logger.error(str(event)) | 846 | logger.error(str(event)) |
767 | continue | 847 | continue |
768 | 848 | ||
@@ -814,15 +894,26 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
814 | 894 | ||
815 | logger.error("Unknown event: %s", event) | 895 | logger.error("Unknown event: %s", event) |
816 | 896 | ||
897 | except (BrokenPipeError, EOFError) as e: | ||
898 | # bitbake-server comms failure, don't attempt further comms and exit | ||
899 | logger.fatal("Executing event: %s", e) | ||
900 | return_value = 1 | ||
901 | errors = errors + 1 | ||
902 | main.shutdown = 3 | ||
817 | except EnvironmentError as ioerror: | 903 | except EnvironmentError as ioerror: |
818 | termfilter.clearFooter() | 904 | termfilter.clearFooter() |
819 | # ignore interrupted io | 905 | # ignore interrupted io |
820 | if ioerror.args[0] == 4: | 906 | if ioerror.args[0] == 4: |
821 | continue | 907 | continue |
822 | sys.stderr.write(str(ioerror)) | 908 | sys.stderr.write(str(ioerror)) |
823 | if not params.observe_only: | ||
824 | _, error = server.runCommand(["stateForceShutdown"]) | ||
825 | main.shutdown = 2 | 909 | main.shutdown = 2 |
910 | if not params.observe_only: | ||
911 | try: | ||
912 | _, error = server.runCommand(["stateForceShutdown"]) | ||
913 | except (BrokenPipeError, EOFError) as e: | ||
914 | # bitbake-server comms failure, don't attempt further comms and exit | ||
915 | logger.fatal("Unable to force shutdown: %s", e) | ||
916 | main.shutdown = 3 | ||
826 | except KeyboardInterrupt: | 917 | except KeyboardInterrupt: |
827 | termfilter.clearFooter() | 918 | termfilter.clearFooter() |
828 | if params.observe_only: | 919 | if params.observe_only: |
@@ -831,9 +922,13 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
831 | 922 | ||
832 | def state_force_shutdown(): | 923 | def state_force_shutdown(): |
833 | print("\nSecond Keyboard Interrupt, stopping...\n") | 924 | print("\nSecond Keyboard Interrupt, stopping...\n") |
834 | _, error = server.runCommand(["stateForceShutdown"]) | 925 | try: |
835 | if error: | 926 | _, error = server.runCommand(["stateForceShutdown"]) |
836 | logger.error("Unable to cleanly stop: %s" % error) | 927 | if error: |
928 | logger.error("Unable to cleanly stop: %s" % error) | ||
929 | except (BrokenPipeError, EOFError) as e: | ||
930 | # bitbake-server comms failure | ||
931 | logger.fatal("Unable to cleanly stop: %s", e) | ||
837 | 932 | ||
838 | if not params.observe_only and main.shutdown == 1: | 933 | if not params.observe_only and main.shutdown == 1: |
839 | state_force_shutdown() | 934 | state_force_shutdown() |
@@ -846,32 +941,49 @@ def main(server, eventHandler, params, tf = TerminalFilter): | |||
846 | _, error = server.runCommand(["stateShutdown"]) | 941 | _, error = server.runCommand(["stateShutdown"]) |
847 | if error: | 942 | if error: |
848 | logger.error("Unable to cleanly shutdown: %s" % error) | 943 | logger.error("Unable to cleanly shutdown: %s" % error) |
944 | except (BrokenPipeError, EOFError) as e: | ||
945 | # bitbake-server comms failure | ||
946 | logger.fatal("Unable to cleanly shutdown: %s", e) | ||
849 | except KeyboardInterrupt: | 947 | except KeyboardInterrupt: |
850 | state_force_shutdown() | 948 | state_force_shutdown() |
851 | 949 | ||
852 | main.shutdown = main.shutdown + 1 | 950 | main.shutdown = main.shutdown + 1 |
853 | pass | ||
854 | except Exception as e: | 951 | except Exception as e: |
855 | import traceback | 952 | import traceback |
856 | sys.stderr.write(traceback.format_exc()) | 953 | sys.stderr.write(traceback.format_exc()) |
857 | if not params.observe_only: | ||
858 | _, error = server.runCommand(["stateForceShutdown"]) | ||
859 | main.shutdown = 2 | 954 | main.shutdown = 2 |
955 | if not params.observe_only: | ||
956 | try: | ||
957 | _, error = server.runCommand(["stateForceShutdown"]) | ||
958 | except (BrokenPipeError, EOFError) as e: | ||
959 | # bitbake-server comms failure, don't attempt further comms and exit | ||
960 | logger.fatal("Unable to force shutdown: %s", e) | ||
961 | main.shudown = 3 | ||
860 | return_value = 1 | 962 | return_value = 1 |
861 | try: | 963 | try: |
862 | termfilter.clearFooter() | 964 | termfilter.clearFooter() |
863 | summary = "" | 965 | summary = "" |
966 | def format_hyperlink(url, link_text): | ||
967 | if should_print_hyperlinks: | ||
968 | start = f'\033]8;;{url}\033\\' | ||
969 | end = '\033]8;;\033\\' | ||
970 | return f'{start}{link_text}{end}' | ||
971 | return link_text | ||
972 | |||
864 | if taskfailures: | 973 | if taskfailures: |
865 | summary += pluralise("\nSummary: %s task failed:", | 974 | summary += pluralise("\nSummary: %s task failed:", |
866 | "\nSummary: %s tasks failed:", len(taskfailures)) | 975 | "\nSummary: %s tasks failed:", len(taskfailures)) |
867 | for failure in taskfailures: | 976 | for (failure, log_file) in taskfailures.items(): |
868 | summary += "\n %s" % failure | 977 | summary += "\n %s" % failure |
978 | if log_file: | ||
979 | hyperlink = format_hyperlink(f"file://{log_file}", log_file) | ||
980 | summary += "\n log: {}".format(hyperlink) | ||
869 | if warnings: | 981 | if warnings: |
870 | summary += pluralise("\nSummary: There was %s WARNING message shown.", | 982 | summary += pluralise("\nSummary: There was %s WARNING message.", |
871 | "\nSummary: There were %s WARNING messages shown.", warnings) | 983 | "\nSummary: There were %s WARNING messages.", warnings) |
872 | if return_value and errors: | 984 | if return_value and errors: |
873 | summary += pluralise("\nSummary: There was %s ERROR message shown, returning a non-zero exit code.", | 985 | summary += pluralise("\nSummary: There was %s ERROR message, returning a non-zero exit code.", |
874 | "\nSummary: There were %s ERROR messages shown, returning a non-zero exit code.", errors) | 986 | "\nSummary: There were %s ERROR messages, returning a non-zero exit code.", errors) |
875 | if summary and params.options.quiet == 0: | 987 | if summary and params.options.quiet == 0: |
876 | print(summary) | 988 | print(summary) |
877 | 989 | ||