def main(self, stdscr, server, eventHandler): #-------------------------------------------------------------------------# height, width = stdscr.getmaxyx() # for now split it like that: # MAIN_y + THREAD_y = 2/3 screen at the top # MAIN_x = 2/3 left, THREAD_y = 1/3 right # CLI_y = 1/3 of screen at the bottom # CLI_x = full main_left = 0 main_top = 0 main_height = ( height / 3 * 2 ) main_width = ( width / 3 ) * 2 clo_left = main_left clo_top = main_top + main_height clo_height = height - main_height - main_top - 1 clo_width = width cli_left = main_left cli_top = clo_top + clo_height cli_height = 1 cli_width = width thread_left = main_left + main_width thread_top = main_top thread_height = main_height thread_width = width - main_width #tw = self.TitleWindow( 0, 0, width, main_top ) mw = self.MainWindow( main_left, main_top, main_width, main_height ) taw = self.ThreadActivityWindow( thread_left, thread_top, thread_width, thread_height ) clo = self.ShellOutputWindow( clo_left, clo_top, clo_width, clo_height ) cli = self.ShellInputWindow( cli_left, cli_top, cli_width, cli_height ) cli.setText( 0, 0, "BB>" ) mw.setStatus("Idle") helper = uihelper.BBUIHelper() shutdown = 0 try: cmdline = server.runCommand(["getCmdLineAction"]) if not cmdline: return ret = server.runCommand(cmdline) if ret != True: print "Couldn't get default commandlind! %s" % ret return except xmlrpclib.Fault, x: print "XMLRPC Fault getting commandline:\n %s" % x return
def init(server, eventHandler): # Get values of variables which control our output includelogs = server.runCommand(["getVariable", "BBINCLUDELOGS"]) loglines = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) helper = uihelper.BBUIHelper() try: cmdline = server.runCommand(["getCmdLineAction"]) #print cmdline if not cmdline: return 1 ret = server.runCommand(cmdline) if ret != True: print "Couldn't get default commandline! %s" % ret return 1 except xmlrpclib.Fault, x: print "XMLRPC Fault getting commandline:\n %s" % x return 1
def main(server, eventHandler, params, tf = TerminalFilter): try: if not params.observe_only: params.updateToServer(server, os.environ.copy()) includelogs, loglines, consolelogfile, logconfigfile = _log_settings_from_server(server, params.observe_only) loglevel, _ = bb.msg.constructLogOptions() except bb.BBHandledException: drain_events_errorhandling(eventHandler) return 1 if params.options.quiet == 0: console_loglevel = loglevel elif params.options.quiet > 2: console_loglevel = bb.msg.BBLogFormatter.ERROR else: console_loglevel = bb.msg.BBLogFormatter.WARNING logconfig = { "version": 1, "handlers": { "BitBake.console": { "class": "logging.StreamHandler", "formatter": "BitBake.consoleFormatter", "level": console_loglevel, "stream": "ext://sys.stdout", "filters": ["BitBake.stdoutFilter"], ".": { "is_console": True, }, }, "BitBake.errconsole": { "class": "logging.StreamHandler", "formatter": "BitBake.consoleFormatter", "level": loglevel, "stream": "ext://sys.stderr", "filters": ["BitBake.stderrFilter"], ".": { "is_console": True, }, }, # This handler can be used if specific loggers should print on # the console at a lower severity than the default. It will # display any messages sent to it that are lower than then # BitBake.console logging level (so as to prevent duplication of # messages). Nothing is attached to this handler by default "BitBake.verbconsole": { "class": "logging.StreamHandler", "formatter": "BitBake.consoleFormatter", "level": 1, "stream": "ext://sys.stdout", "filters": ["BitBake.verbconsoleFilter"], ".": { "is_console": True, }, }, }, "formatters": { # This format instance will get color output enabled by the # terminal "BitBake.consoleFormatter" : { "()": "bb.msg.BBLogFormatter", "format": "%(levelname)s: %(message)s" }, # The file log requires a separate instance so that it doesn't get # color enabled "BitBake.logfileFormatter": { "()": "bb.msg.BBLogFormatter", "format": "%(levelname)s: %(message)s" } }, "filters": { "BitBake.stdoutFilter": { "()": "bb.msg.LogFilterLTLevel", "level": "ERROR" }, "BitBake.stderrFilter": { "()": "bb.msg.LogFilterGEQLevel", "level": "ERROR" }, "BitBake.verbconsoleFilter": { "()": "bb.msg.LogFilterLTLevel", "level": console_loglevel }, }, "loggers": { "BitBake": { "level": loglevel, "handlers": ["BitBake.console", "BitBake.errconsole"], } }, "disable_existing_loggers": False } # Enable the console log file if enabled if consolelogfile and not params.options.show_environment and not params.options.show_versions: logconfig = bb.msg.mergeLoggingConfig(logconfig, { "version": 1, "handlers" : { "BitBake.consolelog": { "class": "logging.FileHandler", "formatter": "BitBake.logfileFormatter", "level": loglevel, "filename": consolelogfile, }, # Just like verbconsole, anything sent here will go to the # log file, unless it would go to BitBake.consolelog "BitBake.verbconsolelog" : { "class": "logging.FileHandler", "formatter": "BitBake.logfileFormatter", "level": 1, "filename": consolelogfile, "filters": ["BitBake.verbconsolelogFilter"], }, }, "filters": { "BitBake.verbconsolelogFilter": { "()": "bb.msg.LogFilterLTLevel", "level": loglevel, }, }, "loggers": { "BitBake": { "handlers": ["BitBake.consolelog"], }, # Other interesting things that we want to keep an eye on # in the log files in case someone has an issue, but not # necessarily show to the user on the console "BitBake.SigGen.HashEquiv": { "level": "VERBOSE", "handlers": ["BitBake.verbconsolelog"], }, "BitBake.RunQueue.HashEquiv": { "level": "VERBOSE", "handlers": ["BitBake.verbconsolelog"], } } }) bb.utils.mkdirhier(os.path.dirname(consolelogfile)) loglink = os.path.join(os.path.dirname(consolelogfile), 'console-latest.log') bb.utils.remove(loglink) try: os.symlink(os.path.basename(consolelogfile), loglink) except OSError: pass conf = bb.msg.setLoggingConfig(logconfig, logconfigfile) if sys.stdin.isatty() and sys.stdout.isatty(): log_exec_tty = True else: log_exec_tty = False helper = uihelper.BBUIHelper() # Look for the specially designated handlers which need to be passed to the # terminal handler console_handlers = [h for h in conf.config['handlers'].values() if getattr(h, 'is_console', False)] bb.utils.set_process_name("KnottyUI") if params.options.remote_server and params.options.kill_server: server.terminateServer() return llevel, debug_domains = bb.msg.constructLogOptions() server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list]) # The logging_tree module is *extremely* helpful in debugging logging # domains. Uncomment here to dump the logging tree when bitbake starts #import logging_tree #logging_tree.printout() universe = False if not params.observe_only: params.updateFromServer(server) cmdline = params.parseActions() if not cmdline: print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.") return 1 if 'msg' in cmdline and cmdline['msg']: logger.error(cmdline['msg']) return 1 if cmdline['action'][0] == "buildTargets" and "universe" in cmdline['action'][1]: universe = True ret, error = server.runCommand(cmdline['action']) if error: logger.error("Command '%s' failed: %s" % (cmdline, error)) return 1 elif not ret: logger.error("Command '%s' failed: returned %s" % (cmdline, ret)) return 1 parseprogress = None cacheprogress = None main.shutdown = 0 interrupted = False return_value = 0 errors = 0 warnings = 0 taskfailures = [] printinterval = 5000 lastprint = time.time() termfilter = tf(main, helper, console_handlers, params.options.quiet) atexit.register(termfilter.finish) while True: try: if (lastprint + printinterval) <= time.time(): termfilter.keepAlive(printinterval) printinterval += 5000 event = eventHandler.waitEvent(0) if event is None: if main.shutdown > 1: break if not parseprogress: termfilter.updateFooter() event = eventHandler.waitEvent(0.25) if event is None: continue helper.eventHandler(event) if isinstance(event, bb.runqueue.runQueueExitWait): if not main.shutdown: main.shutdown = 1 continue if isinstance(event, bb.event.LogExecTTY): if log_exec_tty: tries = event.retries while tries: print("Trying to run: %s" % event.prog) if os.system(event.prog) == 0: break time.sleep(event.sleep_delay) tries -= 1 if tries: continue logger.warning(event.msg) continue if isinstance(event, logging.LogRecord): lastprint = time.time() printinterval = 5000 if event.levelno >= bb.msg.BBLogFormatter.ERROR: errors = errors + 1 return_value = 1 elif event.levelno == bb.msg.BBLogFormatter.WARNING: warnings = warnings + 1 if event.taskpid != 0: # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages if event.levelno <= bb.msg.BBLogFormatter.NOTE and (event.levelno < llevel or (event.levelno == bb.msg.BBLogFormatter.NOTE and llevel != bb.msg.BBLogFormatter.VERBOSE)): continue # Prefix task messages with recipe/task if event.taskpid in helper.pidmap and event.levelno != bb.msg.BBLogFormatter.PLAIN: taskinfo = helper.running_tasks[helper.pidmap[event.taskpid]] event.msg = taskinfo['title'] + ': ' + event.msg if hasattr(event, 'fn'): event.msg = event.fn + ': ' + event.msg logging.getLogger(event.name).handle(event) continue if isinstance(event, bb.build.TaskFailedSilent): logger.warning("Logfile for failed setscene task is %s" % event.logfile) continue if isinstance(event, bb.build.TaskFailed): return_value = 1 print_event_log(event, includelogs, loglines, termfilter) if isinstance(event, bb.build.TaskBase): logger.info(event._message) continue if isinstance(event, bb.event.ParseStarted): if params.options.quiet > 1: continue if event.total == 0: continue termfilter.clearFooter() parseprogress = new_progress("Parsing recipes", event.total).start() continue if isinstance(event, bb.event.ParseProgress): if params.options.quiet > 1: continue if parseprogress: parseprogress.update(event.current) else: bb.warn("Got ParseProgress event for parsing that never started?") continue if isinstance(event, bb.event.ParseCompleted): if params.options.quiet > 1: continue if not parseprogress: continue parseprogress.finish() parseprogress = None if params.options.quiet == 0: print(("Parsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors." % ( event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors))) continue if isinstance(event, bb.event.CacheLoadStarted): if params.options.quiet > 1: continue cacheprogress = new_progress("Loading cache", event.total).start() continue if isinstance(event, bb.event.CacheLoadProgress): if params.options.quiet > 1: continue cacheprogress.update(event.current) continue if isinstance(event, bb.event.CacheLoadCompleted): if params.options.quiet > 1: continue cacheprogress.finish() if params.options.quiet == 0: print("Loaded %d entries from dependency cache." % event.num_entries) continue if isinstance(event, bb.command.CommandFailed): return_value = event.exitcode if event.error: errors = errors + 1 logger.error(str(event)) main.shutdown = 2 continue if isinstance(event, bb.command.CommandExit): if not return_value: return_value = event.exitcode main.shutdown = 2 continue if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)): main.shutdown = 2 continue if isinstance(event, bb.event.MultipleProviders): logger.info(str(event)) continue if isinstance(event, bb.event.NoProvider): # For universe builds, only show these as warnings, not errors if not universe: return_value = 1 errors = errors + 1 logger.error(str(event)) else: logger.warning(str(event)) continue if isinstance(event, bb.runqueue.sceneQueueTaskStarted): logger.info("Running setscene task %d of %d (%s)" % (event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskstring)) continue if isinstance(event, bb.runqueue.runQueueTaskStarted): if event.noexec: tasktype = 'noexec task' else: tasktype = 'task' logger.info("Running %s %d of %d (%s)", tasktype, event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskstring) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): return_value = 1 taskfailures.append(event.taskstring) logger.error(str(event)) continue if isinstance(event, bb.runqueue.sceneQueueTaskFailed): logger.warning(str(event)) continue if isinstance(event, bb.event.DepTreeGenerated): continue if isinstance(event, bb.event.ProcessStarted): if params.options.quiet > 1: continue termfilter.clearFooter() parseprogress = new_progress(event.processname, event.total) parseprogress.start(False) continue if isinstance(event, bb.event.ProcessProgress): if params.options.quiet > 1: continue if parseprogress: parseprogress.update(event.progress) else: bb.warn("Got ProcessProgress event for someting that never started?") continue if isinstance(event, bb.event.ProcessFinished): if params.options.quiet > 1: continue if parseprogress: parseprogress.finish() parseprogress = None continue # ignore if isinstance(event, (bb.event.BuildBase, bb.event.MetadataEvent, bb.event.ConfigParsed, bb.event.MultiConfigParsed, bb.event.RecipeParsed, bb.event.RecipePreFinalise, bb.runqueue.runQueueEvent, bb.event.OperationStarted, bb.event.OperationCompleted, bb.event.OperationProgress, bb.event.DiskFull, bb.event.HeartbeatEvent, bb.build.TaskProgress)): continue logger.error("Unknown event: %s", event) except EnvironmentError as ioerror: termfilter.clearFooter() # ignore interrupted io if ioerror.args[0] == 4: continue sys.stderr.write(str(ioerror)) if not params.observe_only: _, error = server.runCommand(["stateForceShutdown"]) main.shutdown = 2 except KeyboardInterrupt: termfilter.clearFooter() if params.observe_only: print("\nKeyboard Interrupt, exiting observer...") main.shutdown = 2 def state_force_shutdown(): print("\nSecond Keyboard Interrupt, stopping...\n") _, error = server.runCommand(["stateForceShutdown"]) if error: logger.error("Unable to cleanly stop: %s" % error) if not params.observe_only and main.shutdown == 1: state_force_shutdown() if not params.observe_only and main.shutdown == 0: print("\nKeyboard Interrupt, closing down...\n") interrupted = True # Capture the second KeyboardInterrupt during stateShutdown is running try: _, error = server.runCommand(["stateShutdown"]) if error: logger.error("Unable to cleanly shutdown: %s" % error) except KeyboardInterrupt: state_force_shutdown() main.shutdown = main.shutdown + 1 pass except Exception as e: import traceback sys.stderr.write(traceback.format_exc()) if not params.observe_only: _, error = server.runCommand(["stateForceShutdown"]) main.shutdown = 2 return_value = 1 try: termfilter.clearFooter() summary = "" if taskfailures: summary += pluralise("\nSummary: %s task failed:", "\nSummary: %s tasks failed:", len(taskfailures)) for failure in taskfailures: summary += "\n %s" % failure if warnings: summary += pluralise("\nSummary: There was %s WARNING message shown.", "\nSummary: There were %s WARNING messages shown.", warnings) if return_value and errors: summary += pluralise("\nSummary: There was %s ERROR message shown, returning a non-zero exit code.", "\nSummary: There were %s ERROR messages shown, returning a non-zero exit code.", errors) if summary and params.options.quiet == 0: print(summary) if interrupted: print("Execution was interrupted, returning a non-zero exit code.") if return_value == 0: return_value = 1 except IOError as e: import errno if e.errno == errno.EPIPE: pass logging.shutdown() return return_value
def main(server, eventHandler, params): params.updateToServer(server, os.environ.copy()) includelogs, loglines = _log_settings_from_server(server) ui = TeamCityUI() helper = uihelper.BBUIHelper() console = logging.StreamHandler(sys.stdout) errconsole = logging.StreamHandler(sys.stderr) format = TeamcityLogFormatter() if params.options.quiet == 0: forcelevel = None elif params.options.quiet > 2: forcelevel = bb.msg.BBLogFormatter.ERROR else: forcelevel = bb.msg.BBLogFormatter.WARNING bb.msg.addDefaultlogFilter(console, bb.msg.BBLogFilterStdOut, forcelevel) bb.msg.addDefaultlogFilter(errconsole, bb.msg.BBLogFilterStdErr) console.setFormatter(format) errconsole.setFormatter(format) if not bb.msg.has_console_handler(logger): logger.addHandler(console) logger.addHandler(errconsole) if params.options.remote_server and params.options.kill_server: server.terminateServer() return if params.observe_only: logger.error("Observe-only mode not supported in this UI") return 1 llevel, debug_domains = bb.msg.constructLogOptions() server.runCommand([ "setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list ]) try: params.updateFromServer(server) cmdline = params.parseActions() if not cmdline: logger.error("No task given") return 1 if 'msg' in cmdline and cmdline['msg']: logger.error(cmdline['msg']) return 1 cmdline = cmdline['action'] ret, error = server.runCommand(cmdline) if error: logger.error("{0}: {1}".format(cmdline, error)) return 1 elif not ret: logger.error("Couldn't get default commandline: {0}".format(re)) return 1 except xmlrpc.client.Fault as x: logger.error("XMLRPC Fault getting commandline: {0}".format(x)) return 1 active_process_total = None is_tasks_running = False while True: try: event = eventHandler.waitEvent(0.25) if not event: continue helper.eventHandler(event) if isinstance(event, bb.build.TaskBase): logger.info(event._message) if isinstance(event, logging.LogRecord): # Don't report sstate failures as errors, since Yocto will just run the tasks for real if event.msg == "No suitable staging package found" or ( event.msg.startswith( "Fetcher failure: Unable to find file") and "downloadfilename" in event.msg and "sstate" in event.msg): event.levelno = bb.msg.BBLogFormatter.WARNING if event.taskpid != 0: # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages if event.levelno <= bb.msg.BBLogFormatter.NOTE and ( event.levelno < llevel or (event.levelno == bb.msg.BBLogFormatter.NOTE and llevel != bb.msg.BBLogFormatter.VERBOSE)): continue # Prefix task messages with recipe/task if event.taskpid in helper.running_tasks and event.levelno != bb.msg.BBLogFormatter.PLAIN: taskinfo = helper.running_tasks[event.taskpid] event.msg = taskinfo['title'] + ': ' + event.msg if hasattr(event, 'fn'): event.msg = event.fn + ': ' + event.msg logger.handle(event) if isinstance(event, bb.build.TaskFailedSilent): logger.warning("Logfile for failed setscene task is %s" % event.logfile) continue if isinstance(event, bb.build.TaskFailed): rt = "{0}-{1}:{2}".format(event.pn, event.pv.replace("AUTOINC", "0"), event.task) logfile = event.logfile if not logfile or not os.path.exists(logfile): TeamCityUI.emit_service_message( "buildProblem", description= "{0}\nUnknown failure (no log file available)".format( rt)) if not event.task.endswith("_setscene"): server.runCommand(["stateForceShutdown"]) continue details = deque(maxlen=loglines) error_lines = [] if includelogs and not event.errprinted: with open(logfile, "r") as f: while True: line = f.readline() if not line: break line = line.rstrip() details.append(' | %s' % line) # TODO: a less stupid check for errors if (event.task == "do_compile") and ("error:" in line): error_lines.append(line) if error_lines: TeamCityUI.emit_service_message("compilationStarted", compiler=rt) for line in error_lines: TeamCityUI.emit_service_message("message", text=line, status="ERROR") TeamCityUI.emit_service_message("compilationFinished", compiler=rt) else: TeamCityUI.emit_service_message("buildProblem", description=rt) err = "Logfile of failure stored in: %s" % logfile if details: ui.block_start("{0} task log".format(rt)) # TeamCity seems to choke on service messages longer than about 63800 characters, so if error # details is longer than, say, 60000, batch it up into several messages. first_message = True while details: detail_len = 0 batch = deque() while details and detail_len < 60000: # TODO: This code doesn't bother to handle lines that themselves are extremely long. line = details.popleft() batch.append(line) detail_len += len(line) if first_message: batch.appendleft("Log data follows:") first_message = False TeamCityUI.emit_service_message( "message", text=err, status="ERROR", errorDetails="\n".join(batch)) else: TeamCityUI.emit_service_message( "message", text="[continued]", status="ERROR", errorDetails="\n".join(batch)) ui.block_end() else: TeamCityUI.emit_service_message("message", text=err, status="ERROR", errorDetails="") if not event.task.endswith("_setscene"): server.runCommand(["stateForceShutdown"]) if isinstance(event, bb.event.ProcessStarted): if event.processname in [ "Initialising tasks", "Checking sstate mirror object availability" ]: active_process_total = event.total ui.block_start(event.processname) if isinstance(event, bb.event.ProcessFinished): if event.processname in [ "Initialising tasks", "Checking sstate mirror object availability" ]: ui.progress(event.processname, 100) ui.block_end() if isinstance(event, bb.event.ProcessProgress): if event.processname in [ "Initialising tasks", "Checking sstate mirror object availability" ] and active_process_total != 0: ui.progress(event.processname, event.progress * 100 / active_process_total) if isinstance(event, bb.event.CacheLoadStarted): ui.block_start("Loading cache") if isinstance(event, bb.event.CacheLoadProgress): if event.total != 0: ui.progress("Loading cache", math.floor(event.current * 100 / event.total)) if isinstance(event, bb.event.CacheLoadCompleted): ui.progress("Loading cache", 100) ui.block_end() if isinstance(event, bb.event.ParseStarted): ui.block_start( "Parsing recipes and checking upstream revisions") if isinstance(event, bb.event.ParseProgress): if event.total != 0: ui.progress("Parsing recipes", math.floor(event.current * 100 / event.total)) if isinstance(event, bb.event.ParseCompleted): ui.progress("Parsing recipes", 100) ui.block_end() if isinstance(event, bb.command.CommandCompleted): return if isinstance(event, bb.command.CommandFailed): logger.error(str(event)) return 1 if isinstance(event, bb.event.MultipleProviders): logger.warning(str(event)) continue if isinstance(event, bb.event.NoProvider): logger.error(str(event)) continue if isinstance(event, bb.command.CommandExit): return if isinstance(event, bb.cooker.CookerExit): return if isinstance(event, bb.runqueue.sceneQueueTaskStarted): if not is_tasks_running: is_tasks_running = True ui.block_start("Running tasks") if event.stats.total != 0: ui.progress("Running setscene tasks", (event.stats.completed + event.stats.active + event.stats.failed + 1) * 100 / event.stats.total) if isinstance(event, bb.runqueue.runQueueTaskStarted): if not is_tasks_running: is_tasks_running = True ui.block_start("Running tasks") if event.stats.total != 0: pseudo_total = event.stats.total - event.stats.skipped pseudo_complete = event.stats.completed + event.stats.active - event.stats.skipped + event.stats.failed + 1 # TODO: sometimes this gives over 100% ui.progress( "Running runqueue tasks", (pseudo_complete) * 100 / pseudo_total, " ({0}/{1})".format(pseudo_complete, pseudo_total)) if isinstance(event, bb.runqueue.sceneQueueTaskFailed): logger.warning(str(event)) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): logger.error(str(event)) return 1 if isinstance(event, bb.event.LogExecTTY): pass except EnvironmentError as ioerror: # ignore interrupted io if ioerror.args[0] == 4: pass except Exception as ex: logger.error(str(ex))
def main(server, eventHandler, params, tf = TerminalFilter): if not params.observe_only: params.updateToServer(server, os.environ.copy()) includelogs, loglines, consolelogfile = _log_settings_from_server(server, params.observe_only) if sys.stdin.isatty() and sys.stdout.isatty(): log_exec_tty = True else: log_exec_tty = False helper = uihelper.BBUIHelper() console = logging.StreamHandler(sys.stdout) errconsole = logging.StreamHandler(sys.stderr) format_str = "%(levelname)s: %(message)s" format = bb.msg.BBLogFormatter(format_str) if params.options.quiet == 0: forcelevel = None elif params.options.quiet > 2: forcelevel = bb.msg.BBLogFormatter.ERROR else: forcelevel = bb.msg.BBLogFormatter.WARNING bb.msg.addDefaultlogFilter(console, bb.msg.BBLogFilterStdOut, forcelevel) bb.msg.addDefaultlogFilter(errconsole, bb.msg.BBLogFilterStdErr) console.setFormatter(format) errconsole.setFormatter(format) if not bb.msg.has_console_handler(logger): logger.addHandler(console) logger.addHandler(errconsole) bb.utils.set_process_name("KnottyUI") if params.options.remote_server and params.options.kill_server: server.terminateServer() return consolelog = None if consolelogfile and not params.options.show_environment and not params.options.show_versions: bb.utils.mkdirhier(os.path.dirname(consolelogfile)) conlogformat = bb.msg.BBLogFormatter(format_str) consolelog = logging.FileHandler(consolelogfile) bb.msg.addDefaultlogFilter(consolelog) consolelog.setFormatter(conlogformat) logger.addHandler(consolelog) loglink = os.path.join(os.path.dirname(consolelogfile), 'console-latest.log') bb.utils.remove(loglink) try: os.symlink(os.path.basename(consolelogfile), loglink) except OSError: pass llevel, debug_domains = bb.msg.constructLogOptions() server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list]) universe = False if not params.observe_only: params.updateFromServer(server) cmdline = params.parseActions() if not cmdline: print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.") return 1 if 'msg' in cmdline and cmdline['msg']: logger.error(cmdline['msg']) return 1 if cmdline['action'][0] == "buildTargets" and "universe" in cmdline['action'][1]: universe = True ret, error = server.runCommand(cmdline['action']) if error: logger.error("Command '%s' failed: %s" % (cmdline, error)) return 1 elif ret != True: logger.error("Command '%s' failed: returned %s" % (cmdline, ret)) return 1 parseprogress = None cacheprogress = None main.shutdown = 0 interrupted = False return_value = 0 errors = 0 warnings = 0 taskfailures = [] printinterval = 5000 lastprint = time.time() termfilter = tf(main, helper, console, errconsole, format, params.options.quiet) atexit.register(termfilter.finish) while True: try: if (lastprint + printinterval) <= time.time(): termfilter.keepAlive(printinterval) printinterval += 5000 event = eventHandler.waitEvent(0) if event is None: if main.shutdown > 1: break termfilter.updateFooter() event = eventHandler.waitEvent(0.25) if event is None: continue helper.eventHandler(event) if isinstance(event, bb.runqueue.runQueueExitWait): if not main.shutdown: main.shutdown = 1 continue if isinstance(event, bb.event.LogExecTTY): if log_exec_tty: tries = event.retries while tries: print("Trying to run: %s" % event.prog) if os.system(event.prog) == 0: break time.sleep(event.sleep_delay) tries -= 1 if tries: continue logger.warning(event.msg) continue if isinstance(event, logging.LogRecord): lastprint = time.time() printinterval = 5000 if event.levelno >= format.ERROR: errors = errors + 1 return_value = 1 elif event.levelno == format.WARNING: warnings = warnings + 1 if event.taskpid != 0: # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages if event.levelno <= format.NOTE and (event.levelno < llevel or (event.levelno == format.NOTE and llevel != format.VERBOSE)): continue # Prefix task messages with recipe/task if event.taskpid in helper.running_tasks and event.levelno != format.PLAIN: taskinfo = helper.running_tasks[event.taskpid] event.msg = taskinfo['title'] + ': ' + event.msg if hasattr(event, 'fn'): event.msg = event.fn + ': ' + event.msg logger.handle(event) continue if isinstance(event, bb.build.TaskFailedSilent): logger.warning("Logfile for failed setscene task is %s" % event.logfile) continue if isinstance(event, bb.build.TaskFailed): return_value = 1 print_event_log(event, includelogs, loglines, termfilter) if isinstance(event, bb.build.TaskBase): logger.info(event._message) continue if isinstance(event, bb.event.ParseStarted): if params.options.quiet > 1: continue if event.total == 0: continue parseprogress = new_progress("Parsing recipes", event.total).start() continue if isinstance(event, bb.event.ParseProgress): if params.options.quiet > 1: continue if parseprogress: parseprogress.update(event.current) else: bb.warn("Got ParseProgress event for parsing that never started?") continue if isinstance(event, bb.event.ParseCompleted): if params.options.quiet > 1: continue if not parseprogress: continue parseprogress.finish() pasreprogress = None if params.options.quiet == 0: print(("Parsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors." % ( event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors))) continue if isinstance(event, bb.event.CacheLoadStarted): if params.options.quiet > 1: continue cacheprogress = new_progress("Loading cache", event.total).start() continue if isinstance(event, bb.event.CacheLoadProgress): if params.options.quiet > 1: continue cacheprogress.update(event.current) continue if isinstance(event, bb.event.CacheLoadCompleted): if params.options.quiet > 1: continue cacheprogress.finish() if params.options.quiet == 0: print("Loaded %d entries from dependency cache." % event.num_entries) continue if isinstance(event, bb.command.CommandFailed): return_value = event.exitcode if event.error: errors = errors + 1 logger.error(str(event)) main.shutdown = 2 continue if isinstance(event, bb.command.CommandExit): if not return_value: return_value = event.exitcode continue if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)): main.shutdown = 2 continue if isinstance(event, bb.event.MultipleProviders): logger.info(str(event)) continue if isinstance(event, bb.event.NoProvider): # For universe builds, only show these as warnings, not errors if not universe: return_value = 1 errors = errors + 1 logger.error(str(event)) else: logger.warning(str(event)) continue if isinstance(event, bb.runqueue.sceneQueueTaskStarted): logger.info("Running setscene task %d of %d (%s)" % (event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskstring)) continue if isinstance(event, bb.runqueue.runQueueTaskStarted): if event.noexec: tasktype = 'noexec task' else: tasktype = 'task' logger.info("Running %s %d of %d (%s)", tasktype, event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskstring) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): return_value = 1 taskfailures.append(event.taskstring) logger.error(str(event)) continue if isinstance(event, bb.runqueue.sceneQueueTaskFailed): logger.warning(str(event)) continue if isinstance(event, bb.event.DepTreeGenerated): continue if isinstance(event, bb.event.ProcessStarted): if params.options.quiet > 1: continue parseprogress = new_progress(event.processname, event.total) parseprogress.start(False) continue if isinstance(event, bb.event.ProcessProgress): if params.options.quiet > 1: continue if parseprogress: parseprogress.update(event.progress) else: bb.warn("Got ProcessProgress event for someting that never started?") continue if isinstance(event, bb.event.ProcessFinished): if params.options.quiet > 1: continue if parseprogress: parseprogress.finish() parseprogress = None continue # ignore if isinstance(event, (bb.event.BuildBase, bb.event.MetadataEvent, bb.event.StampUpdate, bb.event.ConfigParsed, bb.event.MultiConfigParsed, bb.event.RecipeParsed, bb.event.RecipePreFinalise, bb.runqueue.runQueueEvent, bb.event.OperationStarted, bb.event.OperationCompleted, bb.event.OperationProgress, bb.event.DiskFull, bb.event.HeartbeatEvent, bb.build.TaskProgress)): continue logger.error("Unknown event: %s", event) except EnvironmentError as ioerror: termfilter.clearFooter() # ignore interrupted io if ioerror.args[0] == 4: continue sys.stderr.write(str(ioerror)) if not params.observe_only: _, error = server.runCommand(["stateForceShutdown"]) main.shutdown = 2 except KeyboardInterrupt: termfilter.clearFooter() if params.observe_only: print("\nKeyboard Interrupt, exiting observer...") main.shutdown = 2 if not params.observe_only and main.shutdown == 1: print("\nSecond Keyboard Interrupt, stopping...\n") _, error = server.runCommand(["stateForceShutdown"]) if error: logger.error("Unable to cleanly stop: %s" % error) if not params.observe_only and main.shutdown == 0: print("\nKeyboard Interrupt, closing down...\n") interrupted = True _, error = server.runCommand(["stateShutdown"]) if error: logger.error("Unable to cleanly shutdown: %s" % error) main.shutdown = main.shutdown + 1 pass except Exception as e: import traceback sys.stderr.write(traceback.format_exc()) if not params.observe_only: _, error = server.runCommand(["stateForceShutdown"]) main.shutdown = 2 return_value = 1 try: termfilter.clearFooter() summary = "" if taskfailures: summary += pluralise("\nSummary: %s task failed:", "\nSummary: %s tasks failed:", len(taskfailures)) for failure in taskfailures: summary += "\n %s" % failure if warnings: summary += pluralise("\nSummary: There was %s WARNING message shown.", "\nSummary: There were %s WARNING messages shown.", warnings) if return_value and errors: summary += pluralise("\nSummary: There was %s ERROR message shown, returning a non-zero exit code.", "\nSummary: There were %s ERROR messages shown, returning a non-zero exit code.", errors) if summary and params.options.quiet == 0: print(summary) if interrupted: print("Execution was interrupted, returning a non-zero exit code.") if return_value == 0: return_value = 1 except IOError as e: import errno if e.errno == errno.EPIPE: pass if consolelog: logger.removeHandler(consolelog) consolelog.close() return return_value
def main(server, eventHandler, tf=TerminalFilter): # Get values of variables which control our output includelogs = server.runCommand(["getVariable", "BBINCLUDELOGS"]) loglines = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) consolelogfile = server.runCommand(["getVariable", "BB_CONSOLELOG"]) if sys.stdin.isatty() and sys.stdout.isatty(): log_exec_tty = True else: log_exec_tty = False helper = uihelper.BBUIHelper() console = logging.StreamHandler(sys.stdout) format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s") bb.msg.addDefaultlogFilter(console) console.setFormatter(format) logger.addHandler(console) if consolelogfile: bb.utils.mkdirhier(os.path.dirname(consolelogfile)) consolelog = logging.FileHandler(consolelogfile) bb.msg.addDefaultlogFilter(consolelog) consolelog.setFormatter(format) logger.addHandler(consolelog) try: cmdline = server.runCommand(["getCmdLineAction"]) if not cmdline: print( "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information." ) return 1 elif not cmdline['action']: print(cmdline['msg']) return 1 ret = server.runCommand(cmdline['action']) if ret != True: print("Couldn't get default commandline! %s" % ret) return 1 except xmlrpclib.Fault as x: print("XMLRPC Fault getting commandline:\n %s" % x) return 1 parseprogress = None cacheprogress = None main.shutdown = 0 interrupted = False return_value = 0 errors = 0 warnings = 0 taskfailures = [] termfilter = tf(main, helper, console, format) while True: try: termfilter.updateFooter() event = eventHandler.waitEvent(0.25) if event is None: if main.shutdown > 1: break continue helper.eventHandler(event) if isinstance(event, bb.runqueue.runQueueExitWait): if not main.shutdown: main.shutdown = 1 if isinstance(event, bb.event.LogExecTTY): if log_exec_tty: tries = event.retries while tries: print "Trying to run: %s" % event.prog if os.system(event.prog) == 0: break time.sleep(event.sleep_delay) tries -= 1 if tries: continue logger.warn(event.msg) continue if isinstance(event, logging.LogRecord): if event.levelno >= format.ERROR: errors = errors + 1 return_value = 1 elif event.levelno == format.WARNING: warnings = warnings + 1 # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages if event.taskpid != 0 and event.levelno <= format.NOTE: continue logger.handle(event) continue if isinstance(event, bb.build.TaskFailed): return_value = 1 logfile = event.logfile if logfile and os.path.exists(logfile): termfilter.clearFooter() print("ERROR: Logfile of failure stored in: %s" % logfile) if includelogs and not event.errprinted: print("Log data follows:") f = open(logfile, "r") lines = [] while True: l = f.readline() if l == '': break l = l.rstrip() if loglines: lines.append(' | %s' % l) if len(lines) > int(loglines): lines.pop(0) else: print('| %s' % l) f.close() if lines: for line in lines: print(line) if isinstance(event, bb.build.TaskBase): logger.info(event._message) continue if isinstance(event, bb.event.ParseStarted): if event.total == 0: continue parseprogress = new_progress("Parsing recipes", event.total).start() continue if isinstance(event, bb.event.ParseProgress): parseprogress.update(event.current) continue if isinstance(event, bb.event.ParseCompleted): if not parseprogress: continue parseprogress.finish() print(( "Parsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors." % (event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors))) continue if isinstance(event, bb.event.CacheLoadStarted): cacheprogress = new_progress("Loading cache", event.total).start() continue if isinstance(event, bb.event.CacheLoadProgress): cacheprogress.update(event.current) continue if isinstance(event, bb.event.CacheLoadCompleted): cacheprogress.finish() print("Loaded %d entries from dependency cache." % event.num_entries) continue if isinstance(event, bb.command.CommandFailed): return_value = event.exitcode errors = errors + 1 logger.error("Command execution failed: %s", event.error) main.shutdown = 2 continue if isinstance(event, bb.command.CommandExit): if not return_value: return_value = event.exitcode continue if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)): main.shutdown = 2 continue if isinstance(event, bb.event.MultipleProviders): logger.info("multiple providers are available for %s%s (%s)", event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates)) logger.info( "consider defining a PREFERRED_PROVIDER entry to match %s", event._item) continue if isinstance(event, bb.event.NoProvider): return_value = 1 errors = errors + 1 if event._runtime: r = "R" else: r = "" if event._dependees: logger.error( "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)", r, event._item, ", ".join(event._dependees), r) else: logger.error("Nothing %sPROVIDES '%s'", r, event._item) if event._reasons: for reason in event._reasons: logger.error("%s", reason) continue if isinstance(event, bb.runqueue.sceneQueueTaskStarted): logger.info("Running setscene task %d of %d (%s)" % (event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskstring)) continue if isinstance(event, bb.runqueue.runQueueTaskStarted): if event.noexec: tasktype = 'noexec task' else: tasktype = 'task' logger.info( "Running %s %s of %s (ID: %s, %s)", tasktype, event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskid, event.taskstring) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): taskfailures.append(event.taskstring) logger.error("Task %s (%s) failed with exit code '%s'", event.taskid, event.taskstring, event.exitcode) continue if isinstance(event, bb.runqueue.sceneQueueTaskFailed): logger.warn( "Setscene task %s (%s) failed with exit code '%s' - real task will be run instead", event.taskid, event.taskstring, event.exitcode) continue # ignore if isinstance( event, (bb.event.BuildBase, bb.event.StampUpdate, bb.event.ConfigParsed, bb.event.RecipeParsed, bb.event.RecipePreFinalise, bb.runqueue.runQueueEvent, bb.runqueue.runQueueExitWait, bb.event.OperationStarted, bb.event.OperationCompleted, bb.event.OperationProgress)): continue logger.error("Unknown event: %s", event) except EnvironmentError as ioerror: termfilter.clearFooter() # ignore interrupted io if ioerror.args[0] == 4: pass except KeyboardInterrupt: termfilter.clearFooter() if main.shutdown == 1: print("\nSecond Keyboard Interrupt, stopping...\n") server.runCommand(["stateStop"]) if main.shutdown == 0: interrupted = True print("\nKeyboard Interrupt, closing down...\n") server.runCommand(["stateShutdown"]) main.shutdown = main.shutdown + 1 pass summary = "" if taskfailures: summary += pluralise("\nSummary: %s task failed:", "\nSummary: %s tasks failed:", len(taskfailures)) for failure in taskfailures: summary += "\n %s" % failure if warnings: summary += pluralise( "\nSummary: There was %s WARNING message shown.", "\nSummary: There were %s WARNING messages shown.", warnings) if return_value: summary += pluralise( "\nSummary: There was %s ERROR message shown, returning a non-zero exit code.", "\nSummary: There were %s ERROR messages shown, returning a non-zero exit code.", errors) if summary: print(summary) if interrupted: print("Execution was interrupted, returning a non-zero exit code.") if return_value == 0: return_value = 1 termfilter.finish() return return_value
def main(server, eventHandler, params): # set to a logging.FileHandler instance when a build starts; # see _open_build_log() build_log = None # set to the log path when a build starts build_log_file_path = None helper = uihelper.BBUIHelper() # TODO don't use log output to determine when bitbake has started # # WARNING: this log handler cannot be removed, as localhostbecontroller # relies on output in the toaster_ui.log file to determine whether # the bitbake server has started, which only happens if # this logger is setup here (see the TODO in the loop below) console = logging.StreamHandler(sys.stdout) format_str = "%(levelname)s: %(message)s" formatter = bb.msg.BBLogFormatter(format_str) bb.msg.addDefaultlogFilter(console) console.setFormatter(formatter) logger.addHandler(console) logger.setLevel(logging.INFO) llevel, debug_domains = bb.msg.constructLogOptions() server.runCommand([ "setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list ]) # verify and warn build_history_enabled = True inheritlist, _ = server.runCommand(["getVariable", "INHERIT"]) if not "buildhistory" in inheritlist.split(" "): logger.warn( "buildhistory is not enabled. Please enable INHERIT += \"buildhistory\" to see image details." ) build_history_enabled = False if not params.observe_only: logger.error("ToasterUI can only work in observer mode") return 1 # set to 1 when toasterui needs to shut down main.shutdown = 0 interrupted = False return_value = 0 errors = 0 warnings = 0 taskfailures = [] first = True buildinfohelper = BuildInfoHelper(server, build_history_enabled) # write our own log files into bitbake's log directory; # we're only interested in the path to the parent directory of # this file, as we're writing our own logs into the same directory consolelogfile = _log_settings_from_server(server) log_dir = os.path.dirname(consolelogfile) bb.utils.mkdirhier(log_dir) while True: try: event = eventHandler.waitEvent(0.25) if first: first = False # TODO don't use log output to determine when bitbake has started # # this is the line localhostbecontroller needs to # see in toaster_ui.log which it uses to decide whether # the bitbake server has started... logger.info("ToasterUI waiting for events") if event is None: if main.shutdown > 0: # if shutting down, close any open build log first _close_build_log(build_log) break continue helper.eventHandler(event) # pylint: disable=protected-access # the code will look into the protected variables of the event; no easy way around this # we treat ParseStarted as the first event of toaster-triggered # builds; that way we get the Build Configuration included in the log # and any errors that occur before BuildStarted is fired if isinstance(event, bb.event.ParseStarted): if not (build_log and build_log_file_path): build_log, build_log_file_path = _open_build_log(log_dir) continue if isinstance(event, bb.event.BuildStarted): if not (build_log and build_log_file_path): build_log, build_log_file_path = _open_build_log(log_dir) buildinfohelper.store_started_build(event, build_log_file_path) if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)): buildinfohelper.update_and_store_task(event) logger.info("Logfile for task %s", event.logfile) continue if isinstance(event, bb.build.TaskBase): logger.info(event._message) if isinstance(event, bb.event.LogExecTTY): logger.info(event.msg) continue if isinstance(event, logging.LogRecord): if event.levelno == -1: event.levelno = formatter.ERROR buildinfohelper.store_log_event(event) if event.levelno >= formatter.ERROR: errors = errors + 1 elif event.levelno == formatter.WARNING: warnings = warnings + 1 # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages if event.taskpid != 0 and event.levelno <= formatter.NOTE: continue logger.handle(event) continue if isinstance(event, bb.build.TaskFailed): buildinfohelper.update_and_store_task(event) logfile = event.logfile if logfile and os.path.exists(logfile): bb.error("Logfile of failure stored in: %s" % logfile) continue # these events are unprocessed now, but may be used in the future to log # timing and error informations from the parsing phase in Toaster if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)): continue if isinstance(event, bb.event.ParseProgress): continue if isinstance(event, bb.event.ParseCompleted): continue if isinstance(event, bb.event.CacheLoadStarted): continue if isinstance(event, bb.event.CacheLoadProgress): continue if isinstance(event, bb.event.CacheLoadCompleted): continue if isinstance(event, bb.event.MultipleProviders): logger.info("multiple providers are available for %s%s (%s)", event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates)) logger.info( "consider defining a PREFERRED_PROVIDER entry to match %s", event._item) continue if isinstance(event, bb.event.NoProvider): errors = errors + 1 if event._runtime: r = "R" else: r = "" if event._dependees: text = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)" % ( r, event._item, ", ".join(event._dependees), r) else: text = "Nothing %sPROVIDES '%s'" % (r, event._item) logger.error(text) if event._reasons: for reason in event._reasons: logger.error("%s", reason) text += reason buildinfohelper.store_log_error(text) continue if isinstance(event, bb.event.ConfigParsed): continue if isinstance(event, bb.event.RecipeParsed): continue # end of saved events if isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped)): buildinfohelper.store_started_task(event) continue if isinstance(event, bb.runqueue.runQueueTaskCompleted): buildinfohelper.update_and_store_task(event) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): buildinfohelper.update_and_store_task(event) taskfailures.append(event.taskstring) logger.error("Task %s (%s) failed with exit code '%s'", event.taskid, event.taskstring, event.exitcode) continue if isinstance(event, (bb.runqueue.sceneQueueTaskCompleted, bb.runqueue.sceneQueueTaskFailed)): buildinfohelper.update_and_store_task(event) continue if isinstance(event, (bb.event.TreeDataPreparationStarted, bb.event.TreeDataPreparationCompleted)): continue if isinstance(event, (bb.event.BuildCompleted, bb.command.CommandFailed)): errorcode = 0 if isinstance(event, bb.command.CommandFailed): errors += 1 errorcode = 1 logger.error("Command execution failed: %s", event.error) # turn off logging to the current build log _close_build_log(build_log) # reset ready for next BuildStarted build_log = None # update the build info helper on BuildCompleted, not on CommandXXX buildinfohelper.update_build_information( event, errors, warnings, taskfailures) buildinfohelper.close(errorcode) # mark the log output; controllers may kill the toasterUI after seeing this log logger.info("ToasterUI build done 1, brbe: %s", buildinfohelper.brbe) # we start a new build info logger.debug("ToasterUI prepared for new build") errors = 0 warnings = 0 taskfailures = [] buildinfohelper = BuildInfoHelper(server, build_history_enabled) logger.info("ToasterUI build done 2") continue if isinstance(event, (bb.command.CommandCompleted, bb.command.CommandFailed, bb.command.CommandExit)): errorcode = 0 continue if isinstance(event, bb.event.MetadataEvent): if event.type == "SinglePackageInfo": buildinfohelper.store_build_package_information(event) elif event.type == "LayerInfo": buildinfohelper.store_layer_info(event) elif event.type == "BuildStatsList": buildinfohelper.store_tasks_stats(event) elif event.type == "ImagePkgList": buildinfohelper.store_target_package_data(event) elif event.type == "MissedSstate": buildinfohelper.store_missed_state_tasks(event) elif event.type == "ImageFileSize": buildinfohelper.update_target_image_file(event) elif event.type == "ArtifactFileSize": buildinfohelper.update_artifact_image_file(event) elif event.type == "LicenseManifestPath": buildinfohelper.store_license_manifest_path(event) elif event.type == "SetBRBE": buildinfohelper.brbe = buildinfohelper._get_data_from_event( event) elif event.type == "OSErrorException": logger.error(event) else: logger.error("Unprocessed MetadataEvent %s ", str(event)) continue if isinstance(event, bb.cooker.CookerExit): # shutdown when bitbake server shuts down main.shutdown = 1 continue # ignore if isinstance( event, (bb.event.BuildBase, bb.event.StampUpdate, bb.event.RecipePreFinalise, bb.runqueue.runQueueEvent, bb.runqueue.runQueueExitWait, bb.event.OperationProgress, bb.command.CommandFailed, bb.command.CommandExit, bb.command.CommandCompleted, bb.event.ReachableStamps)): continue if isinstance(event, bb.event.DepTreeGenerated): buildinfohelper.store_dependency_information(event) continue logger.warn("Unknown event: %s", event) return_value += 1 except EnvironmentError as ioerror: # ignore interrupted io if ioerror.args[0] == 4: pass except KeyboardInterrupt: main.shutdown = 1 except Exception as e: # print errors to log import traceback from pprint import pformat exception_data = traceback.format_exc() logger.error("%s\n%s", e, exception_data) _, _, tb = sys.exc_info() if tb is not None: curr = tb while curr is not None: logger.error("Error data dump %s\n%s\n", traceback.format_tb(curr, 1), pformat(curr.tb_frame.f_locals)) curr = curr.tb_next # save them to database, if possible; if it fails, we already logged to console. try: buildinfohelper.store_log_exception("%s\n%s" % (str(e), exception_data)) except Exception as ce: logger.error( "CRITICAL - Failed to to save toaster exception to the database: %s", str(ce)) # make sure we return with an error return_value += 1 if interrupted and return_value == 0: return_value += 1 logger.warn("Return value is %d", return_value) return return_value
def main(server, eventHandler, params, tf=TerminalFilter): includelogs, loglines, consolelogfile = _log_settings_from_server(server) if sys.stdin.isatty() and sys.stdout.isatty(): log_exec_tty = True else: log_exec_tty = False helper = uihelper.BBUIHelper() console = logging.StreamHandler(sys.stdout) errconsole = logging.StreamHandler(sys.stderr) format_str = "%(levelname)s: %(message)s" format = bb.msg.BBLogFormatter(format_str) bb.msg.addDefaultlogFilter(console, bb.msg.BBLogFilterStdOut) bb.msg.addDefaultlogFilter(errconsole, bb.msg.BBLogFilterStdErr) console.setFormatter(format) errconsole.setFormatter(format) logger.addHandler(console) logger.addHandler(errconsole) if params.options.remote_server and params.options.kill_server: server.terminateServer() return if consolelogfile and not params.options.show_environment: bb.utils.mkdirhier(os.path.dirname(consolelogfile)) conlogformat = bb.msg.BBLogFormatter(format_str) consolelog = logging.FileHandler(consolelogfile) bb.msg.addDefaultlogFilter(consolelog) consolelog.setFormatter(conlogformat) logger.addHandler(consolelog) llevel, debug_domains = bb.msg.constructLogOptions() server.runCommand([ "setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list ]) if not params.observe_only: params.updateFromServer(server) cmdline = params.parseActions() if not cmdline: print( "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information." ) return 1 if 'msg' in cmdline and cmdline['msg']: logger.error(cmdline['msg']) return 1 ret, error = server.runCommand(cmdline['action']) if error: logger.error("Command '%s' failed: %s" % (cmdline, error)) return 1 elif ret != True: logger.error("Command '%s' failed: returned %s" % (cmdline, ret)) return 1 parseprogress = None cacheprogress = None main.shutdown = 0 interrupted = False return_value = 0 errors = 0 warnings = 0 taskfailures = [] termfilter = tf(main, helper, console, errconsole, format) atexit.register(termfilter.finish) while True: try: event = eventHandler.waitEvent(0) if event is None: if main.shutdown > 1: break termfilter.updateFooter() event = eventHandler.waitEvent(0.25) if event is None: continue helper.eventHandler(event) if isinstance(event, bb.runqueue.runQueueExitWait): if not main.shutdown: main.shutdown = 1 continue if isinstance(event, bb.event.LogExecTTY): if log_exec_tty: tries = event.retries while tries: print("Trying to run: %s" % event.prog) if os.system(event.prog) == 0: break time.sleep(event.sleep_delay) tries -= 1 if tries: continue logger.warn(event.msg) continue if isinstance(event, logging.LogRecord): if event.levelno >= format.ERROR: errors = errors + 1 return_value = 1 elif event.levelno == format.WARNING: warnings = warnings + 1 # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages if event.taskpid != 0 and event.levelno <= format.NOTE and ( event.levelno < llevel or (event.levelno == format.NOTE and llevel != format.VERBOSE)): continue logger.handle(event) continue if isinstance(event, bb.build.TaskFailedSilent): logger.warn("Logfile for failed setscene task is %s" % event.logfile) continue if isinstance(event, bb.build.TaskFailed): return_value = 1 logfile = event.logfile if logfile and os.path.exists(logfile): termfilter.clearFooter() bb.error("Logfile of failure stored in: %s" % logfile) if includelogs and not event.errprinted: print("Log data follows:") f = open(logfile, "r") lines = [] while True: l = f.readline() if l == '': break l = l.rstrip() if loglines: lines.append(' | %s' % l) if len(lines) > int(loglines): lines.pop(0) else: print('| %s' % l) f.close() if lines: for line in lines: print(line) if isinstance(event, bb.build.TaskBase): logger.info(event._message) continue if isinstance(event, bb.event.ParseStarted): if event.total == 0: continue parseprogress = new_progress("Parsing recipes", event.total).start() continue if isinstance(event, bb.event.ParseProgress): parseprogress.update(event.current) continue if isinstance(event, bb.event.ParseCompleted): if not parseprogress: continue parseprogress.finish() print(( "Parsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors." % (event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors))) continue if isinstance(event, bb.event.CacheLoadStarted): cacheprogress = new_progress("Loading cache", event.total).start() continue if isinstance(event, bb.event.CacheLoadProgress): cacheprogress.update(event.current) continue if isinstance(event, bb.event.CacheLoadCompleted): cacheprogress.finish() print("Loaded %d entries from dependency cache." % event.num_entries) continue if isinstance(event, bb.command.CommandFailed): return_value = event.exitcode if event.error: errors = errors + 1 logger.error("Command execution failed: %s", event.error) main.shutdown = 2 continue if isinstance(event, bb.command.CommandExit): if not return_value: return_value = event.exitcode continue if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)): main.shutdown = 2 continue if isinstance(event, bb.event.MultipleProviders): logger.info("multiple providers are available for %s%s (%s)", event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates)) logger.info( "consider defining a PREFERRED_PROVIDER entry to match %s", event._item) continue if isinstance(event, bb.event.NoProvider): return_value = 1 errors = errors + 1 if event._runtime: r = "R" else: r = "" extra = '' if not event._reasons: if event._close_matches: extra = ". Close matches:\n %s" % '\n '.join( event._close_matches) if event._dependees: logger.error( "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s", r, event._item, ", ".join(event._dependees), r, extra) else: logger.error("Nothing %sPROVIDES '%s'%s", r, event._item, extra) if event._reasons: for reason in event._reasons: logger.error("%s", reason) continue if isinstance(event, bb.runqueue.sceneQueueTaskStarted): logger.info("Running setscene task %d of %d (%s)" % (event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskstring)) continue if isinstance(event, bb.runqueue.runQueueTaskStarted): if event.noexec: tasktype = 'noexec task' else: tasktype = 'task' logger.info( "Running %s %s of %s (ID: %s, %s)", tasktype, event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskid, event.taskstring) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): taskfailures.append(event.taskstring) logger.error("Task %s (%s) failed with exit code '%s'", event.taskid, event.taskstring, event.exitcode) continue if isinstance(event, bb.runqueue.sceneQueueTaskFailed): logger.warn( "Setscene task %s (%s) failed with exit code '%s' - real task will be run instead", event.taskid, event.taskstring, event.exitcode) continue if isinstance(event, bb.event.DepTreeGenerated): continue # ignore if isinstance( event, (bb.event.BuildBase, bb.event.MetadataEvent, bb.event.StampUpdate, bb.event.ConfigParsed, bb.event.RecipeParsed, bb.event.RecipePreFinalise, bb.runqueue.runQueueEvent, bb.event.OperationStarted, bb.event.OperationCompleted, bb.event.OperationProgress, bb.event.DiskFull)): continue logger.error("Unknown event: %s", event) except EnvironmentError as ioerror: termfilter.clearFooter() # ignore interrupted io if ioerror.args[0] == 4: pass except KeyboardInterrupt: termfilter.clearFooter() if params.observe_only: print("\nKeyboard Interrupt, exiting observer...") main.shutdown = 2 if not params.observe_only and main.shutdown == 1: print("\nSecond Keyboard Interrupt, stopping...\n") _, error = server.runCommand(["stateForceShutdown"]) if error: logger.error("Unable to cleanly stop: %s" % error) if not params.observe_only and main.shutdown == 0: print("\nKeyboard Interrupt, closing down...\n") interrupted = True _, error = server.runCommand(["stateShutdown"]) if error: logger.error("Unable to cleanly shutdown: %s" % error) main.shutdown = main.shutdown + 1 pass summary = "" if taskfailures: summary += pluralise("\nSummary: %s task failed:", "\nSummary: %s tasks failed:", len(taskfailures)) for failure in taskfailures: summary += "\n %s" % failure if warnings: summary += pluralise( "\nSummary: There was %s WARNING message shown.", "\nSummary: There were %s WARNING messages shown.", warnings) if return_value and errors: summary += pluralise( "\nSummary: There was %s ERROR message shown, returning a non-zero exit code.", "\nSummary: There were %s ERROR messages shown, returning a non-zero exit code.", errors) if summary: print(summary) if interrupted: print("Execution was interrupted, returning a non-zero exit code.") if return_value == 0: return_value = 1 return return_value
def main(self, stdscr, server, eventHandler, params): #-------------------------------------------------------------------------# height, width = stdscr.getmaxyx() # for now split it like that: # MAIN_y + THREAD_y = 2/3 screen at the top # MAIN_x = 2/3 left, THREAD_y = 1/3 right # CLI_y = 1/3 of screen at the bottom # CLI_x = full main_left = 0 main_top = 0 main_height = (height // 3 * 2) main_width = (width // 3) * 2 clo_left = main_left clo_top = main_top + main_height clo_height = height - main_height - main_top - 1 clo_width = width cli_left = main_left cli_top = clo_top + clo_height cli_height = 1 cli_width = width thread_left = main_left + main_width thread_top = main_top thread_height = main_height thread_width = width - main_width #tw = self.TitleWindow( 0, 0, width, main_top ) mw = self.MainWindow(main_left, main_top, main_width, main_height) taw = self.ThreadActivityWindow(thread_left, thread_top, thread_width, thread_height) clo = self.ShellOutputWindow(clo_left, clo_top, clo_width, clo_height) cli = self.ShellInputWindow(cli_left, cli_top, cli_width, cli_height) cli.setText(0, 0, "BB>") mw.setStatus("Idle") helper = uihelper.BBUIHelper() shutdown = 0 try: params.updateFromServer(server) cmdline = params.parseActions() if not cmdline: print( "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information." ) return 1 if 'msg' in cmdline and cmdline['msg']: logger.error(cmdline['msg']) return 1 cmdline = cmdline['action'] ret, error = server.runCommand(cmdline) if error: print("Error running command '%s': %s" % (cmdline, error)) return elif not ret: print("Couldn't get default commandlind! %s" % ret) return except xmlrpc.client.Fault as x: print("XMLRPC Fault getting commandline:\n %s" % x) return exitflag = False while not exitflag: try: event = eventHandler.waitEvent(0.25) if not event: continue helper.eventHandler(event) if isinstance(event, bb.build.TaskBase): mw.appendText("NOTE: %s\n" % event._message) if isinstance(event, logging.LogRecord): mw.appendText( logging.getLevelName(event.levelno) + ': ' + event.getMessage() + '\n') if isinstance(event, bb.event.CacheLoadStarted): self.parse_total = event.total if isinstance(event, bb.event.CacheLoadProgress): x = event.current y = self.parse_total mw.setStatus("Loading Cache: %s [%2d %%]" % (next(parsespin), x * 100 / y)) if isinstance(event, bb.event.CacheLoadCompleted): mw.setStatus("Idle") mw.appendText( "Loaded %d entries from dependency cache.\n" % (event.num_entries)) if isinstance(event, bb.event.ParseStarted): self.parse_total = event.total if isinstance(event, bb.event.ParseProgress): x = event.current y = self.parse_total mw.setStatus("Parsing Recipes: %s [%2d %%]" % (next(parsespin), x * 100 / y)) if isinstance(event, bb.event.ParseCompleted): mw.setStatus("Idle") mw.appendText( "Parsing finished. %d cached, %d parsed, %d skipped, %d masked.\n" % (event.cached, event.parsed, event.skipped, event.masked)) # if isinstance(event, bb.build.TaskFailed): # if event.logfile: # if data.getVar("BBINCLUDELOGS", d): # bb.error("log data follows (%s)" % logfile) # number_of_lines = data.getVar("BBINCLUDELOGS_LINES", d) # if number_of_lines: # subprocess.check_call('tail -n%s %s' % (number_of_lines, logfile), shell=True) # else: # f = open(logfile, "r") # while True: # l = f.readline() # if l == '': # break # l = l.rstrip() # print '| %s' % l # f.close() # else: # bb.error("see log in %s" % logfile) if isinstance(event, bb.command.CommandCompleted): # stop so the user can see the result of the build, but # also allow them to now exit with a single ^C shutdown = 2 if isinstance(event, bb.command.CommandFailed): mw.appendText(str(event)) time.sleep(2) exitflag = True if isinstance(event, bb.command.CommandExit): exitflag = True if isinstance(event, bb.cooker.CookerExit): exitflag = True if isinstance(event, bb.event.LogExecTTY): mw.appendText('WARN: ' + event.msg + '\n') if helper.needUpdate: activetasks, failedtasks = helper.getTasks() taw.erase() taw.setText(0, 0, "") if activetasks: taw.appendText("Active Tasks:\n") for task in activetasks.values(): taw.appendText(task["title"] + '\n') if failedtasks: taw.appendText("Failed Tasks:\n") for task in failedtasks: taw.appendText(task["title"] + '\n') curses.doupdate() except EnvironmentError as ioerror: # ignore interrupted io if ioerror.args[0] == 4: pass except KeyboardInterrupt: if shutdown == 2: mw.appendText("Third Keyboard Interrupt, exit.\n") exitflag = True if shutdown == 1: mw.appendText("Second Keyboard Interrupt, stopping...\n") _, error = server.runCommand(["stateForceShutdown"]) if error: print("Unable to cleanly stop: %s" % error) if shutdown == 0: mw.appendText("Keyboard Interrupt, closing down...\n") _, error = server.runCommand(["stateShutdown"]) if error: print("Unable to cleanly shutdown: %s" % error) shutdown = shutdown + 1 pass
def main(server, eventHandler, params): # set to a logging.FileHandler instance when a build starts; # see _open_build_log() build_log = None # set to the log path when a build starts build_log_file_path = None helper = uihelper.BBUIHelper() # TODO don't use log output to determine when bitbake has started # # WARNING: this log handler cannot be removed, as localhostbecontroller # relies on output in the toaster_ui.log file to determine whether # the bitbake server has started, which only happens if # this logger is setup here (see the TODO in the loop below) console = logging.StreamHandler(sys.stdout) format_str = "%(levelname)s: %(message)s" formatter = bb.msg.BBLogFormatter(format_str) bb.msg.addDefaultlogFilter(console) console.setFormatter(formatter) logger.addHandler(console) logger.setLevel(logging.INFO) llevel, debug_domains = bb.msg.constructLogOptions() result, error = server.runCommand([ "setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list ]) if not result or error: logger.error("can't set event mask: %s", error) return 1 # verify and warn build_history_enabled = True inheritlist, _ = server.runCommand(["getVariable", "INHERIT"]) if not "buildhistory" in inheritlist.split(" "): logger.warning( "buildhistory is not enabled. Please enable INHERIT += \"buildhistory\" to see image details." ) build_history_enabled = False if not params.observe_only: params.updateFromServer(server) params.updateToServer(server, os.environ.copy()) cmdline = params.parseActions() if not cmdline: print( "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information." ) return 1 if 'msg' in cmdline and cmdline['msg']: logger.error(cmdline['msg']) return 1 ret, error = server.runCommand(cmdline['action']) if error: logger.error("Command '%s' failed: %s" % (cmdline, error)) return 1 elif ret != True: logger.error("Command '%s' failed: returned %s" % (cmdline, ret)) return 1 # set to 1 when toasterui needs to shut down main.shutdown = 0 interrupted = False return_value = 0 errors = 0 warnings = 0 taskfailures = [] first = True buildinfohelper = BuildInfoHelper(server, build_history_enabled, os.getenv('TOASTER_BRBE')) # write our own log files into bitbake's log directory; # we're only interested in the path to the parent directory of # this file, as we're writing our own logs into the same directory consolelogfile = _log_settings_from_server(server) log_dir = os.path.dirname(consolelogfile) bb.utils.mkdirhier(log_dir) while True: try: event = eventHandler.waitEvent(0.25) if first: first = False # TODO don't use log output to determine when bitbake has started # # this is the line localhostbecontroller needs to # see in toaster_ui.log which it uses to decide whether # the bitbake server has started... logger.info("ToasterUI waiting for events") if event is None: if main.shutdown > 0: # if shutting down, close any open build log first _close_build_log(build_log) break continue helper.eventHandler(event) # pylint: disable=protected-access # the code will look into the protected variables of the event; no easy way around this if isinstance(event, bb.event.ParseStarted): if not (build_log and build_log_file_path): build_log, build_log_file_path = _open_build_log(log_dir) buildinfohelper.store_started_build() buildinfohelper.save_build_log_file_path(build_log_file_path) buildinfohelper.set_recipes_to_parse(event.total) continue # create a build object in buildinfohelper from either BuildInit # (if available) or BuildStarted (for jethro and previous versions) if isinstance(event, (bb.event.BuildStarted, bb.event.BuildInit)): if not (build_log and build_log_file_path): build_log, build_log_file_path = _open_build_log(log_dir) buildinfohelper.save_build_targets(event) buildinfohelper.save_build_log_file_path(build_log_file_path) # get additional data from BuildStarted if isinstance(event, bb.event.BuildStarted): buildinfohelper.save_build_layers_and_variables() continue if isinstance(event, bb.event.ParseProgress): buildinfohelper.set_recipes_parsed(event.current) continue if isinstance(event, bb.event.ParseCompleted): buildinfohelper.set_recipes_parsed(event.total) continue if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)): buildinfohelper.update_and_store_task(event) logger.info("Logfile for task %s", event.logfile) continue if isinstance(event, bb.build.TaskBase): logger.info(event._message) if isinstance(event, bb.event.LogExecTTY): logger.info(event.msg) continue if isinstance(event, logging.LogRecord): if event.levelno == -1: event.levelno = formatter.ERROR buildinfohelper.store_log_event(event) if event.levelno >= formatter.ERROR: errors = errors + 1 elif event.levelno == formatter.WARNING: warnings = warnings + 1 # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages if event.taskpid != 0 and event.levelno <= formatter.NOTE: continue logger.handle(event) continue if isinstance(event, bb.build.TaskFailed): buildinfohelper.update_and_store_task(event) logfile = event.logfile if logfile and os.path.exists(logfile): bb.error("Logfile of failure stored in: %s" % logfile) continue # these events are unprocessed now, but may be used in the future to log # timing and error informations from the parsing phase in Toaster if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)): continue if isinstance(event, bb.event.CacheLoadStarted): continue if isinstance(event, bb.event.CacheLoadProgress): continue if isinstance(event, bb.event.CacheLoadCompleted): continue if isinstance(event, bb.event.MultipleProviders): logger.info("multiple providers are available for %s%s (%s)", event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates)) logger.info( "consider defining a PREFERRED_PROVIDER entry to match %s", event._item) continue if isinstance(event, bb.event.NoProvider): errors = errors + 1 if event._runtime: r = "R" else: r = "" if event._dependees: text = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)" % ( r, event._item, ", ".join(event._dependees), r) else: text = "Nothing %sPROVIDES '%s'" % (r, event._item) logger.error(text) if event._reasons: for reason in event._reasons: logger.error("%s", reason) text += reason buildinfohelper.store_log_error(text) continue if isinstance(event, bb.event.ConfigParsed): continue if isinstance(event, bb.event.RecipeParsed): continue # end of saved events if isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped)): buildinfohelper.store_started_task(event) continue if isinstance(event, bb.runqueue.runQueueTaskCompleted): buildinfohelper.update_and_store_task(event) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): buildinfohelper.update_and_store_task(event) taskfailures.append(event.taskstring) logger.error("Task (%s) failed with exit code '%s'", event.taskstring, event.exitcode) continue if isinstance(event, (bb.runqueue.sceneQueueTaskCompleted, bb.runqueue.sceneQueueTaskFailed)): buildinfohelper.update_and_store_task(event) continue if isinstance(event, (bb.event.TreeDataPreparationStarted, bb.event.TreeDataPreparationCompleted)): continue if isinstance(event, (bb.event.BuildCompleted, bb.command.CommandFailed)): errorcode = 0 if isinstance(event, bb.command.CommandFailed): errors += 1 errorcode = 1 logger.error("Command execution failed: %s", event.error) elif isinstance(event, bb.event.BuildCompleted): buildinfohelper.scan_image_artifacts() buildinfohelper.clone_required_sdk_artifacts() # turn off logging to the current build log _close_build_log(build_log) # reset ready for next BuildStarted build_log = None # update the build info helper on BuildCompleted, not on CommandXXX buildinfohelper.update_build_information( event, errors, warnings, taskfailures) brbe = buildinfohelper.brbe buildinfohelper.close(errorcode) # we start a new build info if params.observe_only: logger.debug("ToasterUI prepared for new build") errors = 0 warnings = 0 taskfailures = [] buildinfohelper = BuildInfoHelper(server, build_history_enabled) else: main.shutdown = 1 logger.info("ToasterUI build done, brbe: %s", brbe) continue if isinstance(event, (bb.command.CommandCompleted, bb.command.CommandFailed, bb.command.CommandExit)): if params.observe_only: errorcode = 0 else: main.shutdown = 1 continue if isinstance(event, bb.event.MetadataEvent): if event.type == "SinglePackageInfo": buildinfohelper.store_build_package_information(event) elif event.type == "LayerInfo": buildinfohelper.store_layer_info(event) elif event.type == "BuildStatsList": buildinfohelper.store_tasks_stats(event) elif event.type == "ImagePkgList": buildinfohelper.store_target_package_data(event) elif event.type == "MissedSstate": buildinfohelper.store_missed_state_tasks(event) elif event.type == "SDKArtifactInfo": buildinfohelper.scan_sdk_artifacts(event) elif event.type == "SetBRBE": buildinfohelper.brbe = buildinfohelper._get_data_from_event( event) elif event.type == "TaskArtifacts": # not implemented yet # see https://bugzilla.yoctoproject.org/show_bug.cgi?id=10283 for details pass elif event.type == "OSErrorException": logger.error(event) else: logger.error("Unprocessed MetadataEvent %s", event.type) continue if isinstance(event, bb.cooker.CookerExit): # shutdown when bitbake server shuts down main.shutdown = 1 continue if isinstance(event, bb.event.DepTreeGenerated): buildinfohelper.store_dependency_information(event) continue logger.warning("Unknown event: %s", event) return_value += 1 except EnvironmentError as ioerror: logger.warning("EnvironmentError: %s" % ioerror) # ignore interrupted io system calls if ioerror.args[0] == 4: # errno 4 is EINTR logger.warning("Skipped EINTR: %s" % ioerror) else: raise except KeyboardInterrupt: if params.observe_only: print("\nKeyboard Interrupt, exiting observer...") main.shutdown = 2 if not params.observe_only and main.shutdown == 1: print("\nSecond Keyboard Interrupt, stopping...\n") _, error = server.runCommand(["stateForceShutdown"]) if error: logger.error("Unable to cleanly stop: %s" % error) if not params.observe_only and main.shutdown == 0: print("\nKeyboard Interrupt, closing down...\n") interrupted = True _, error = server.runCommand(["stateShutdown"]) if error: logger.error("Unable to cleanly shutdown: %s" % error) buildinfohelper.cancel_cli_build() main.shutdown = main.shutdown + 1 except Exception as e: # print errors to log import traceback from pprint import pformat exception_data = traceback.format_exc() logger.error("%s\n%s", e, exception_data) # save them to database, if possible; if it fails, we already logged to console. try: buildinfohelper.store_log_exception("%s\n%s" % (str(e), exception_data)) except Exception as ce: logger.error( "CRITICAL - Failed to to save toaster exception to the database: %s", str(ce)) # make sure we return with an error return_value += 1 if interrupted and return_value == 0: return_value += 1 logger.warning("Return value is %d", return_value) return return_value
def main(server, eventHandler, params): includelogs, loglines = _log_settings_from_server(server) # verify and warn build_history_enabled = True inheritlist, error = server.runCommand(["getVariable", "INHERIT"]) if not "buildhistory" in inheritlist.split(" "): logger.warn( "buildhistory is not enabled. Please enable INHERIT += \"buildhistory\" to see image details." ) build_history_enabled = False helper = uihelper.BBUIHelper() console = logging.StreamHandler(sys.stdout) format_str = "%(levelname)s: %(message)s" format = bb.msg.BBLogFormatter(format_str) bb.msg.addDefaultlogFilter(console) console.setFormatter(format) logger.addHandler(console) if not params.observe_only: logger.error("ToasterUI can only work in observer mode") return main.shutdown = 0 interrupted = False return_value = 0 errors = 0 warnings = 0 taskfailures = [] buildinfohelper = BuildInfoHelper(server, build_history_enabled) while True: try: event = eventHandler.waitEvent(0.25) if event is None: if main.shutdown > 0: break continue helper.eventHandler(event) if isinstance(event, bb.event.BuildStarted): buildinfohelper.store_started_build(event) if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)): buildinfohelper.update_and_store_task(event) continue if isinstance(event, bb.event.LogExecTTY): logger.warn(event.msg) continue if isinstance(event, logging.LogRecord): buildinfohelper.store_log_event(event) if event.levelno >= format.ERROR: errors = errors + 1 return_value = 1 elif event.levelno == format.WARNING: warnings = warnings + 1 # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages if event.taskpid != 0 and event.levelno <= format.NOTE: continue logger.handle(event) continue if isinstance(event, bb.build.TaskFailed): buildinfohelper.update_and_store_task(event) return_value = 1 logfile = event.logfile if logfile and os.path.exists(logfile): bb.error("Logfile of failure stored in: %s" % logfile) continue # these events are unprocessed now, but may be used in the future to log # timing and error informations from the parsing phase in Toaster if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)): continue if isinstance(event, bb.event.ParseStarted): continue if isinstance(event, bb.event.ParseProgress): continue if isinstance(event, bb.event.ParseCompleted): continue if isinstance(event, bb.event.CacheLoadStarted): continue if isinstance(event, bb.event.CacheLoadProgress): continue if isinstance(event, bb.event.CacheLoadCompleted): continue if isinstance(event, bb.event.MultipleProviders): continue if isinstance(event, bb.event.NoProvider): return_value = 1 errors = errors + 1 if event._runtime: r = "R" else: r = "" if event._dependees: text = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)" % ( r, event._item, ", ".join(event._dependees), r) else: text = "Nothing %sPROVIDES '%s'" % (r, event._item) logger.error(text) if event._reasons: for reason in event._reasons: logger.error("%s", reason) text += reason buildinfohelper.store_log_error(text) continue if isinstance(event, bb.event.ConfigParsed): continue if isinstance(event, bb.event.RecipeParsed): continue # end of saved events if isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped)): buildinfohelper.store_started_task(event) continue if isinstance(event, bb.runqueue.runQueueTaskCompleted): buildinfohelper.update_and_store_task(event) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): buildinfohelper.update_and_store_task(event) taskfailures.append(event.taskstring) logger.error("Task %s (%s) failed with exit code '%s'", event.taskid, event.taskstring, event.exitcode) continue if isinstance(event, (bb.runqueue.sceneQueueTaskCompleted, bb.runqueue.sceneQueueTaskFailed)): buildinfohelper.update_and_store_task(event) continue if isinstance(event, (bb.event.TreeDataPreparationStarted, bb.event.TreeDataPreparationCompleted)): continue if isinstance(event, (bb.event.BuildCompleted)): continue if isinstance(event, (bb.command.CommandCompleted, bb.command.CommandFailed, bb.command.CommandExit)): if (isinstance(event, bb.command.CommandFailed)): event.levelno = format.ERROR event.msg = event.error event.pathname = "" event.lineno = 0 buildinfohelper.store_log_event(event) errors += 1 buildinfohelper.update_build_information( event, errors, warnings, taskfailures) brbe = server.runCommand(["getVariable", "TOASTER_BRBE"])[0] # we start a new build info if brbe is not None: print "we are under BuildEnvironment management - after the build, we exit" server.terminateServer() else: print "prepared for new build" errors = 0 warnings = 0 taskfailures = [] buildinfohelper = BuildInfoHelper(server, build_history_enabled) continue if isinstance(event, bb.event.MetadataEvent): if event.type == "SinglePackageInfo": buildinfohelper.store_build_package_information(event) elif event.type == "LayerInfo": buildinfohelper.store_layer_info(event) elif event.type == "BuildStatsList": buildinfohelper.store_tasks_stats(event) elif event.type == "ImagePkgList": buildinfohelper.store_target_package_data(event) elif event.type == "MissedSstate": buildinfohelper.store_missed_state_tasks(event) elif event.type == "ImageFileSize": buildinfohelper.update_target_image_file(event) elif event.type == "LicenseManifestPath": buildinfohelper.store_license_manifest_path(event) continue if isinstance(event, bb.cooker.CookerExit): # exit when the server exits break # ignore if isinstance( event, (bb.event.BuildBase, bb.event.StampUpdate, bb.event.RecipePreFinalise, bb.runqueue.runQueueEvent, bb.runqueue.runQueueExitWait, bb.event.OperationProgress, bb.command.CommandFailed, bb.command.CommandExit, bb.command.CommandCompleted)): continue if isinstance(event, bb.event.DepTreeGenerated): buildinfohelper.store_dependency_information(event) continue logger.error("Unknown event: %s", event) except EnvironmentError as ioerror: # ignore interrupted io if ioerror.args[0] == 4: pass except KeyboardInterrupt: main.shutdown = 1 pass except Exception as e: logger.error(e) import traceback traceback.print_exc() pass if interrupted: if return_value == 0: return_value = 1 return return_value
def main(server, eventHandler): # Get values of variables which control our output includelogs = server.runCommand(["getVariable", "BBINCLUDELOGS"]) loglines = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) consolelogfile = server.runCommand(["getVariable", "BB_CONSOLELOG"]) helper = uihelper.BBUIHelper() console = logging.StreamHandler(sys.stdout) format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s") bb.msg.addDefaultlogFilter(console) console.setFormatter(format) logger.addHandler(console) if consolelogfile: consolelog = logging.FileHandler(consolelogfile) bb.msg.addDefaultlogFilter(consolelog) consolelog.setFormatter(format) logger.addHandler(consolelog) try: cmdline = server.runCommand(["getCmdLineAction"]) if not cmdline: print( "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information." ) return 1 elif not cmdline['action']: print(cmdline['msg']) return 1 ret = server.runCommand(cmdline['action']) if ret != True: print("Couldn't get default commandline! %s" % ret) return 1 except xmlrpclib.Fault as x: print("XMLRPC Fault getting commandline:\n %s" % x) return 1 parseprogress = None cacheprogress = None shutdown = 0 return_value = 0 while True: try: event = eventHandler.waitEvent(0.25) if event is None: continue helper.eventHandler(event) if isinstance(event, bb.runqueue.runQueueExitWait): if not shutdown: shutdown = 1 if shutdown and helper.needUpdate: activetasks, failedtasks = helper.getTasks() if activetasks: print("Waiting for %s active tasks to finish:" % len(activetasks)) for tasknum, task in enumerate(activetasks): print("%s: %s (pid %s)" % (tasknum, activetasks[task]["title"], task)) if isinstance(event, logging.LogRecord): if event.levelno >= format.ERROR: return_value = 1 # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages #if logger.getEffectiveLevel() > format.VERBOSE: if event.taskpid != 0 and event.levelno <= format.NOTE: continue logger.handle(event) continue if isinstance(event, bb.build.TaskFailed): return_value = 1 logfile = event.logfile if logfile and os.path.exists(logfile): print("ERROR: Logfile of failure stored in: %s" % logfile) if 1 or includelogs: print("Log data follows:") f = open(logfile, "r") lines = [] while True: l = f.readline() if l == '': break l = l.rstrip() if loglines: lines.append(' | %s' % l) if len(lines) > int(loglines): lines.pop(0) else: print('| %s' % l) f.close() if lines: for line in lines: print(line) if isinstance(event, bb.build.TaskBase): logger.info(event._message) continue if isinstance(event, bb.event.ParseStarted): if event.total == 0: continue parseprogress = new_progress("Parsing recipes", event.total).start() continue if isinstance(event, bb.event.ParseProgress): parseprogress.update(event.current) continue if isinstance(event, bb.event.ParseCompleted): if not parseprogress: continue parseprogress.finish() print(( "Parsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors." % (event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors))) continue if isinstance(event, bb.event.CacheLoadStarted): cacheprogress = new_progress("Loading cache", event.total).start() continue if isinstance(event, bb.event.CacheLoadProgress): cacheprogress.update(event.current) continue if isinstance(event, bb.event.CacheLoadCompleted): cacheprogress.finish() print("Loaded %d entries from dependency cache." % event.num_entries) continue if isinstance(event, bb.command.CommandCompleted): break if isinstance(event, bb.command.CommandFailed): return_value = event.exitcode logger.error("Command execution failed: %s", event.error) break if isinstance(event, bb.command.CommandExit): if not return_value: return_value = event.exitcode continue if isinstance(event, bb.cooker.CookerExit): break if isinstance(event, bb.event.MultipleProviders): logger.info("multiple providers are available for %s%s (%s)", event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates)) logger.info( "consider defining a PREFERRED_PROVIDER entry to match %s", event._item) continue if isinstance(event, bb.event.NoProvider): return_value = 1 if event._runtime: r = "R" else: r = "" if event._dependees: logger.error( "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)", r, event._item, ", ".join(event._dependees), r) else: logger.error("Nothing %sPROVIDES '%s'", r, event._item) if event._reasons: for reason in event._reasons: logger.error("%s", reason) continue if isinstance(event, bb.runqueue.runQueueTaskStarted): if event.noexec: tasktype = 'noexec task' else: tasktype = 'task' logger.info( "Running %s %s of %s (ID: %s, %s)", tasktype, event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskid, event.taskstring) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): logger.error("Task %s (%s) failed with exit code '%s'", event.taskid, event.taskstring, event.exitcode) continue # ignore if isinstance( event, (bb.event.BuildBase, bb.event.StampUpdate, bb.event.ConfigParsed, bb.event.RecipeParsed, bb.event.RecipePreFinalise, bb.runqueue.runQueueEvent, bb.runqueue.runQueueExitWait)): continue logger.error("Unknown event: %s", event) except EnvironmentError as ioerror: # ignore interrupted io if ioerror.args[0] == 4: pass except KeyboardInterrupt: if shutdown == 2: print("\nThird Keyboard Interrupt, exit.\n") break if shutdown == 1: print("\nSecond Keyboard Interrupt, stopping...\n") server.runCommand(["stateStop"]) if shutdown == 0: print("\nKeyboard Interrupt, closing down...\n") server.runCommand(["stateShutdown"]) shutdown = shutdown + 1 pass return return_value
def main(server, eventHandler, params): helper = uihelper.BBUIHelper() console = logging.StreamHandler(sys.stdout) format_str = "%(levelname)s: %(message)s" format = bb.msg.BBLogFormatter(format_str) bb.msg.addDefaultlogFilter(console) console.setFormatter(format) logger.addHandler(console) includelogs, loglines, consolelogfile = _log_settings_from_server(server) # verify and warn build_history_enabled = True inheritlist, error = server.runCommand(["getVariable", "INHERIT"]) if not "buildhistory" in inheritlist.split(" "): logger.warn( "buildhistory is not enabled. Please enable INHERIT += \"buildhistory\" to see image details." ) build_history_enabled = False if not params.observe_only: logger.error("ToasterUI can only work in observer mode") return 1 main.shutdown = 0 interrupted = False return_value = 0 errors = 0 warnings = 0 taskfailures = [] first = True buildinfohelper = BuildInfoHelper(server, build_history_enabled) if buildinfohelper.brbe is not None and consolelogfile: # if we are under managed mode we have no other UI and we need to write our own file bb.utils.mkdirhier(os.path.dirname(consolelogfile)) conlogformat = bb.msg.BBLogFormatter(format_str) consolelog = logging.FileHandler(consolelogfile) bb.msg.addDefaultlogFilter(consolelog) consolelog.setFormatter(conlogformat) logger.addHandler(consolelog) while True: try: event = eventHandler.waitEvent(0.25) if first: first = False logger.info("ToasterUI waiting for events") if event is None: if main.shutdown > 0: break continue helper.eventHandler(event) if isinstance(event, bb.event.BuildStarted): buildinfohelper.store_started_build(event) if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)): buildinfohelper.update_and_store_task(event) logger.warn("Logfile for task %s" % event.logfile) continue if isinstance(event, bb.build.TaskBase): logger.info(event._message) if isinstance(event, bb.event.LogExecTTY): logger.warn(event.msg) continue if isinstance(event, logging.LogRecord): buildinfohelper.store_log_event(event) if event.levelno >= format.ERROR: errors = errors + 1 elif event.levelno == format.WARNING: warnings = warnings + 1 # For "normal" logging conditions, don't show note logs from tasks # but do show them if the user has changed the default log level to # include verbose/debug messages if event.taskpid != 0 and event.levelno <= format.NOTE: continue logger.handle(event) continue if isinstance(event, bb.build.TaskFailed): buildinfohelper.update_and_store_task(event) logfile = event.logfile if logfile and os.path.exists(logfile): bb.error("Logfile of failure stored in: %s" % logfile) continue # these events are unprocessed now, but may be used in the future to log # timing and error informations from the parsing phase in Toaster if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)): continue if isinstance(event, bb.event.ParseStarted): continue if isinstance(event, bb.event.ParseProgress): continue if isinstance(event, bb.event.ParseCompleted): continue if isinstance(event, bb.event.CacheLoadStarted): continue if isinstance(event, bb.event.CacheLoadProgress): continue if isinstance(event, bb.event.CacheLoadCompleted): continue if isinstance(event, bb.event.MultipleProviders): logger.info("multiple providers are available for %s%s (%s)", event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates)) logger.info( "consider defining a PREFERRED_PROVIDER entry to match %s", event._item) continue if isinstance(event, bb.event.NoProvider): errors = errors + 1 if event._runtime: r = "R" else: r = "" if event._dependees: text = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)" % ( r, event._item, ", ".join(event._dependees), r) else: text = "Nothing %sPROVIDES '%s'" % (r, event._item) logger.error(text) if event._reasons: for reason in event._reasons: logger.error("%s", reason) text += reason buildinfohelper.store_log_error(text) continue if isinstance(event, bb.event.ConfigParsed): continue if isinstance(event, bb.event.RecipeParsed): continue # end of saved events if isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped)): buildinfohelper.store_started_task(event) continue if isinstance(event, bb.runqueue.runQueueTaskCompleted): buildinfohelper.update_and_store_task(event) continue if isinstance(event, bb.runqueue.runQueueTaskFailed): buildinfohelper.update_and_store_task(event) taskfailures.append(event.taskstring) logger.error("Task %s (%s) failed with exit code '%s'", event.taskid, event.taskstring, event.exitcode) continue if isinstance(event, (bb.runqueue.sceneQueueTaskCompleted, bb.runqueue.sceneQueueTaskFailed)): buildinfohelper.update_and_store_task(event) continue if isinstance(event, (bb.event.TreeDataPreparationStarted, bb.event.TreeDataPreparationCompleted)): continue if isinstance(event, (bb.event.BuildCompleted)): # update the build info helper on BuildCompleted, not on CommandXXX buildinfohelper.update_build_information( event, errors, warnings, taskfailures) buildinfohelper.close(errorcode) # mark the log output; controllers may kill the toasterUI after seeing this log logger.info("ToasterUI build done") # we start a new build info if buildinfohelper.brbe is not None: logger.debug( 1, "ToasterUI under BuildEnvironment management - exiting after the build" ) server.terminateServer() else: logger.debug(1, "ToasterUI prepared for new build") errors = 0 warnings = 0 taskfailures = [] buildinfohelper = BuildInfoHelper(server, build_history_enabled) continue if isinstance(event, (bb.command.CommandCompleted, bb.command.CommandFailed, bb.command.CommandExit)): errorcode = 0 if (isinstance(event, bb.command.CommandFailed)): event.levelno = format.ERROR event.msg = "Command Failed " + event.error event.pathname = "" event.lineno = 0 buildinfohelper.store_log_event(event) errors += 1 errorcode = 1 logger.error("Command execution failed: %s", event.error) continue if isinstance(event, bb.event.MetadataEvent): if event.type == "SinglePackageInfo": buildinfohelper.store_build_package_information(event) elif event.type == "LayerInfo": buildinfohelper.store_layer_info(event) elif event.type == "BuildStatsList": buildinfohelper.store_tasks_stats(event) elif event.type == "ImagePkgList": buildinfohelper.store_target_package_data(event) elif event.type == "MissedSstate": buildinfohelper.store_missed_state_tasks(event) elif event.type == "ImageFileSize": buildinfohelper.update_target_image_file(event) elif event.type == "ArtifactFileSize": buildinfohelper.update_artifact_image_file(event) elif event.type == "LicenseManifestPath": buildinfohelper.store_license_manifest_path(event) else: logger.error("Unprocessed MetadataEvent %s " % str(event)) continue if isinstance(event, bb.cooker.CookerExit): # exit when the server exits break # ignore if isinstance( event, (bb.event.BuildBase, bb.event.StampUpdate, bb.event.RecipePreFinalise, bb.runqueue.runQueueEvent, bb.runqueue.runQueueExitWait, bb.event.OperationProgress, bb.command.CommandFailed, bb.command.CommandExit, bb.command.CommandCompleted)): continue if isinstance(event, bb.event.DepTreeGenerated): buildinfohelper.store_dependency_information(event) continue logger.error("Unknown event: %s", event) return_value += 1 except EnvironmentError as ioerror: # ignore interrupted io if ioerror.args[0] == 4: pass except KeyboardInterrupt: main.shutdown = 1 pass except Exception as e: # print errors to log import traceback from pprint import pformat exception_data = traceback.format_exc() logger.error("%s\n%s" % (e, exception_data)) exc_type, exc_value, tb = sys.exc_info() if tb is not None: curr = tb while curr is not None: logger.warn( "Error data dump %s\n%s\n" % (traceback.format_tb( curr, 1), pformat(curr.tb_frame.f_locals))) curr = curr.tb_next # save them to database, if possible; if it fails, we already logged to console. try: buildinfohelper.store_log_exception("%s\n%s" % (str(e), exception_data)) except Exception as ce: logger.error( "CRITICAL - Failed to to save toaster exception to the database: %s" % str(ce)) # make sure we return with an error return_value += 1 pass if interrupted: if return_value == 0: return_value += 1 logger.warn("Return value is %d", return_value) return return_value