1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# BitBake ToasterUI Implementation 3*4882a593Smuzhiyun# based on (No)TTY UI Implementation by Richard Purdie 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# Handling output to TTYs or files (no TTY) 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun# Copyright (C) 2006-2012 Richard Purdie 8*4882a593Smuzhiyun# Copyright (C) 2013 Intel Corporation 9*4882a593Smuzhiyun# 10*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 11*4882a593Smuzhiyun# 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunfrom __future__ import division 14*4882a593Smuzhiyunimport time 15*4882a593Smuzhiyunimport sys 16*4882a593Smuzhiyuntry: 17*4882a593Smuzhiyun import bb 18*4882a593Smuzhiyunexcept RuntimeError as exc: 19*4882a593Smuzhiyun sys.exit(str(exc)) 20*4882a593Smuzhiyun 21*4882a593Smuzhiyunfrom bb.ui import uihelper 22*4882a593Smuzhiyunfrom bb.ui.buildinfohelper import BuildInfoHelper 23*4882a593Smuzhiyun 24*4882a593Smuzhiyunimport bb.msg 25*4882a593Smuzhiyunimport logging 26*4882a593Smuzhiyunimport os 27*4882a593Smuzhiyun 28*4882a593Smuzhiyun# pylint: disable=invalid-name 29*4882a593Smuzhiyun# module properties for UI modules are read by bitbake and the contract should not be broken 30*4882a593Smuzhiyun 31*4882a593Smuzhiyun 32*4882a593SmuzhiyunfeatureSet = [bb.cooker.CookerFeatures.HOB_EXTRA_CACHES, bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING, bb.cooker.CookerFeatures.SEND_SANITYEVENTS] 33*4882a593Smuzhiyun 34*4882a593Smuzhiyunlogger = logging.getLogger("ToasterLogger") 35*4882a593Smuzhiyuninteractive = sys.stdout.isatty() 36*4882a593Smuzhiyun 37*4882a593Smuzhiyundef _log_settings_from_server(server): 38*4882a593Smuzhiyun # Get values of variables which control our output 39*4882a593Smuzhiyun includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"]) 40*4882a593Smuzhiyun if error: 41*4882a593Smuzhiyun logger.error("Unable to get the value of BBINCLUDELOGS variable: %s", error) 42*4882a593Smuzhiyun raise BaseException(error) 43*4882a593Smuzhiyun loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) 44*4882a593Smuzhiyun if error: 45*4882a593Smuzhiyun logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s", error) 46*4882a593Smuzhiyun raise BaseException(error) 47*4882a593Smuzhiyun consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"]) 48*4882a593Smuzhiyun if error: 49*4882a593Smuzhiyun logger.error("Unable to get the value of BB_CONSOLELOG variable: %s", error) 50*4882a593Smuzhiyun raise BaseException(error) 51*4882a593Smuzhiyun return consolelogfile 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun# create a log file for a single build and direct the logger at it; 54*4882a593Smuzhiyun# log file name is timestamped to the millisecond (depending 55*4882a593Smuzhiyun# on system clock accuracy) to ensure it doesn't overlap with 56*4882a593Smuzhiyun# other log file names 57*4882a593Smuzhiyun# 58*4882a593Smuzhiyun# returns (log file, path to log file) for a build 59*4882a593Smuzhiyundef _open_build_log(log_dir): 60*4882a593Smuzhiyun format_str = "%(levelname)s: %(message)s" 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun now = time.time() 63*4882a593Smuzhiyun now_ms = int((now - int(now)) * 1000) 64*4882a593Smuzhiyun time_str = time.strftime('build_%Y%m%d_%H%M%S', time.localtime(now)) 65*4882a593Smuzhiyun log_file_name = time_str + ('.%d.log' % now_ms) 66*4882a593Smuzhiyun build_log_file_path = os.path.join(log_dir, log_file_name) 67*4882a593Smuzhiyun 68*4882a593Smuzhiyun build_log = logging.FileHandler(build_log_file_path) 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun logformat = bb.msg.BBLogFormatter(format_str) 71*4882a593Smuzhiyun build_log.setFormatter(logformat) 72*4882a593Smuzhiyun 73*4882a593Smuzhiyun bb.msg.addDefaultlogFilter(build_log) 74*4882a593Smuzhiyun logger.addHandler(build_log) 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun return (build_log, build_log_file_path) 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun# stop logging to the build log if it exists 79*4882a593Smuzhiyundef _close_build_log(build_log): 80*4882a593Smuzhiyun if build_log: 81*4882a593Smuzhiyun build_log.flush() 82*4882a593Smuzhiyun build_log.close() 83*4882a593Smuzhiyun logger.removeHandler(build_log) 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun_evt_list = [ 86*4882a593Smuzhiyun "bb.build.TaskBase", 87*4882a593Smuzhiyun "bb.build.TaskFailed", 88*4882a593Smuzhiyun "bb.build.TaskFailedSilent", 89*4882a593Smuzhiyun "bb.build.TaskStarted", 90*4882a593Smuzhiyun "bb.build.TaskSucceeded", 91*4882a593Smuzhiyun "bb.command.CommandCompleted", 92*4882a593Smuzhiyun "bb.command.CommandExit", 93*4882a593Smuzhiyun "bb.command.CommandFailed", 94*4882a593Smuzhiyun "bb.cooker.CookerExit", 95*4882a593Smuzhiyun "bb.event.BuildInit", 96*4882a593Smuzhiyun "bb.event.BuildCompleted", 97*4882a593Smuzhiyun "bb.event.BuildStarted", 98*4882a593Smuzhiyun "bb.event.CacheLoadCompleted", 99*4882a593Smuzhiyun "bb.event.CacheLoadProgress", 100*4882a593Smuzhiyun "bb.event.CacheLoadStarted", 101*4882a593Smuzhiyun "bb.event.ConfigParsed", 102*4882a593Smuzhiyun "bb.event.DepTreeGenerated", 103*4882a593Smuzhiyun "bb.event.LogExecTTY", 104*4882a593Smuzhiyun "bb.event.MetadataEvent", 105*4882a593Smuzhiyun "bb.event.MultipleProviders", 106*4882a593Smuzhiyun "bb.event.NoProvider", 107*4882a593Smuzhiyun "bb.event.ParseCompleted", 108*4882a593Smuzhiyun "bb.event.ParseProgress", 109*4882a593Smuzhiyun "bb.event.ParseStarted", 110*4882a593Smuzhiyun "bb.event.RecipeParsed", 111*4882a593Smuzhiyun "bb.event.SanityCheck", 112*4882a593Smuzhiyun "bb.event.SanityCheckPassed", 113*4882a593Smuzhiyun "bb.event.TreeDataPreparationCompleted", 114*4882a593Smuzhiyun "bb.event.TreeDataPreparationStarted", 115*4882a593Smuzhiyun "bb.runqueue.runQueueTaskCompleted", 116*4882a593Smuzhiyun "bb.runqueue.runQueueTaskFailed", 117*4882a593Smuzhiyun "bb.runqueue.runQueueTaskSkipped", 118*4882a593Smuzhiyun "bb.runqueue.runQueueTaskStarted", 119*4882a593Smuzhiyun "bb.runqueue.sceneQueueTaskCompleted", 120*4882a593Smuzhiyun "bb.runqueue.sceneQueueTaskFailed", 121*4882a593Smuzhiyun "bb.runqueue.sceneQueueTaskStarted", 122*4882a593Smuzhiyun "logging.LogRecord"] 123*4882a593Smuzhiyun 124*4882a593Smuzhiyundef main(server, eventHandler, params): 125*4882a593Smuzhiyun # set to a logging.FileHandler instance when a build starts; 126*4882a593Smuzhiyun # see _open_build_log() 127*4882a593Smuzhiyun build_log = None 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun # set to the log path when a build starts 130*4882a593Smuzhiyun build_log_file_path = None 131*4882a593Smuzhiyun 132*4882a593Smuzhiyun helper = uihelper.BBUIHelper() 133*4882a593Smuzhiyun 134*4882a593Smuzhiyun if not params.observe_only: 135*4882a593Smuzhiyun params.updateToServer(server, os.environ.copy()) 136*4882a593Smuzhiyun params.updateFromServer(server) 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun # TODO don't use log output to determine when bitbake has started 139*4882a593Smuzhiyun # 140*4882a593Smuzhiyun # WARNING: this log handler cannot be removed, as localhostbecontroller 141*4882a593Smuzhiyun # relies on output in the toaster_ui.log file to determine whether 142*4882a593Smuzhiyun # the bitbake server has started, which only happens if 143*4882a593Smuzhiyun # this logger is setup here (see the TODO in the loop below) 144*4882a593Smuzhiyun console = logging.StreamHandler(sys.stdout) 145*4882a593Smuzhiyun format_str = "%(levelname)s: %(message)s" 146*4882a593Smuzhiyun formatter = bb.msg.BBLogFormatter(format_str) 147*4882a593Smuzhiyun bb.msg.addDefaultlogFilter(console) 148*4882a593Smuzhiyun console.setFormatter(formatter) 149*4882a593Smuzhiyun logger.addHandler(console) 150*4882a593Smuzhiyun logger.setLevel(logging.INFO) 151*4882a593Smuzhiyun llevel, debug_domains = bb.msg.constructLogOptions() 152*4882a593Smuzhiyun result, error = server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list]) 153*4882a593Smuzhiyun if not result or error: 154*4882a593Smuzhiyun logger.error("can't set event mask: %s", error) 155*4882a593Smuzhiyun return 1 156*4882a593Smuzhiyun 157*4882a593Smuzhiyun # verify and warn 158*4882a593Smuzhiyun build_history_enabled = True 159*4882a593Smuzhiyun inheritlist, _ = server.runCommand(["getVariable", "INHERIT"]) 160*4882a593Smuzhiyun 161*4882a593Smuzhiyun if not "buildhistory" in inheritlist.split(" "): 162*4882a593Smuzhiyun logger.warning("buildhistory is not enabled. Please enable INHERIT += \"buildhistory\" to see image details.") 163*4882a593Smuzhiyun build_history_enabled = False 164*4882a593Smuzhiyun 165*4882a593Smuzhiyun if not "buildstats" in inheritlist.split(" "): 166*4882a593Smuzhiyun logger.warning("buildstats is not enabled. Please enable INHERIT += \"buildstats\" to generate build statistics.") 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun if not params.observe_only: 169*4882a593Smuzhiyun cmdline = params.parseActions() 170*4882a593Smuzhiyun if not cmdline: 171*4882a593Smuzhiyun print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.") 172*4882a593Smuzhiyun return 1 173*4882a593Smuzhiyun if 'msg' in cmdline and cmdline['msg']: 174*4882a593Smuzhiyun logger.error(cmdline['msg']) 175*4882a593Smuzhiyun return 1 176*4882a593Smuzhiyun 177*4882a593Smuzhiyun ret, error = server.runCommand(cmdline['action']) 178*4882a593Smuzhiyun if error: 179*4882a593Smuzhiyun logger.error("Command '%s' failed: %s" % (cmdline, error)) 180*4882a593Smuzhiyun return 1 181*4882a593Smuzhiyun elif not ret: 182*4882a593Smuzhiyun logger.error("Command '%s' failed: returned %s" % (cmdline, ret)) 183*4882a593Smuzhiyun return 1 184*4882a593Smuzhiyun 185*4882a593Smuzhiyun # set to 1 when toasterui needs to shut down 186*4882a593Smuzhiyun main.shutdown = 0 187*4882a593Smuzhiyun 188*4882a593Smuzhiyun interrupted = False 189*4882a593Smuzhiyun return_value = 0 190*4882a593Smuzhiyun errors = 0 191*4882a593Smuzhiyun warnings = 0 192*4882a593Smuzhiyun taskfailures = [] 193*4882a593Smuzhiyun first = True 194*4882a593Smuzhiyun 195*4882a593Smuzhiyun buildinfohelper = BuildInfoHelper(server, build_history_enabled, 196*4882a593Smuzhiyun os.getenv('TOASTER_BRBE')) 197*4882a593Smuzhiyun 198*4882a593Smuzhiyun # write our own log files into bitbake's log directory; 199*4882a593Smuzhiyun # we're only interested in the path to the parent directory of 200*4882a593Smuzhiyun # this file, as we're writing our own logs into the same directory 201*4882a593Smuzhiyun consolelogfile = _log_settings_from_server(server) 202*4882a593Smuzhiyun log_dir = os.path.dirname(consolelogfile) 203*4882a593Smuzhiyun bb.utils.mkdirhier(log_dir) 204*4882a593Smuzhiyun 205*4882a593Smuzhiyun while True: 206*4882a593Smuzhiyun try: 207*4882a593Smuzhiyun event = eventHandler.waitEvent(0.25) 208*4882a593Smuzhiyun if first: 209*4882a593Smuzhiyun first = False 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun # TODO don't use log output to determine when bitbake has started 212*4882a593Smuzhiyun # 213*4882a593Smuzhiyun # this is the line localhostbecontroller needs to 214*4882a593Smuzhiyun # see in toaster_ui.log which it uses to decide whether 215*4882a593Smuzhiyun # the bitbake server has started... 216*4882a593Smuzhiyun logger.info("ToasterUI waiting for events") 217*4882a593Smuzhiyun 218*4882a593Smuzhiyun if event is None: 219*4882a593Smuzhiyun if main.shutdown > 0: 220*4882a593Smuzhiyun # if shutting down, close any open build log first 221*4882a593Smuzhiyun _close_build_log(build_log) 222*4882a593Smuzhiyun 223*4882a593Smuzhiyun break 224*4882a593Smuzhiyun continue 225*4882a593Smuzhiyun 226*4882a593Smuzhiyun helper.eventHandler(event) 227*4882a593Smuzhiyun 228*4882a593Smuzhiyun # pylint: disable=protected-access 229*4882a593Smuzhiyun # the code will look into the protected variables of the event; no easy way around this 230*4882a593Smuzhiyun 231*4882a593Smuzhiyun if isinstance(event, bb.event.HeartbeatEvent): 232*4882a593Smuzhiyun continue 233*4882a593Smuzhiyun 234*4882a593Smuzhiyun if isinstance(event, bb.event.ParseStarted): 235*4882a593Smuzhiyun if not (build_log and build_log_file_path): 236*4882a593Smuzhiyun build_log, build_log_file_path = _open_build_log(log_dir) 237*4882a593Smuzhiyun 238*4882a593Smuzhiyun buildinfohelper.store_started_build() 239*4882a593Smuzhiyun buildinfohelper.save_build_log_file_path(build_log_file_path) 240*4882a593Smuzhiyun buildinfohelper.set_recipes_to_parse(event.total) 241*4882a593Smuzhiyun continue 242*4882a593Smuzhiyun 243*4882a593Smuzhiyun # create a build object in buildinfohelper from either BuildInit 244*4882a593Smuzhiyun # (if available) or BuildStarted (for jethro and previous versions) 245*4882a593Smuzhiyun if isinstance(event, (bb.event.BuildStarted, bb.event.BuildInit)): 246*4882a593Smuzhiyun if not (build_log and build_log_file_path): 247*4882a593Smuzhiyun build_log, build_log_file_path = _open_build_log(log_dir) 248*4882a593Smuzhiyun 249*4882a593Smuzhiyun buildinfohelper.save_build_targets(event) 250*4882a593Smuzhiyun buildinfohelper.save_build_log_file_path(build_log_file_path) 251*4882a593Smuzhiyun 252*4882a593Smuzhiyun # get additional data from BuildStarted 253*4882a593Smuzhiyun if isinstance(event, bb.event.BuildStarted): 254*4882a593Smuzhiyun buildinfohelper.save_build_layers_and_variables() 255*4882a593Smuzhiyun continue 256*4882a593Smuzhiyun 257*4882a593Smuzhiyun if isinstance(event, bb.event.ParseProgress): 258*4882a593Smuzhiyun buildinfohelper.set_recipes_parsed(event.current) 259*4882a593Smuzhiyun continue 260*4882a593Smuzhiyun 261*4882a593Smuzhiyun if isinstance(event, bb.event.ParseCompleted): 262*4882a593Smuzhiyun buildinfohelper.set_recipes_parsed(event.total) 263*4882a593Smuzhiyun continue 264*4882a593Smuzhiyun 265*4882a593Smuzhiyun if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)): 266*4882a593Smuzhiyun buildinfohelper.update_and_store_task(event) 267*4882a593Smuzhiyun logger.info("Logfile for task %s", event.logfile) 268*4882a593Smuzhiyun continue 269*4882a593Smuzhiyun 270*4882a593Smuzhiyun if isinstance(event, bb.build.TaskBase): 271*4882a593Smuzhiyun logger.info(event._message) 272*4882a593Smuzhiyun 273*4882a593Smuzhiyun if isinstance(event, bb.event.LogExecTTY): 274*4882a593Smuzhiyun logger.info(event.msg) 275*4882a593Smuzhiyun continue 276*4882a593Smuzhiyun 277*4882a593Smuzhiyun if isinstance(event, logging.LogRecord): 278*4882a593Smuzhiyun if event.levelno == -1: 279*4882a593Smuzhiyun event.levelno = formatter.ERROR 280*4882a593Smuzhiyun 281*4882a593Smuzhiyun buildinfohelper.store_log_event(event) 282*4882a593Smuzhiyun 283*4882a593Smuzhiyun if event.levelno >= formatter.ERROR: 284*4882a593Smuzhiyun errors = errors + 1 285*4882a593Smuzhiyun elif event.levelno == formatter.WARNING: 286*4882a593Smuzhiyun warnings = warnings + 1 287*4882a593Smuzhiyun 288*4882a593Smuzhiyun # For "normal" logging conditions, don't show note logs from tasks 289*4882a593Smuzhiyun # but do show them if the user has changed the default log level to 290*4882a593Smuzhiyun # include verbose/debug messages 291*4882a593Smuzhiyun if event.taskpid != 0 and event.levelno <= formatter.NOTE: 292*4882a593Smuzhiyun continue 293*4882a593Smuzhiyun 294*4882a593Smuzhiyun logger.handle(event) 295*4882a593Smuzhiyun continue 296*4882a593Smuzhiyun 297*4882a593Smuzhiyun if isinstance(event, bb.build.TaskFailed): 298*4882a593Smuzhiyun buildinfohelper.update_and_store_task(event) 299*4882a593Smuzhiyun logfile = event.logfile 300*4882a593Smuzhiyun if logfile and os.path.exists(logfile): 301*4882a593Smuzhiyun bb.error("Logfile of failure stored in: %s" % logfile) 302*4882a593Smuzhiyun continue 303*4882a593Smuzhiyun 304*4882a593Smuzhiyun # these events are unprocessed now, but may be used in the future to log 305*4882a593Smuzhiyun # timing and error informations from the parsing phase in Toaster 306*4882a593Smuzhiyun if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)): 307*4882a593Smuzhiyun continue 308*4882a593Smuzhiyun if isinstance(event, bb.event.CacheLoadStarted): 309*4882a593Smuzhiyun continue 310*4882a593Smuzhiyun if isinstance(event, bb.event.CacheLoadProgress): 311*4882a593Smuzhiyun continue 312*4882a593Smuzhiyun if isinstance(event, bb.event.CacheLoadCompleted): 313*4882a593Smuzhiyun continue 314*4882a593Smuzhiyun if isinstance(event, bb.event.MultipleProviders): 315*4882a593Smuzhiyun logger.info(str(event)) 316*4882a593Smuzhiyun continue 317*4882a593Smuzhiyun 318*4882a593Smuzhiyun if isinstance(event, bb.event.NoProvider): 319*4882a593Smuzhiyun errors = errors + 1 320*4882a593Smuzhiyun text = str(event) 321*4882a593Smuzhiyun logger.error(text) 322*4882a593Smuzhiyun buildinfohelper.store_log_error(text) 323*4882a593Smuzhiyun continue 324*4882a593Smuzhiyun 325*4882a593Smuzhiyun if isinstance(event, bb.event.ConfigParsed): 326*4882a593Smuzhiyun continue 327*4882a593Smuzhiyun if isinstance(event, bb.event.RecipeParsed): 328*4882a593Smuzhiyun continue 329*4882a593Smuzhiyun 330*4882a593Smuzhiyun # end of saved events 331*4882a593Smuzhiyun 332*4882a593Smuzhiyun if isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped)): 333*4882a593Smuzhiyun buildinfohelper.store_started_task(event) 334*4882a593Smuzhiyun continue 335*4882a593Smuzhiyun 336*4882a593Smuzhiyun if isinstance(event, bb.runqueue.runQueueTaskCompleted): 337*4882a593Smuzhiyun buildinfohelper.update_and_store_task(event) 338*4882a593Smuzhiyun continue 339*4882a593Smuzhiyun 340*4882a593Smuzhiyun if isinstance(event, bb.runqueue.runQueueTaskFailed): 341*4882a593Smuzhiyun buildinfohelper.update_and_store_task(event) 342*4882a593Smuzhiyun taskfailures.append(event.taskstring) 343*4882a593Smuzhiyun logger.error(str(event)) 344*4882a593Smuzhiyun continue 345*4882a593Smuzhiyun 346*4882a593Smuzhiyun if isinstance(event, (bb.runqueue.sceneQueueTaskCompleted, bb.runqueue.sceneQueueTaskFailed)): 347*4882a593Smuzhiyun buildinfohelper.update_and_store_task(event) 348*4882a593Smuzhiyun continue 349*4882a593Smuzhiyun 350*4882a593Smuzhiyun 351*4882a593Smuzhiyun if isinstance(event, (bb.event.TreeDataPreparationStarted, bb.event.TreeDataPreparationCompleted)): 352*4882a593Smuzhiyun continue 353*4882a593Smuzhiyun 354*4882a593Smuzhiyun if isinstance(event, (bb.event.BuildCompleted, bb.command.CommandFailed)): 355*4882a593Smuzhiyun 356*4882a593Smuzhiyun errorcode = 0 357*4882a593Smuzhiyun if isinstance(event, bb.command.CommandFailed): 358*4882a593Smuzhiyun errors += 1 359*4882a593Smuzhiyun errorcode = 1 360*4882a593Smuzhiyun logger.error(str(event)) 361*4882a593Smuzhiyun elif isinstance(event, bb.event.BuildCompleted): 362*4882a593Smuzhiyun buildinfohelper.scan_image_artifacts() 363*4882a593Smuzhiyun buildinfohelper.clone_required_sdk_artifacts() 364*4882a593Smuzhiyun 365*4882a593Smuzhiyun # turn off logging to the current build log 366*4882a593Smuzhiyun _close_build_log(build_log) 367*4882a593Smuzhiyun 368*4882a593Smuzhiyun # reset ready for next BuildStarted 369*4882a593Smuzhiyun build_log = None 370*4882a593Smuzhiyun 371*4882a593Smuzhiyun # update the build info helper on BuildCompleted, not on CommandXXX 372*4882a593Smuzhiyun buildinfohelper.update_build_information(event, errors, warnings, taskfailures) 373*4882a593Smuzhiyun 374*4882a593Smuzhiyun brbe = buildinfohelper.brbe 375*4882a593Smuzhiyun buildinfohelper.close(errorcode) 376*4882a593Smuzhiyun 377*4882a593Smuzhiyun # we start a new build info 378*4882a593Smuzhiyun if params.observe_only: 379*4882a593Smuzhiyun logger.debug("ToasterUI prepared for new build") 380*4882a593Smuzhiyun errors = 0 381*4882a593Smuzhiyun warnings = 0 382*4882a593Smuzhiyun taskfailures = [] 383*4882a593Smuzhiyun buildinfohelper = BuildInfoHelper(server, build_history_enabled) 384*4882a593Smuzhiyun else: 385*4882a593Smuzhiyun main.shutdown = 1 386*4882a593Smuzhiyun 387*4882a593Smuzhiyun logger.info("ToasterUI build done, brbe: %s", brbe) 388*4882a593Smuzhiyun continue 389*4882a593Smuzhiyun 390*4882a593Smuzhiyun if isinstance(event, (bb.command.CommandCompleted, 391*4882a593Smuzhiyun bb.command.CommandFailed, 392*4882a593Smuzhiyun bb.command.CommandExit)): 393*4882a593Smuzhiyun if params.observe_only: 394*4882a593Smuzhiyun errorcode = 0 395*4882a593Smuzhiyun else: 396*4882a593Smuzhiyun main.shutdown = 1 397*4882a593Smuzhiyun 398*4882a593Smuzhiyun continue 399*4882a593Smuzhiyun 400*4882a593Smuzhiyun if isinstance(event, bb.event.MetadataEvent): 401*4882a593Smuzhiyun if event.type == "SinglePackageInfo": 402*4882a593Smuzhiyun buildinfohelper.store_build_package_information(event) 403*4882a593Smuzhiyun elif event.type == "LayerInfo": 404*4882a593Smuzhiyun buildinfohelper.store_layer_info(event) 405*4882a593Smuzhiyun elif event.type == "BuildStatsList": 406*4882a593Smuzhiyun buildinfohelper.store_tasks_stats(event) 407*4882a593Smuzhiyun elif event.type == "ImagePkgList": 408*4882a593Smuzhiyun buildinfohelper.store_target_package_data(event) 409*4882a593Smuzhiyun elif event.type == "MissedSstate": 410*4882a593Smuzhiyun buildinfohelper.store_missed_state_tasks(event) 411*4882a593Smuzhiyun elif event.type == "SDKArtifactInfo": 412*4882a593Smuzhiyun buildinfohelper.scan_sdk_artifacts(event) 413*4882a593Smuzhiyun elif event.type == "SetBRBE": 414*4882a593Smuzhiyun buildinfohelper.brbe = buildinfohelper._get_data_from_event(event) 415*4882a593Smuzhiyun elif event.type == "TaskArtifacts": 416*4882a593Smuzhiyun buildinfohelper.scan_task_artifacts(event) 417*4882a593Smuzhiyun elif event.type == "OSErrorException": 418*4882a593Smuzhiyun logger.error(event) 419*4882a593Smuzhiyun else: 420*4882a593Smuzhiyun logger.error("Unprocessed MetadataEvent %s", event.type) 421*4882a593Smuzhiyun continue 422*4882a593Smuzhiyun 423*4882a593Smuzhiyun if isinstance(event, bb.cooker.CookerExit): 424*4882a593Smuzhiyun # shutdown when bitbake server shuts down 425*4882a593Smuzhiyun main.shutdown = 1 426*4882a593Smuzhiyun continue 427*4882a593Smuzhiyun 428*4882a593Smuzhiyun if isinstance(event, bb.event.DepTreeGenerated): 429*4882a593Smuzhiyun buildinfohelper.store_dependency_information(event) 430*4882a593Smuzhiyun continue 431*4882a593Smuzhiyun 432*4882a593Smuzhiyun logger.warning("Unknown event: %s", event) 433*4882a593Smuzhiyun return_value += 1 434*4882a593Smuzhiyun 435*4882a593Smuzhiyun except EnvironmentError as ioerror: 436*4882a593Smuzhiyun logger.warning("EnvironmentError: %s" % ioerror) 437*4882a593Smuzhiyun # ignore interrupted io system calls 438*4882a593Smuzhiyun if ioerror.args[0] == 4: # errno 4 is EINTR 439*4882a593Smuzhiyun logger.warning("Skipped EINTR: %s" % ioerror) 440*4882a593Smuzhiyun else: 441*4882a593Smuzhiyun raise 442*4882a593Smuzhiyun except KeyboardInterrupt: 443*4882a593Smuzhiyun if params.observe_only: 444*4882a593Smuzhiyun print("\nKeyboard Interrupt, exiting observer...") 445*4882a593Smuzhiyun main.shutdown = 2 446*4882a593Smuzhiyun if not params.observe_only and main.shutdown == 1: 447*4882a593Smuzhiyun print("\nSecond Keyboard Interrupt, stopping...\n") 448*4882a593Smuzhiyun _, error = server.runCommand(["stateForceShutdown"]) 449*4882a593Smuzhiyun if error: 450*4882a593Smuzhiyun logger.error("Unable to cleanly stop: %s" % error) 451*4882a593Smuzhiyun if not params.observe_only and main.shutdown == 0: 452*4882a593Smuzhiyun print("\nKeyboard Interrupt, closing down...\n") 453*4882a593Smuzhiyun interrupted = True 454*4882a593Smuzhiyun _, error = server.runCommand(["stateShutdown"]) 455*4882a593Smuzhiyun if error: 456*4882a593Smuzhiyun logger.error("Unable to cleanly shutdown: %s" % error) 457*4882a593Smuzhiyun buildinfohelper.cancel_cli_build() 458*4882a593Smuzhiyun main.shutdown = main.shutdown + 1 459*4882a593Smuzhiyun except Exception as e: 460*4882a593Smuzhiyun # print errors to log 461*4882a593Smuzhiyun import traceback 462*4882a593Smuzhiyun from pprint import pformat 463*4882a593Smuzhiyun exception_data = traceback.format_exc() 464*4882a593Smuzhiyun logger.error("%s\n%s" , e, exception_data) 465*4882a593Smuzhiyun 466*4882a593Smuzhiyun # save them to database, if possible; if it fails, we already logged to console. 467*4882a593Smuzhiyun try: 468*4882a593Smuzhiyun buildinfohelper.store_log_exception("%s\n%s" % (str(e), exception_data)) 469*4882a593Smuzhiyun except Exception as ce: 470*4882a593Smuzhiyun logger.error("CRITICAL - Failed to to save toaster exception to the database: %s", str(ce)) 471*4882a593Smuzhiyun 472*4882a593Smuzhiyun # make sure we return with an error 473*4882a593Smuzhiyun return_value += 1 474*4882a593Smuzhiyun 475*4882a593Smuzhiyun if interrupted and return_value == 0: 476*4882a593Smuzhiyun return_value += 1 477*4882a593Smuzhiyun 478*4882a593Smuzhiyun logger.warning("Return value is %d", return_value) 479*4882a593Smuzhiyun return return_value 480