1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# BitBake (No)TTY UI Implementation 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# Handling output to TTYs or files (no TTY) 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun# Copyright (C) 2006-2012 Richard Purdie 7*4882a593Smuzhiyun# 8*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 9*4882a593Smuzhiyun# 10*4882a593Smuzhiyun 11*4882a593Smuzhiyunfrom __future__ import division 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunimport os 14*4882a593Smuzhiyunimport sys 15*4882a593Smuzhiyunimport logging 16*4882a593Smuzhiyunimport progressbar 17*4882a593Smuzhiyunimport signal 18*4882a593Smuzhiyunimport bb.msg 19*4882a593Smuzhiyunimport time 20*4882a593Smuzhiyunimport fcntl 21*4882a593Smuzhiyunimport struct 22*4882a593Smuzhiyunimport copy 23*4882a593Smuzhiyunimport atexit 24*4882a593Smuzhiyunfrom itertools import groupby 25*4882a593Smuzhiyun 26*4882a593Smuzhiyunfrom bb.ui import uihelper 27*4882a593Smuzhiyun 28*4882a593SmuzhiyunfeatureSet = [bb.cooker.CookerFeatures.SEND_SANITYEVENTS, bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING] 29*4882a593Smuzhiyun 30*4882a593Smuzhiyunlogger = logging.getLogger("BitBake") 31*4882a593Smuzhiyuninteractive = sys.stdout.isatty() 32*4882a593Smuzhiyun 33*4882a593Smuzhiyunclass BBProgress(progressbar.ProgressBar): 34*4882a593Smuzhiyun def __init__(self, msg, maxval, widgets=None, extrapos=-1, resize_handler=None): 35*4882a593Smuzhiyun self.msg = msg 36*4882a593Smuzhiyun self.extrapos = extrapos 37*4882a593Smuzhiyun if not widgets: 38*4882a593Smuzhiyun widgets = [': ', progressbar.Percentage(), ' ', progressbar.Bar(), 39*4882a593Smuzhiyun ' ', progressbar.ETA()] 40*4882a593Smuzhiyun self.extrapos = 5 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun if resize_handler: 43*4882a593Smuzhiyun self._resize_default = resize_handler 44*4882a593Smuzhiyun else: 45*4882a593Smuzhiyun self._resize_default = signal.getsignal(signal.SIGWINCH) 46*4882a593Smuzhiyun progressbar.ProgressBar.__init__(self, maxval, [self.msg] + widgets, fd=sys.stdout) 47*4882a593Smuzhiyun 48*4882a593Smuzhiyun def _handle_resize(self, signum=None, frame=None): 49*4882a593Smuzhiyun progressbar.ProgressBar._handle_resize(self, signum, frame) 50*4882a593Smuzhiyun if self._resize_default: 51*4882a593Smuzhiyun self._resize_default(signum, frame) 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun def finish(self): 54*4882a593Smuzhiyun progressbar.ProgressBar.finish(self) 55*4882a593Smuzhiyun if self._resize_default: 56*4882a593Smuzhiyun signal.signal(signal.SIGWINCH, self._resize_default) 57*4882a593Smuzhiyun 58*4882a593Smuzhiyun def setmessage(self, msg): 59*4882a593Smuzhiyun self.msg = msg 60*4882a593Smuzhiyun self.widgets[0] = msg 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun def setextra(self, extra): 63*4882a593Smuzhiyun if self.extrapos > -1: 64*4882a593Smuzhiyun if extra: 65*4882a593Smuzhiyun extrastr = str(extra) 66*4882a593Smuzhiyun if extrastr[0] != ' ': 67*4882a593Smuzhiyun extrastr = ' ' + extrastr 68*4882a593Smuzhiyun else: 69*4882a593Smuzhiyun extrastr = '' 70*4882a593Smuzhiyun self.widgets[self.extrapos] = extrastr 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun def _need_update(self): 73*4882a593Smuzhiyun # We always want the bar to print when update() is called 74*4882a593Smuzhiyun return True 75*4882a593Smuzhiyun 76*4882a593Smuzhiyunclass NonInteractiveProgress(object): 77*4882a593Smuzhiyun fobj = sys.stdout 78*4882a593Smuzhiyun 79*4882a593Smuzhiyun def __init__(self, msg, maxval): 80*4882a593Smuzhiyun self.msg = msg 81*4882a593Smuzhiyun self.maxval = maxval 82*4882a593Smuzhiyun self.finished = False 83*4882a593Smuzhiyun 84*4882a593Smuzhiyun def start(self, update=True): 85*4882a593Smuzhiyun self.fobj.write("%s..." % self.msg) 86*4882a593Smuzhiyun self.fobj.flush() 87*4882a593Smuzhiyun return self 88*4882a593Smuzhiyun 89*4882a593Smuzhiyun def update(self, value): 90*4882a593Smuzhiyun pass 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun def finish(self): 93*4882a593Smuzhiyun if self.finished: 94*4882a593Smuzhiyun return 95*4882a593Smuzhiyun self.fobj.write("done.\n") 96*4882a593Smuzhiyun self.fobj.flush() 97*4882a593Smuzhiyun self.finished = True 98*4882a593Smuzhiyun 99*4882a593Smuzhiyundef new_progress(msg, maxval): 100*4882a593Smuzhiyun if interactive: 101*4882a593Smuzhiyun return BBProgress(msg, maxval) 102*4882a593Smuzhiyun else: 103*4882a593Smuzhiyun return NonInteractiveProgress(msg, maxval) 104*4882a593Smuzhiyun 105*4882a593Smuzhiyundef pluralise(singular, plural, qty): 106*4882a593Smuzhiyun if(qty == 1): 107*4882a593Smuzhiyun return singular % qty 108*4882a593Smuzhiyun else: 109*4882a593Smuzhiyun return plural % qty 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun 112*4882a593Smuzhiyunclass InteractConsoleLogFilter(logging.Filter): 113*4882a593Smuzhiyun def __init__(self, tf): 114*4882a593Smuzhiyun self.tf = tf 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun def filter(self, record): 117*4882a593Smuzhiyun if record.levelno == bb.msg.BBLogFormatter.NOTE and (record.msg.startswith("Running") or record.msg.startswith("recipe ")): 118*4882a593Smuzhiyun return False 119*4882a593Smuzhiyun self.tf.clearFooter() 120*4882a593Smuzhiyun return True 121*4882a593Smuzhiyun 122*4882a593Smuzhiyunclass TerminalFilter(object): 123*4882a593Smuzhiyun rows = 25 124*4882a593Smuzhiyun columns = 80 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun def sigwinch_handle(self, signum, frame): 127*4882a593Smuzhiyun self.rows, self.columns = self.getTerminalColumns() 128*4882a593Smuzhiyun if self._sigwinch_default: 129*4882a593Smuzhiyun self._sigwinch_default(signum, frame) 130*4882a593Smuzhiyun 131*4882a593Smuzhiyun def getTerminalColumns(self): 132*4882a593Smuzhiyun def ioctl_GWINSZ(fd): 133*4882a593Smuzhiyun try: 134*4882a593Smuzhiyun cr = struct.unpack('hh', fcntl.ioctl(fd, self.termios.TIOCGWINSZ, '1234')) 135*4882a593Smuzhiyun except: 136*4882a593Smuzhiyun return None 137*4882a593Smuzhiyun return cr 138*4882a593Smuzhiyun cr = ioctl_GWINSZ(sys.stdout.fileno()) 139*4882a593Smuzhiyun if not cr: 140*4882a593Smuzhiyun try: 141*4882a593Smuzhiyun fd = os.open(os.ctermid(), os.O_RDONLY) 142*4882a593Smuzhiyun cr = ioctl_GWINSZ(fd) 143*4882a593Smuzhiyun os.close(fd) 144*4882a593Smuzhiyun except: 145*4882a593Smuzhiyun pass 146*4882a593Smuzhiyun if not cr: 147*4882a593Smuzhiyun try: 148*4882a593Smuzhiyun cr = (os.environ['LINES'], os.environ['COLUMNS']) 149*4882a593Smuzhiyun except: 150*4882a593Smuzhiyun cr = (25, 80) 151*4882a593Smuzhiyun return cr 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun def __init__(self, main, helper, handlers, quiet): 154*4882a593Smuzhiyun self.main = main 155*4882a593Smuzhiyun self.helper = helper 156*4882a593Smuzhiyun self.cuu = None 157*4882a593Smuzhiyun self.stdinbackup = None 158*4882a593Smuzhiyun self.interactive = sys.stdout.isatty() 159*4882a593Smuzhiyun self.footer_present = False 160*4882a593Smuzhiyun self.lastpids = [] 161*4882a593Smuzhiyun self.lasttime = None 162*4882a593Smuzhiyun self.quiet = quiet 163*4882a593Smuzhiyun 164*4882a593Smuzhiyun if not self.interactive: 165*4882a593Smuzhiyun return 166*4882a593Smuzhiyun 167*4882a593Smuzhiyun try: 168*4882a593Smuzhiyun import curses 169*4882a593Smuzhiyun except ImportError: 170*4882a593Smuzhiyun sys.exit("FATAL: The knotty ui could not load the required curses python module.") 171*4882a593Smuzhiyun 172*4882a593Smuzhiyun import termios 173*4882a593Smuzhiyun self.curses = curses 174*4882a593Smuzhiyun self.termios = termios 175*4882a593Smuzhiyun try: 176*4882a593Smuzhiyun fd = sys.stdin.fileno() 177*4882a593Smuzhiyun self.stdinbackup = termios.tcgetattr(fd) 178*4882a593Smuzhiyun new = copy.deepcopy(self.stdinbackup) 179*4882a593Smuzhiyun new[3] = new[3] & ~termios.ECHO 180*4882a593Smuzhiyun termios.tcsetattr(fd, termios.TCSADRAIN, new) 181*4882a593Smuzhiyun curses.setupterm() 182*4882a593Smuzhiyun if curses.tigetnum("colors") > 2: 183*4882a593Smuzhiyun for h in handlers: 184*4882a593Smuzhiyun try: 185*4882a593Smuzhiyun h.formatter.enable_color() 186*4882a593Smuzhiyun except AttributeError: 187*4882a593Smuzhiyun pass 188*4882a593Smuzhiyun self.ed = curses.tigetstr("ed") 189*4882a593Smuzhiyun if self.ed: 190*4882a593Smuzhiyun self.cuu = curses.tigetstr("cuu") 191*4882a593Smuzhiyun try: 192*4882a593Smuzhiyun self._sigwinch_default = signal.getsignal(signal.SIGWINCH) 193*4882a593Smuzhiyun signal.signal(signal.SIGWINCH, self.sigwinch_handle) 194*4882a593Smuzhiyun except: 195*4882a593Smuzhiyun pass 196*4882a593Smuzhiyun self.rows, self.columns = self.getTerminalColumns() 197*4882a593Smuzhiyun except: 198*4882a593Smuzhiyun self.cuu = None 199*4882a593Smuzhiyun if not self.cuu: 200*4882a593Smuzhiyun self.interactive = False 201*4882a593Smuzhiyun bb.note("Unable to use interactive mode for this terminal, using fallback") 202*4882a593Smuzhiyun return 203*4882a593Smuzhiyun 204*4882a593Smuzhiyun for h in handlers: 205*4882a593Smuzhiyun h.addFilter(InteractConsoleLogFilter(self)) 206*4882a593Smuzhiyun 207*4882a593Smuzhiyun self.main_progress = None 208*4882a593Smuzhiyun 209*4882a593Smuzhiyun def clearFooter(self): 210*4882a593Smuzhiyun if self.footer_present: 211*4882a593Smuzhiyun lines = self.footer_present 212*4882a593Smuzhiyun sys.stdout.buffer.write(self.curses.tparm(self.cuu, lines)) 213*4882a593Smuzhiyun sys.stdout.buffer.write(self.curses.tparm(self.ed)) 214*4882a593Smuzhiyun sys.stdout.flush() 215*4882a593Smuzhiyun self.footer_present = False 216*4882a593Smuzhiyun 217*4882a593Smuzhiyun def elapsed(self, sec): 218*4882a593Smuzhiyun hrs = int(sec / 3600.0) 219*4882a593Smuzhiyun sec -= hrs * 3600 220*4882a593Smuzhiyun min = int(sec / 60.0) 221*4882a593Smuzhiyun sec -= min * 60 222*4882a593Smuzhiyun if hrs > 0: 223*4882a593Smuzhiyun return "%dh%dm%ds" % (hrs, min, sec) 224*4882a593Smuzhiyun elif min > 0: 225*4882a593Smuzhiyun return "%dm%ds" % (min, sec) 226*4882a593Smuzhiyun else: 227*4882a593Smuzhiyun return "%ds" % (sec) 228*4882a593Smuzhiyun 229*4882a593Smuzhiyun def keepAlive(self, t): 230*4882a593Smuzhiyun if not self.cuu: 231*4882a593Smuzhiyun print("Bitbake still alive (no events for %ds). Active tasks:" % t) 232*4882a593Smuzhiyun for t in self.helper.running_tasks: 233*4882a593Smuzhiyun print(t) 234*4882a593Smuzhiyun sys.stdout.flush() 235*4882a593Smuzhiyun 236*4882a593Smuzhiyun def updateFooter(self): 237*4882a593Smuzhiyun if not self.cuu: 238*4882a593Smuzhiyun return 239*4882a593Smuzhiyun activetasks = self.helper.running_tasks 240*4882a593Smuzhiyun failedtasks = self.helper.failed_tasks 241*4882a593Smuzhiyun runningpids = self.helper.running_pids 242*4882a593Smuzhiyun currenttime = time.time() 243*4882a593Smuzhiyun if not self.lasttime or (currenttime - self.lasttime > 5): 244*4882a593Smuzhiyun self.helper.needUpdate = True 245*4882a593Smuzhiyun self.lasttime = currenttime 246*4882a593Smuzhiyun if self.footer_present and not self.helper.needUpdate: 247*4882a593Smuzhiyun return 248*4882a593Smuzhiyun self.helper.needUpdate = False 249*4882a593Smuzhiyun if self.footer_present: 250*4882a593Smuzhiyun self.clearFooter() 251*4882a593Smuzhiyun if (not self.helper.tasknumber_total or self.helper.tasknumber_current == self.helper.tasknumber_total) and not len(activetasks): 252*4882a593Smuzhiyun return 253*4882a593Smuzhiyun tasks = [] 254*4882a593Smuzhiyun for t in runningpids: 255*4882a593Smuzhiyun start_time = activetasks[t].get("starttime", None) 256*4882a593Smuzhiyun if start_time: 257*4882a593Smuzhiyun msg = "%s - %s (pid %s)" % (activetasks[t]["title"], self.elapsed(currenttime - start_time), activetasks[t]["pid"]) 258*4882a593Smuzhiyun else: 259*4882a593Smuzhiyun msg = "%s (pid %s)" % (activetasks[t]["title"], activetasks[t]["pid"]) 260*4882a593Smuzhiyun progress = activetasks[t].get("progress", None) 261*4882a593Smuzhiyun if progress is not None: 262*4882a593Smuzhiyun pbar = activetasks[t].get("progressbar", None) 263*4882a593Smuzhiyun rate = activetasks[t].get("rate", None) 264*4882a593Smuzhiyun if not pbar or pbar.bouncing != (progress < 0): 265*4882a593Smuzhiyun if progress < 0: 266*4882a593Smuzhiyun pbar = BBProgress("0: %s" % msg, 100, widgets=[' ', progressbar.BouncingSlider(), ''], extrapos=3, resize_handler=self.sigwinch_handle) 267*4882a593Smuzhiyun pbar.bouncing = True 268*4882a593Smuzhiyun else: 269*4882a593Smuzhiyun pbar = BBProgress("0: %s" % msg, 100, widgets=[' ', progressbar.Percentage(), ' ', progressbar.Bar(), ''], extrapos=5, resize_handler=self.sigwinch_handle) 270*4882a593Smuzhiyun pbar.bouncing = False 271*4882a593Smuzhiyun activetasks[t]["progressbar"] = pbar 272*4882a593Smuzhiyun tasks.append((pbar, msg, progress, rate, start_time)) 273*4882a593Smuzhiyun else: 274*4882a593Smuzhiyun tasks.append(msg) 275*4882a593Smuzhiyun 276*4882a593Smuzhiyun if self.main.shutdown: 277*4882a593Smuzhiyun content = pluralise("Waiting for %s running task to finish", 278*4882a593Smuzhiyun "Waiting for %s running tasks to finish", len(activetasks)) 279*4882a593Smuzhiyun if not self.quiet: 280*4882a593Smuzhiyun content += ':' 281*4882a593Smuzhiyun print(content) 282*4882a593Smuzhiyun else: 283*4882a593Smuzhiyun scene_tasks = "%s of %s" % (self.helper.setscene_current, self.helper.setscene_total) 284*4882a593Smuzhiyun cur_tasks = "%s of %s" % (self.helper.tasknumber_current, self.helper.tasknumber_total) 285*4882a593Smuzhiyun 286*4882a593Smuzhiyun content = '' 287*4882a593Smuzhiyun if not self.quiet: 288*4882a593Smuzhiyun msg = "Setscene tasks: %s" % scene_tasks 289*4882a593Smuzhiyun content += msg + "\n" 290*4882a593Smuzhiyun print(msg) 291*4882a593Smuzhiyun 292*4882a593Smuzhiyun if self.quiet: 293*4882a593Smuzhiyun msg = "Running tasks (%s, %s)" % (scene_tasks, cur_tasks) 294*4882a593Smuzhiyun elif not len(activetasks): 295*4882a593Smuzhiyun msg = "No currently running tasks (%s)" % cur_tasks 296*4882a593Smuzhiyun else: 297*4882a593Smuzhiyun msg = "Currently %2s running tasks (%s)" % (len(activetasks), cur_tasks) 298*4882a593Smuzhiyun maxtask = self.helper.tasknumber_total 299*4882a593Smuzhiyun if not self.main_progress or self.main_progress.maxval != maxtask: 300*4882a593Smuzhiyun widgets = [' ', progressbar.Percentage(), ' ', progressbar.Bar()] 301*4882a593Smuzhiyun self.main_progress = BBProgress("Running tasks", maxtask, widgets=widgets, resize_handler=self.sigwinch_handle) 302*4882a593Smuzhiyun self.main_progress.start(False) 303*4882a593Smuzhiyun self.main_progress.setmessage(msg) 304*4882a593Smuzhiyun progress = max(0, self.helper.tasknumber_current - 1) 305*4882a593Smuzhiyun content += self.main_progress.update(progress) 306*4882a593Smuzhiyun print('') 307*4882a593Smuzhiyun lines = self.getlines(content) 308*4882a593Smuzhiyun if not self.quiet: 309*4882a593Smuzhiyun for tasknum, task in enumerate(tasks[:(self.rows - 1 - lines)]): 310*4882a593Smuzhiyun if isinstance(task, tuple): 311*4882a593Smuzhiyun pbar, msg, progress, rate, start_time = task 312*4882a593Smuzhiyun if not pbar.start_time: 313*4882a593Smuzhiyun pbar.start(False) 314*4882a593Smuzhiyun if start_time: 315*4882a593Smuzhiyun pbar.start_time = start_time 316*4882a593Smuzhiyun pbar.setmessage('%s: %s' % (tasknum, msg)) 317*4882a593Smuzhiyun pbar.setextra(rate) 318*4882a593Smuzhiyun if progress > -1: 319*4882a593Smuzhiyun content = pbar.update(progress) 320*4882a593Smuzhiyun else: 321*4882a593Smuzhiyun content = pbar.update(1) 322*4882a593Smuzhiyun print('') 323*4882a593Smuzhiyun else: 324*4882a593Smuzhiyun content = "%s: %s" % (tasknum, task) 325*4882a593Smuzhiyun print(content) 326*4882a593Smuzhiyun lines = lines + self.getlines(content) 327*4882a593Smuzhiyun self.footer_present = lines 328*4882a593Smuzhiyun self.lastpids = runningpids[:] 329*4882a593Smuzhiyun self.lastcount = self.helper.tasknumber_current 330*4882a593Smuzhiyun 331*4882a593Smuzhiyun def getlines(self, content): 332*4882a593Smuzhiyun lines = 0 333*4882a593Smuzhiyun for line in content.split("\n"): 334*4882a593Smuzhiyun lines = lines + 1 + int(len(line) / (self.columns + 1)) 335*4882a593Smuzhiyun return lines 336*4882a593Smuzhiyun 337*4882a593Smuzhiyun def finish(self): 338*4882a593Smuzhiyun if self.stdinbackup: 339*4882a593Smuzhiyun fd = sys.stdin.fileno() 340*4882a593Smuzhiyun self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup) 341*4882a593Smuzhiyun 342*4882a593Smuzhiyundef print_event_log(event, includelogs, loglines, termfilter): 343*4882a593Smuzhiyun # FIXME refactor this out further 344*4882a593Smuzhiyun logfile = event.logfile 345*4882a593Smuzhiyun if logfile and os.path.exists(logfile): 346*4882a593Smuzhiyun termfilter.clearFooter() 347*4882a593Smuzhiyun bb.error("Logfile of failure stored in: %s" % logfile) 348*4882a593Smuzhiyun if includelogs and not event.errprinted: 349*4882a593Smuzhiyun print("Log data follows:") 350*4882a593Smuzhiyun f = open(logfile, "r") 351*4882a593Smuzhiyun lines = [] 352*4882a593Smuzhiyun while True: 353*4882a593Smuzhiyun l = f.readline() 354*4882a593Smuzhiyun if l == '': 355*4882a593Smuzhiyun break 356*4882a593Smuzhiyun l = l.rstrip() 357*4882a593Smuzhiyun if loglines: 358*4882a593Smuzhiyun lines.append(' | %s' % l) 359*4882a593Smuzhiyun if len(lines) > int(loglines): 360*4882a593Smuzhiyun lines.pop(0) 361*4882a593Smuzhiyun else: 362*4882a593Smuzhiyun print('| %s' % l) 363*4882a593Smuzhiyun f.close() 364*4882a593Smuzhiyun if lines: 365*4882a593Smuzhiyun for line in lines: 366*4882a593Smuzhiyun print(line) 367*4882a593Smuzhiyun 368*4882a593Smuzhiyundef _log_settings_from_server(server, observe_only): 369*4882a593Smuzhiyun # Get values of variables which control our output 370*4882a593Smuzhiyun includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"]) 371*4882a593Smuzhiyun if error: 372*4882a593Smuzhiyun logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error) 373*4882a593Smuzhiyun raise BaseException(error) 374*4882a593Smuzhiyun loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"]) 375*4882a593Smuzhiyun if error: 376*4882a593Smuzhiyun logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error) 377*4882a593Smuzhiyun raise BaseException(error) 378*4882a593Smuzhiyun if observe_only: 379*4882a593Smuzhiyun cmd = 'getVariable' 380*4882a593Smuzhiyun else: 381*4882a593Smuzhiyun cmd = 'getSetVariable' 382*4882a593Smuzhiyun consolelogfile, error = server.runCommand([cmd, "BB_CONSOLELOG"]) 383*4882a593Smuzhiyun if error: 384*4882a593Smuzhiyun logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error) 385*4882a593Smuzhiyun raise BaseException(error) 386*4882a593Smuzhiyun logconfigfile, error = server.runCommand([cmd, "BB_LOGCONFIG"]) 387*4882a593Smuzhiyun if error: 388*4882a593Smuzhiyun logger.error("Unable to get the value of BB_LOGCONFIG variable: %s" % error) 389*4882a593Smuzhiyun raise BaseException(error) 390*4882a593Smuzhiyun return includelogs, loglines, consolelogfile, logconfigfile 391*4882a593Smuzhiyun 392*4882a593Smuzhiyun_evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord", 393*4882a593Smuzhiyun "bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted", 394*4882a593Smuzhiyun "bb.event.ParseProgress", "bb.event.ParseCompleted", "bb.event.CacheLoadStarted", 395*4882a593Smuzhiyun "bb.event.CacheLoadProgress", "bb.event.CacheLoadCompleted", "bb.command.CommandFailed", 396*4882a593Smuzhiyun "bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit", 397*4882a593Smuzhiyun "bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted", 398*4882a593Smuzhiyun "bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed", 399*4882a593Smuzhiyun "bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent", 400*4882a593Smuzhiyun "bb.build.TaskProgress", "bb.event.ProcessStarted", "bb.event.ProcessProgress", "bb.event.ProcessFinished"] 401*4882a593Smuzhiyun 402*4882a593Smuzhiyundef drain_events_errorhandling(eventHandler): 403*4882a593Smuzhiyun # We don't have logging setup, we do need to show any events we see before exiting 404*4882a593Smuzhiyun event = True 405*4882a593Smuzhiyun logger = bb.msg.logger_create('bitbake', sys.stdout) 406*4882a593Smuzhiyun while event: 407*4882a593Smuzhiyun event = eventHandler.waitEvent(0) 408*4882a593Smuzhiyun if isinstance(event, logging.LogRecord): 409*4882a593Smuzhiyun logger.handle(event) 410*4882a593Smuzhiyun 411*4882a593Smuzhiyundef main(server, eventHandler, params, tf = TerminalFilter): 412*4882a593Smuzhiyun 413*4882a593Smuzhiyun try: 414*4882a593Smuzhiyun if not params.observe_only: 415*4882a593Smuzhiyun params.updateToServer(server, os.environ.copy()) 416*4882a593Smuzhiyun 417*4882a593Smuzhiyun includelogs, loglines, consolelogfile, logconfigfile = _log_settings_from_server(server, params.observe_only) 418*4882a593Smuzhiyun 419*4882a593Smuzhiyun loglevel, _ = bb.msg.constructLogOptions() 420*4882a593Smuzhiyun except bb.BBHandledException: 421*4882a593Smuzhiyun drain_events_errorhandling(eventHandler) 422*4882a593Smuzhiyun return 1 423*4882a593Smuzhiyun 424*4882a593Smuzhiyun if params.options.quiet == 0: 425*4882a593Smuzhiyun console_loglevel = loglevel 426*4882a593Smuzhiyun elif params.options.quiet > 2: 427*4882a593Smuzhiyun console_loglevel = bb.msg.BBLogFormatter.ERROR 428*4882a593Smuzhiyun else: 429*4882a593Smuzhiyun console_loglevel = bb.msg.BBLogFormatter.WARNING 430*4882a593Smuzhiyun 431*4882a593Smuzhiyun logconfig = { 432*4882a593Smuzhiyun "version": 1, 433*4882a593Smuzhiyun "handlers": { 434*4882a593Smuzhiyun "BitBake.console": { 435*4882a593Smuzhiyun "class": "logging.StreamHandler", 436*4882a593Smuzhiyun "formatter": "BitBake.consoleFormatter", 437*4882a593Smuzhiyun "level": console_loglevel, 438*4882a593Smuzhiyun "stream": "ext://sys.stdout", 439*4882a593Smuzhiyun "filters": ["BitBake.stdoutFilter"], 440*4882a593Smuzhiyun ".": { 441*4882a593Smuzhiyun "is_console": True, 442*4882a593Smuzhiyun }, 443*4882a593Smuzhiyun }, 444*4882a593Smuzhiyun "BitBake.errconsole": { 445*4882a593Smuzhiyun "class": "logging.StreamHandler", 446*4882a593Smuzhiyun "formatter": "BitBake.consoleFormatter", 447*4882a593Smuzhiyun "level": loglevel, 448*4882a593Smuzhiyun "stream": "ext://sys.stderr", 449*4882a593Smuzhiyun "filters": ["BitBake.stderrFilter"], 450*4882a593Smuzhiyun ".": { 451*4882a593Smuzhiyun "is_console": True, 452*4882a593Smuzhiyun }, 453*4882a593Smuzhiyun }, 454*4882a593Smuzhiyun # This handler can be used if specific loggers should print on 455*4882a593Smuzhiyun # the console at a lower severity than the default. It will 456*4882a593Smuzhiyun # display any messages sent to it that are lower than then 457*4882a593Smuzhiyun # BitBake.console logging level (so as to prevent duplication of 458*4882a593Smuzhiyun # messages). Nothing is attached to this handler by default 459*4882a593Smuzhiyun "BitBake.verbconsole": { 460*4882a593Smuzhiyun "class": "logging.StreamHandler", 461*4882a593Smuzhiyun "formatter": "BitBake.consoleFormatter", 462*4882a593Smuzhiyun "level": 1, 463*4882a593Smuzhiyun "stream": "ext://sys.stdout", 464*4882a593Smuzhiyun "filters": ["BitBake.verbconsoleFilter"], 465*4882a593Smuzhiyun ".": { 466*4882a593Smuzhiyun "is_console": True, 467*4882a593Smuzhiyun }, 468*4882a593Smuzhiyun }, 469*4882a593Smuzhiyun }, 470*4882a593Smuzhiyun "formatters": { 471*4882a593Smuzhiyun # This format instance will get color output enabled by the 472*4882a593Smuzhiyun # terminal 473*4882a593Smuzhiyun "BitBake.consoleFormatter" : { 474*4882a593Smuzhiyun "()": "bb.msg.BBLogFormatter", 475*4882a593Smuzhiyun "format": "%(levelname)s: %(message)s" 476*4882a593Smuzhiyun }, 477*4882a593Smuzhiyun # The file log requires a separate instance so that it doesn't get 478*4882a593Smuzhiyun # color enabled 479*4882a593Smuzhiyun "BitBake.logfileFormatter": { 480*4882a593Smuzhiyun "()": "bb.msg.BBLogFormatter", 481*4882a593Smuzhiyun "format": "%(levelname)s: %(message)s" 482*4882a593Smuzhiyun } 483*4882a593Smuzhiyun }, 484*4882a593Smuzhiyun "filters": { 485*4882a593Smuzhiyun "BitBake.stdoutFilter": { 486*4882a593Smuzhiyun "()": "bb.msg.LogFilterLTLevel", 487*4882a593Smuzhiyun "level": "ERROR" 488*4882a593Smuzhiyun }, 489*4882a593Smuzhiyun "BitBake.stderrFilter": { 490*4882a593Smuzhiyun "()": "bb.msg.LogFilterGEQLevel", 491*4882a593Smuzhiyun "level": "ERROR" 492*4882a593Smuzhiyun }, 493*4882a593Smuzhiyun "BitBake.verbconsoleFilter": { 494*4882a593Smuzhiyun "()": "bb.msg.LogFilterLTLevel", 495*4882a593Smuzhiyun "level": console_loglevel 496*4882a593Smuzhiyun }, 497*4882a593Smuzhiyun }, 498*4882a593Smuzhiyun "loggers": { 499*4882a593Smuzhiyun "BitBake": { 500*4882a593Smuzhiyun "level": loglevel, 501*4882a593Smuzhiyun "handlers": ["BitBake.console", "BitBake.errconsole"], 502*4882a593Smuzhiyun } 503*4882a593Smuzhiyun }, 504*4882a593Smuzhiyun "disable_existing_loggers": False 505*4882a593Smuzhiyun } 506*4882a593Smuzhiyun 507*4882a593Smuzhiyun # Enable the console log file if enabled 508*4882a593Smuzhiyun if consolelogfile and not params.options.show_environment and not params.options.show_versions: 509*4882a593Smuzhiyun logconfig = bb.msg.mergeLoggingConfig(logconfig, { 510*4882a593Smuzhiyun "version": 1, 511*4882a593Smuzhiyun "handlers" : { 512*4882a593Smuzhiyun "BitBake.consolelog": { 513*4882a593Smuzhiyun "class": "logging.FileHandler", 514*4882a593Smuzhiyun "formatter": "BitBake.logfileFormatter", 515*4882a593Smuzhiyun "level": loglevel, 516*4882a593Smuzhiyun "filename": consolelogfile, 517*4882a593Smuzhiyun }, 518*4882a593Smuzhiyun # Just like verbconsole, anything sent here will go to the 519*4882a593Smuzhiyun # log file, unless it would go to BitBake.consolelog 520*4882a593Smuzhiyun "BitBake.verbconsolelog" : { 521*4882a593Smuzhiyun "class": "logging.FileHandler", 522*4882a593Smuzhiyun "formatter": "BitBake.logfileFormatter", 523*4882a593Smuzhiyun "level": 1, 524*4882a593Smuzhiyun "filename": consolelogfile, 525*4882a593Smuzhiyun "filters": ["BitBake.verbconsolelogFilter"], 526*4882a593Smuzhiyun }, 527*4882a593Smuzhiyun }, 528*4882a593Smuzhiyun "filters": { 529*4882a593Smuzhiyun "BitBake.verbconsolelogFilter": { 530*4882a593Smuzhiyun "()": "bb.msg.LogFilterLTLevel", 531*4882a593Smuzhiyun "level": loglevel, 532*4882a593Smuzhiyun }, 533*4882a593Smuzhiyun }, 534*4882a593Smuzhiyun "loggers": { 535*4882a593Smuzhiyun "BitBake": { 536*4882a593Smuzhiyun "handlers": ["BitBake.consolelog"], 537*4882a593Smuzhiyun }, 538*4882a593Smuzhiyun 539*4882a593Smuzhiyun # Other interesting things that we want to keep an eye on 540*4882a593Smuzhiyun # in the log files in case someone has an issue, but not 541*4882a593Smuzhiyun # necessarily show to the user on the console 542*4882a593Smuzhiyun "BitBake.SigGen.HashEquiv": { 543*4882a593Smuzhiyun "level": "VERBOSE", 544*4882a593Smuzhiyun "handlers": ["BitBake.verbconsolelog"], 545*4882a593Smuzhiyun }, 546*4882a593Smuzhiyun "BitBake.RunQueue.HashEquiv": { 547*4882a593Smuzhiyun "level": "VERBOSE", 548*4882a593Smuzhiyun "handlers": ["BitBake.verbconsolelog"], 549*4882a593Smuzhiyun } 550*4882a593Smuzhiyun } 551*4882a593Smuzhiyun }) 552*4882a593Smuzhiyun 553*4882a593Smuzhiyun bb.utils.mkdirhier(os.path.dirname(consolelogfile)) 554*4882a593Smuzhiyun loglink = os.path.join(os.path.dirname(consolelogfile), 'console-latest.log') 555*4882a593Smuzhiyun bb.utils.remove(loglink) 556*4882a593Smuzhiyun try: 557*4882a593Smuzhiyun os.symlink(os.path.basename(consolelogfile), loglink) 558*4882a593Smuzhiyun except OSError: 559*4882a593Smuzhiyun pass 560*4882a593Smuzhiyun 561*4882a593Smuzhiyun # Add the logging domains specified by the user on the command line 562*4882a593Smuzhiyun for (domainarg, iterator) in groupby(params.debug_domains): 563*4882a593Smuzhiyun dlevel = len(tuple(iterator)) 564*4882a593Smuzhiyun l = logconfig["loggers"].setdefault("BitBake.%s" % domainarg, {}) 565*4882a593Smuzhiyun l["level"] = logging.DEBUG - dlevel + 1 566*4882a593Smuzhiyun l.setdefault("handlers", []).extend(["BitBake.verbconsole"]) 567*4882a593Smuzhiyun 568*4882a593Smuzhiyun conf = bb.msg.setLoggingConfig(logconfig, logconfigfile) 569*4882a593Smuzhiyun 570*4882a593Smuzhiyun if sys.stdin.isatty() and sys.stdout.isatty(): 571*4882a593Smuzhiyun log_exec_tty = True 572*4882a593Smuzhiyun else: 573*4882a593Smuzhiyun log_exec_tty = False 574*4882a593Smuzhiyun 575*4882a593Smuzhiyun helper = uihelper.BBUIHelper() 576*4882a593Smuzhiyun 577*4882a593Smuzhiyun # Look for the specially designated handlers which need to be passed to the 578*4882a593Smuzhiyun # terminal handler 579*4882a593Smuzhiyun console_handlers = [h for h in conf.config['handlers'].values() if getattr(h, 'is_console', False)] 580*4882a593Smuzhiyun 581*4882a593Smuzhiyun bb.utils.set_process_name("KnottyUI") 582*4882a593Smuzhiyun 583*4882a593Smuzhiyun if params.options.remote_server and params.options.kill_server: 584*4882a593Smuzhiyun server.terminateServer() 585*4882a593Smuzhiyun return 586*4882a593Smuzhiyun 587*4882a593Smuzhiyun llevel, debug_domains = bb.msg.constructLogOptions() 588*4882a593Smuzhiyun server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list]) 589*4882a593Smuzhiyun 590*4882a593Smuzhiyun # The logging_tree module is *extremely* helpful in debugging logging 591*4882a593Smuzhiyun # domains. Uncomment here to dump the logging tree when bitbake starts 592*4882a593Smuzhiyun #import logging_tree 593*4882a593Smuzhiyun #logging_tree.printout() 594*4882a593Smuzhiyun 595*4882a593Smuzhiyun universe = False 596*4882a593Smuzhiyun if not params.observe_only: 597*4882a593Smuzhiyun params.updateFromServer(server) 598*4882a593Smuzhiyun cmdline = params.parseActions() 599*4882a593Smuzhiyun if not cmdline: 600*4882a593Smuzhiyun print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.") 601*4882a593Smuzhiyun return 1 602*4882a593Smuzhiyun if 'msg' in cmdline and cmdline['msg']: 603*4882a593Smuzhiyun logger.error(cmdline['msg']) 604*4882a593Smuzhiyun return 1 605*4882a593Smuzhiyun if cmdline['action'][0] == "buildTargets" and "universe" in cmdline['action'][1]: 606*4882a593Smuzhiyun universe = True 607*4882a593Smuzhiyun 608*4882a593Smuzhiyun ret, error = server.runCommand(cmdline['action']) 609*4882a593Smuzhiyun if error: 610*4882a593Smuzhiyun logger.error("Command '%s' failed: %s" % (cmdline, error)) 611*4882a593Smuzhiyun return 1 612*4882a593Smuzhiyun elif not ret: 613*4882a593Smuzhiyun logger.error("Command '%s' failed: returned %s" % (cmdline, ret)) 614*4882a593Smuzhiyun return 1 615*4882a593Smuzhiyun 616*4882a593Smuzhiyun 617*4882a593Smuzhiyun parseprogress = None 618*4882a593Smuzhiyun cacheprogress = None 619*4882a593Smuzhiyun main.shutdown = 0 620*4882a593Smuzhiyun interrupted = False 621*4882a593Smuzhiyun return_value = 0 622*4882a593Smuzhiyun errors = 0 623*4882a593Smuzhiyun warnings = 0 624*4882a593Smuzhiyun taskfailures = [] 625*4882a593Smuzhiyun 626*4882a593Smuzhiyun printintervaldelta = 10 * 60 # 10 minutes 627*4882a593Smuzhiyun printinterval = printintervaldelta 628*4882a593Smuzhiyun lastprint = time.time() 629*4882a593Smuzhiyun 630*4882a593Smuzhiyun termfilter = tf(main, helper, console_handlers, params.options.quiet) 631*4882a593Smuzhiyun atexit.register(termfilter.finish) 632*4882a593Smuzhiyun 633*4882a593Smuzhiyun while True: 634*4882a593Smuzhiyun try: 635*4882a593Smuzhiyun if (lastprint + printinterval) <= time.time(): 636*4882a593Smuzhiyun termfilter.keepAlive(printinterval) 637*4882a593Smuzhiyun printinterval += printintervaldelta 638*4882a593Smuzhiyun event = eventHandler.waitEvent(0) 639*4882a593Smuzhiyun if event is None: 640*4882a593Smuzhiyun if main.shutdown > 1: 641*4882a593Smuzhiyun break 642*4882a593Smuzhiyun if not parseprogress: 643*4882a593Smuzhiyun termfilter.updateFooter() 644*4882a593Smuzhiyun event = eventHandler.waitEvent(0.25) 645*4882a593Smuzhiyun if event is None: 646*4882a593Smuzhiyun continue 647*4882a593Smuzhiyun helper.eventHandler(event) 648*4882a593Smuzhiyun if isinstance(event, bb.runqueue.runQueueExitWait): 649*4882a593Smuzhiyun if not main.shutdown: 650*4882a593Smuzhiyun main.shutdown = 1 651*4882a593Smuzhiyun continue 652*4882a593Smuzhiyun if isinstance(event, bb.event.LogExecTTY): 653*4882a593Smuzhiyun if log_exec_tty: 654*4882a593Smuzhiyun tries = event.retries 655*4882a593Smuzhiyun while tries: 656*4882a593Smuzhiyun print("Trying to run: %s" % event.prog) 657*4882a593Smuzhiyun if os.system(event.prog) == 0: 658*4882a593Smuzhiyun break 659*4882a593Smuzhiyun time.sleep(event.sleep_delay) 660*4882a593Smuzhiyun tries -= 1 661*4882a593Smuzhiyun if tries: 662*4882a593Smuzhiyun continue 663*4882a593Smuzhiyun logger.warning(event.msg) 664*4882a593Smuzhiyun continue 665*4882a593Smuzhiyun 666*4882a593Smuzhiyun if isinstance(event, logging.LogRecord): 667*4882a593Smuzhiyun lastprint = time.time() 668*4882a593Smuzhiyun printinterval = printintervaldelta 669*4882a593Smuzhiyun if event.levelno >= bb.msg.BBLogFormatter.ERRORONCE: 670*4882a593Smuzhiyun errors = errors + 1 671*4882a593Smuzhiyun return_value = 1 672*4882a593Smuzhiyun elif event.levelno == bb.msg.BBLogFormatter.WARNING: 673*4882a593Smuzhiyun warnings = warnings + 1 674*4882a593Smuzhiyun 675*4882a593Smuzhiyun if event.taskpid != 0: 676*4882a593Smuzhiyun # For "normal" logging conditions, don't show note logs from tasks 677*4882a593Smuzhiyun # but do show them if the user has changed the default log level to 678*4882a593Smuzhiyun # include verbose/debug messages 679*4882a593Smuzhiyun if event.levelno <= bb.msg.BBLogFormatter.NOTE and (event.levelno < llevel or (event.levelno == bb.msg.BBLogFormatter.NOTE and llevel != bb.msg.BBLogFormatter.VERBOSE)): 680*4882a593Smuzhiyun continue 681*4882a593Smuzhiyun 682*4882a593Smuzhiyun # Prefix task messages with recipe/task 683*4882a593Smuzhiyun if event.taskpid in helper.pidmap and event.levelno not in [bb.msg.BBLogFormatter.PLAIN, bb.msg.BBLogFormatter.WARNONCE, bb.msg.BBLogFormatter.ERRORONCE]: 684*4882a593Smuzhiyun taskinfo = helper.running_tasks[helper.pidmap[event.taskpid]] 685*4882a593Smuzhiyun event.msg = taskinfo['title'] + ': ' + event.msg 686*4882a593Smuzhiyun if hasattr(event, 'fn') and event.levelno not in [bb.msg.BBLogFormatter.WARNONCE, bb.msg.BBLogFormatter.ERRORONCE]: 687*4882a593Smuzhiyun event.msg = event.fn + ': ' + event.msg 688*4882a593Smuzhiyun logging.getLogger(event.name).handle(event) 689*4882a593Smuzhiyun continue 690*4882a593Smuzhiyun 691*4882a593Smuzhiyun if isinstance(event, bb.build.TaskFailedSilent): 692*4882a593Smuzhiyun logger.warning("Logfile for failed setscene task is %s" % event.logfile) 693*4882a593Smuzhiyun continue 694*4882a593Smuzhiyun if isinstance(event, bb.build.TaskFailed): 695*4882a593Smuzhiyun return_value = 1 696*4882a593Smuzhiyun print_event_log(event, includelogs, loglines, termfilter) 697*4882a593Smuzhiyun if isinstance(event, bb.build.TaskBase): 698*4882a593Smuzhiyun logger.info(event._message) 699*4882a593Smuzhiyun continue 700*4882a593Smuzhiyun if isinstance(event, bb.event.ParseStarted): 701*4882a593Smuzhiyun if params.options.quiet > 1: 702*4882a593Smuzhiyun continue 703*4882a593Smuzhiyun if event.total == 0: 704*4882a593Smuzhiyun continue 705*4882a593Smuzhiyun termfilter.clearFooter() 706*4882a593Smuzhiyun parseprogress = new_progress("Parsing recipes", event.total).start() 707*4882a593Smuzhiyun continue 708*4882a593Smuzhiyun if isinstance(event, bb.event.ParseProgress): 709*4882a593Smuzhiyun if params.options.quiet > 1: 710*4882a593Smuzhiyun continue 711*4882a593Smuzhiyun if parseprogress: 712*4882a593Smuzhiyun parseprogress.update(event.current) 713*4882a593Smuzhiyun else: 714*4882a593Smuzhiyun bb.warn("Got ParseProgress event for parsing that never started?") 715*4882a593Smuzhiyun continue 716*4882a593Smuzhiyun if isinstance(event, bb.event.ParseCompleted): 717*4882a593Smuzhiyun if params.options.quiet > 1: 718*4882a593Smuzhiyun continue 719*4882a593Smuzhiyun if not parseprogress: 720*4882a593Smuzhiyun continue 721*4882a593Smuzhiyun parseprogress.finish() 722*4882a593Smuzhiyun parseprogress = None 723*4882a593Smuzhiyun if params.options.quiet == 0: 724*4882a593Smuzhiyun print(("Parsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors." 725*4882a593Smuzhiyun % ( event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors))) 726*4882a593Smuzhiyun continue 727*4882a593Smuzhiyun 728*4882a593Smuzhiyun if isinstance(event, bb.event.CacheLoadStarted): 729*4882a593Smuzhiyun if params.options.quiet > 1: 730*4882a593Smuzhiyun continue 731*4882a593Smuzhiyun cacheprogress = new_progress("Loading cache", event.total).start() 732*4882a593Smuzhiyun continue 733*4882a593Smuzhiyun if isinstance(event, bb.event.CacheLoadProgress): 734*4882a593Smuzhiyun if params.options.quiet > 1: 735*4882a593Smuzhiyun continue 736*4882a593Smuzhiyun cacheprogress.update(event.current) 737*4882a593Smuzhiyun continue 738*4882a593Smuzhiyun if isinstance(event, bb.event.CacheLoadCompleted): 739*4882a593Smuzhiyun if params.options.quiet > 1: 740*4882a593Smuzhiyun continue 741*4882a593Smuzhiyun cacheprogress.finish() 742*4882a593Smuzhiyun if params.options.quiet == 0: 743*4882a593Smuzhiyun print("Loaded %d entries from dependency cache." % event.num_entries) 744*4882a593Smuzhiyun continue 745*4882a593Smuzhiyun 746*4882a593Smuzhiyun if isinstance(event, bb.command.CommandFailed): 747*4882a593Smuzhiyun return_value = event.exitcode 748*4882a593Smuzhiyun if event.error: 749*4882a593Smuzhiyun errors = errors + 1 750*4882a593Smuzhiyun logger.error(str(event)) 751*4882a593Smuzhiyun main.shutdown = 2 752*4882a593Smuzhiyun continue 753*4882a593Smuzhiyun if isinstance(event, bb.command.CommandExit): 754*4882a593Smuzhiyun if not return_value: 755*4882a593Smuzhiyun return_value = event.exitcode 756*4882a593Smuzhiyun main.shutdown = 2 757*4882a593Smuzhiyun continue 758*4882a593Smuzhiyun if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)): 759*4882a593Smuzhiyun main.shutdown = 2 760*4882a593Smuzhiyun continue 761*4882a593Smuzhiyun if isinstance(event, bb.event.MultipleProviders): 762*4882a593Smuzhiyun logger.info(str(event)) 763*4882a593Smuzhiyun continue 764*4882a593Smuzhiyun if isinstance(event, bb.event.NoProvider): 765*4882a593Smuzhiyun # For universe builds, only show these as warnings, not errors 766*4882a593Smuzhiyun if not universe: 767*4882a593Smuzhiyun return_value = 1 768*4882a593Smuzhiyun errors = errors + 1 769*4882a593Smuzhiyun logger.error(str(event)) 770*4882a593Smuzhiyun else: 771*4882a593Smuzhiyun logger.warning(str(event)) 772*4882a593Smuzhiyun continue 773*4882a593Smuzhiyun 774*4882a593Smuzhiyun if isinstance(event, bb.runqueue.sceneQueueTaskStarted): 775*4882a593Smuzhiyun logger.info("Running setscene task %d of %d (%s)" % (event.stats.setscene_covered + event.stats.setscene_active + event.stats.setscene_notcovered + 1, event.stats.setscene_total, event.taskstring)) 776*4882a593Smuzhiyun continue 777*4882a593Smuzhiyun 778*4882a593Smuzhiyun if isinstance(event, bb.runqueue.runQueueTaskStarted): 779*4882a593Smuzhiyun if event.noexec: 780*4882a593Smuzhiyun tasktype = 'noexec task' 781*4882a593Smuzhiyun else: 782*4882a593Smuzhiyun tasktype = 'task' 783*4882a593Smuzhiyun logger.info("Running %s %d of %d (%s)", 784*4882a593Smuzhiyun tasktype, 785*4882a593Smuzhiyun event.stats.completed + event.stats.active + 786*4882a593Smuzhiyun event.stats.failed + 1, 787*4882a593Smuzhiyun event.stats.total, event.taskstring) 788*4882a593Smuzhiyun continue 789*4882a593Smuzhiyun 790*4882a593Smuzhiyun if isinstance(event, bb.runqueue.runQueueTaskFailed): 791*4882a593Smuzhiyun return_value = 1 792*4882a593Smuzhiyun taskfailures.append(event.taskstring) 793*4882a593Smuzhiyun logger.error(str(event)) 794*4882a593Smuzhiyun continue 795*4882a593Smuzhiyun 796*4882a593Smuzhiyun if isinstance(event, bb.runqueue.sceneQueueTaskFailed): 797*4882a593Smuzhiyun logger.warning(str(event)) 798*4882a593Smuzhiyun continue 799*4882a593Smuzhiyun 800*4882a593Smuzhiyun if isinstance(event, bb.event.DepTreeGenerated): 801*4882a593Smuzhiyun continue 802*4882a593Smuzhiyun 803*4882a593Smuzhiyun if isinstance(event, bb.event.ProcessStarted): 804*4882a593Smuzhiyun if params.options.quiet > 1: 805*4882a593Smuzhiyun continue 806*4882a593Smuzhiyun termfilter.clearFooter() 807*4882a593Smuzhiyun parseprogress = new_progress(event.processname, event.total) 808*4882a593Smuzhiyun parseprogress.start(False) 809*4882a593Smuzhiyun continue 810*4882a593Smuzhiyun if isinstance(event, bb.event.ProcessProgress): 811*4882a593Smuzhiyun if params.options.quiet > 1: 812*4882a593Smuzhiyun continue 813*4882a593Smuzhiyun if parseprogress: 814*4882a593Smuzhiyun parseprogress.update(event.progress) 815*4882a593Smuzhiyun else: 816*4882a593Smuzhiyun bb.warn("Got ProcessProgress event for someting that never started?") 817*4882a593Smuzhiyun continue 818*4882a593Smuzhiyun if isinstance(event, bb.event.ProcessFinished): 819*4882a593Smuzhiyun if params.options.quiet > 1: 820*4882a593Smuzhiyun continue 821*4882a593Smuzhiyun if parseprogress: 822*4882a593Smuzhiyun parseprogress.finish() 823*4882a593Smuzhiyun parseprogress = None 824*4882a593Smuzhiyun continue 825*4882a593Smuzhiyun 826*4882a593Smuzhiyun # ignore 827*4882a593Smuzhiyun if isinstance(event, (bb.event.BuildBase, 828*4882a593Smuzhiyun bb.event.MetadataEvent, 829*4882a593Smuzhiyun bb.event.ConfigParsed, 830*4882a593Smuzhiyun bb.event.MultiConfigParsed, 831*4882a593Smuzhiyun bb.event.RecipeParsed, 832*4882a593Smuzhiyun bb.event.RecipePreFinalise, 833*4882a593Smuzhiyun bb.runqueue.runQueueEvent, 834*4882a593Smuzhiyun bb.event.OperationStarted, 835*4882a593Smuzhiyun bb.event.OperationCompleted, 836*4882a593Smuzhiyun bb.event.OperationProgress, 837*4882a593Smuzhiyun bb.event.DiskFull, 838*4882a593Smuzhiyun bb.event.HeartbeatEvent, 839*4882a593Smuzhiyun bb.build.TaskProgress)): 840*4882a593Smuzhiyun continue 841*4882a593Smuzhiyun 842*4882a593Smuzhiyun logger.error("Unknown event: %s", event) 843*4882a593Smuzhiyun 844*4882a593Smuzhiyun except EnvironmentError as ioerror: 845*4882a593Smuzhiyun termfilter.clearFooter() 846*4882a593Smuzhiyun # ignore interrupted io 847*4882a593Smuzhiyun if ioerror.args[0] == 4: 848*4882a593Smuzhiyun continue 849*4882a593Smuzhiyun sys.stderr.write(str(ioerror)) 850*4882a593Smuzhiyun if not params.observe_only: 851*4882a593Smuzhiyun _, error = server.runCommand(["stateForceShutdown"]) 852*4882a593Smuzhiyun main.shutdown = 2 853*4882a593Smuzhiyun except KeyboardInterrupt: 854*4882a593Smuzhiyun termfilter.clearFooter() 855*4882a593Smuzhiyun if params.observe_only: 856*4882a593Smuzhiyun print("\nKeyboard Interrupt, exiting observer...") 857*4882a593Smuzhiyun main.shutdown = 2 858*4882a593Smuzhiyun 859*4882a593Smuzhiyun def state_force_shutdown(): 860*4882a593Smuzhiyun print("\nSecond Keyboard Interrupt, stopping...\n") 861*4882a593Smuzhiyun _, error = server.runCommand(["stateForceShutdown"]) 862*4882a593Smuzhiyun if error: 863*4882a593Smuzhiyun logger.error("Unable to cleanly stop: %s" % error) 864*4882a593Smuzhiyun 865*4882a593Smuzhiyun if not params.observe_only and main.shutdown == 1: 866*4882a593Smuzhiyun state_force_shutdown() 867*4882a593Smuzhiyun 868*4882a593Smuzhiyun if not params.observe_only and main.shutdown == 0: 869*4882a593Smuzhiyun print("\nKeyboard Interrupt, closing down...\n") 870*4882a593Smuzhiyun interrupted = True 871*4882a593Smuzhiyun # Capture the second KeyboardInterrupt during stateShutdown is running 872*4882a593Smuzhiyun try: 873*4882a593Smuzhiyun _, error = server.runCommand(["stateShutdown"]) 874*4882a593Smuzhiyun if error: 875*4882a593Smuzhiyun logger.error("Unable to cleanly shutdown: %s" % error) 876*4882a593Smuzhiyun except KeyboardInterrupt: 877*4882a593Smuzhiyun state_force_shutdown() 878*4882a593Smuzhiyun 879*4882a593Smuzhiyun main.shutdown = main.shutdown + 1 880*4882a593Smuzhiyun except Exception as e: 881*4882a593Smuzhiyun import traceback 882*4882a593Smuzhiyun sys.stderr.write(traceback.format_exc()) 883*4882a593Smuzhiyun if not params.observe_only: 884*4882a593Smuzhiyun _, error = server.runCommand(["stateForceShutdown"]) 885*4882a593Smuzhiyun main.shutdown = 2 886*4882a593Smuzhiyun return_value = 1 887*4882a593Smuzhiyun try: 888*4882a593Smuzhiyun termfilter.clearFooter() 889*4882a593Smuzhiyun summary = "" 890*4882a593Smuzhiyun if taskfailures: 891*4882a593Smuzhiyun summary += pluralise("\nSummary: %s task failed:", 892*4882a593Smuzhiyun "\nSummary: %s tasks failed:", len(taskfailures)) 893*4882a593Smuzhiyun for failure in taskfailures: 894*4882a593Smuzhiyun summary += "\n %s" % failure 895*4882a593Smuzhiyun if warnings: 896*4882a593Smuzhiyun summary += pluralise("\nSummary: There was %s WARNING message.", 897*4882a593Smuzhiyun "\nSummary: There were %s WARNING messages.", warnings) 898*4882a593Smuzhiyun if return_value and errors: 899*4882a593Smuzhiyun summary += pluralise("\nSummary: There was %s ERROR message, returning a non-zero exit code.", 900*4882a593Smuzhiyun "\nSummary: There were %s ERROR messages, returning a non-zero exit code.", errors) 901*4882a593Smuzhiyun if summary and params.options.quiet == 0: 902*4882a593Smuzhiyun print(summary) 903*4882a593Smuzhiyun 904*4882a593Smuzhiyun if interrupted: 905*4882a593Smuzhiyun print("Execution was interrupted, returning a non-zero exit code.") 906*4882a593Smuzhiyun if return_value == 0: 907*4882a593Smuzhiyun return_value = 1 908*4882a593Smuzhiyun except IOError as e: 909*4882a593Smuzhiyun import errno 910*4882a593Smuzhiyun if e.errno == errno.EPIPE: 911*4882a593Smuzhiyun pass 912*4882a593Smuzhiyun 913*4882a593Smuzhiyun logging.shutdown() 914*4882a593Smuzhiyun 915*4882a593Smuzhiyun return return_value 916