summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/ui/knotty.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/ui/knotty.py')
-rw-r--r--bitbake/lib/bb/ui/knotty.py246
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
21import struct 21import struct
22import copy 22import copy
23import atexit 23import atexit
24from itertools import groupby
24 25
25from bb.ui import uihelper 26from bb.ui import uihelper
27import bb.build
28import bb.command
29import bb.cooker
30import bb.event
31import bb.runqueue
32import bb.utils
26 33
27featureSet = [bb.cooker.CookerFeatures.SEND_SANITYEVENTS] 34featureSet = [bb.cooker.CookerFeatures.SEND_SANITYEVENTS, bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
28 35
29logger = logging.getLogger("BitBake") 36logger = logging.getLogger("BitBake")
30interactive = sys.stdout.isatty() 37interactive = sys.stdout.isatty()
@@ -102,7 +109,7 @@ def new_progress(msg, maxval):
102 return NonInteractiveProgress(msg, maxval) 109 return NonInteractiveProgress(msg, maxval)
103 110
104def pluralise(singular, plural, qty): 111def 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):
111class InteractConsoleLogFilter(logging.Filter): 118class 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
349def _log_settings_from_server(server, observe_only): 375def _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