knotty.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. #
  2. # BitBake (No)TTY UI Implementation
  3. #
  4. # Handling output to TTYs or files (no TTY)
  5. #
  6. # Copyright (C) 2006-2012 Richard Purdie
  7. #
  8. # SPDX-License-Identifier: GPL-2.0-only
  9. #
  10. from __future__ import division
  11. import os
  12. import sys
  13. import xmlrpc.client as xmlrpclib
  14. import logging
  15. import progressbar
  16. import signal
  17. import bb.msg
  18. import time
  19. import fcntl
  20. import struct
  21. import copy
  22. import atexit
  23. from bb.ui import uihelper
  24. featureSet = [bb.cooker.CookerFeatures.SEND_SANITYEVENTS]
  25. logger = logging.getLogger("BitBake")
  26. interactive = sys.stdout.isatty()
  27. class BBProgress(progressbar.ProgressBar):
  28. def __init__(self, msg, maxval, widgets=None, extrapos=-1, resize_handler=None):
  29. self.msg = msg
  30. self.extrapos = extrapos
  31. if not widgets:
  32. widgets = [progressbar.Percentage(), ' ', progressbar.Bar(), ' ',
  33. progressbar.ETA()]
  34. self.extrapos = 4
  35. if resize_handler:
  36. self._resize_default = resize_handler
  37. else:
  38. self._resize_default = signal.getsignal(signal.SIGWINCH)
  39. progressbar.ProgressBar.__init__(self, maxval, [self.msg + ": "] + widgets, fd=sys.stdout)
  40. def _handle_resize(self, signum=None, frame=None):
  41. progressbar.ProgressBar._handle_resize(self, signum, frame)
  42. if self._resize_default:
  43. self._resize_default(signum, frame)
  44. def finish(self):
  45. progressbar.ProgressBar.finish(self)
  46. if self._resize_default:
  47. signal.signal(signal.SIGWINCH, self._resize_default)
  48. def setmessage(self, msg):
  49. self.msg = msg
  50. self.widgets[0] = msg
  51. def setextra(self, extra):
  52. if self.extrapos > -1:
  53. if extra:
  54. extrastr = str(extra)
  55. if extrastr[0] != ' ':
  56. extrastr = ' ' + extrastr
  57. else:
  58. extrastr = ''
  59. self.widgets[self.extrapos] = extrastr
  60. def _need_update(self):
  61. # We always want the bar to print when update() is called
  62. return True
  63. class NonInteractiveProgress(object):
  64. fobj = sys.stdout
  65. def __init__(self, msg, maxval):
  66. self.msg = msg
  67. self.maxval = maxval
  68. self.finished = False
  69. def start(self, update=True):
  70. self.fobj.write("%s..." % self.msg)
  71. self.fobj.flush()
  72. return self
  73. def update(self, value):
  74. pass
  75. def finish(self):
  76. if self.finished:
  77. return
  78. self.fobj.write("done.\n")
  79. self.fobj.flush()
  80. self.finished = True
  81. def new_progress(msg, maxval):
  82. if interactive:
  83. return BBProgress(msg, maxval)
  84. else:
  85. return NonInteractiveProgress(msg, maxval)
  86. def pluralise(singular, plural, qty):
  87. if(qty == 1):
  88. return singular % qty
  89. else:
  90. return plural % qty
  91. class InteractConsoleLogFilter(logging.Filter):
  92. def __init__(self, tf, format):
  93. self.tf = tf
  94. self.format = format
  95. def filter(self, record):
  96. if record.levelno == self.format.NOTE and (record.msg.startswith("Running") or record.msg.startswith("recipe ")):
  97. return False
  98. self.tf.clearFooter()
  99. return True
  100. class TerminalFilter(object):
  101. rows = 25
  102. columns = 80
  103. def sigwinch_handle(self, signum, frame):
  104. self.rows, self.columns = self.getTerminalColumns()
  105. if self._sigwinch_default:
  106. self._sigwinch_default(signum, frame)
  107. def getTerminalColumns(self):
  108. def ioctl_GWINSZ(fd):
  109. try:
  110. cr = struct.unpack('hh', fcntl.ioctl(fd, self.termios.TIOCGWINSZ, '1234'))
  111. except:
  112. return None
  113. return cr
  114. cr = ioctl_GWINSZ(sys.stdout.fileno())
  115. if not cr:
  116. try:
  117. fd = os.open(os.ctermid(), os.O_RDONLY)
  118. cr = ioctl_GWINSZ(fd)
  119. os.close(fd)
  120. except:
  121. pass
  122. if not cr:
  123. try:
  124. cr = (env['LINES'], env['COLUMNS'])
  125. except:
  126. cr = (25, 80)
  127. return cr
  128. def __init__(self, main, helper, console, errconsole, format, quiet):
  129. self.main = main
  130. self.helper = helper
  131. self.cuu = None
  132. self.stdinbackup = None
  133. self.interactive = sys.stdout.isatty()
  134. self.footer_present = False
  135. self.lastpids = []
  136. self.lasttime = None
  137. self.quiet = quiet
  138. if not self.interactive:
  139. return
  140. try:
  141. import curses
  142. except ImportError:
  143. sys.exit("FATAL: The knotty ui could not load the required curses python module.")
  144. import termios
  145. self.curses = curses
  146. self.termios = termios
  147. try:
  148. fd = sys.stdin.fileno()
  149. self.stdinbackup = termios.tcgetattr(fd)
  150. new = copy.deepcopy(self.stdinbackup)
  151. new[3] = new[3] & ~termios.ECHO
  152. termios.tcsetattr(fd, termios.TCSADRAIN, new)
  153. curses.setupterm()
  154. if curses.tigetnum("colors") > 2:
  155. format.enable_color()
  156. self.ed = curses.tigetstr("ed")
  157. if self.ed:
  158. self.cuu = curses.tigetstr("cuu")
  159. try:
  160. self._sigwinch_default = signal.getsignal(signal.SIGWINCH)
  161. signal.signal(signal.SIGWINCH, self.sigwinch_handle)
  162. except:
  163. pass
  164. self.rows, self.columns = self.getTerminalColumns()
  165. except:
  166. self.cuu = None
  167. if not self.cuu:
  168. self.interactive = False
  169. bb.note("Unable to use interactive mode for this terminal, using fallback")
  170. return
  171. if console:
  172. console.addFilter(InteractConsoleLogFilter(self, format))
  173. if errconsole:
  174. errconsole.addFilter(InteractConsoleLogFilter(self, format))
  175. self.main_progress = None
  176. def clearFooter(self):
  177. if self.footer_present:
  178. lines = self.footer_present
  179. sys.stdout.buffer.write(self.curses.tparm(self.cuu, lines))
  180. sys.stdout.buffer.write(self.curses.tparm(self.ed))
  181. sys.stdout.flush()
  182. self.footer_present = False
  183. def updateFooter(self):
  184. if not self.cuu:
  185. return
  186. activetasks = self.helper.running_tasks
  187. failedtasks = self.helper.failed_tasks
  188. runningpids = self.helper.running_pids
  189. currenttime = time.time()
  190. if not self.lasttime or (currenttime - self.lasttime > 5):
  191. self.helper.needUpdate = True
  192. self.lasttime = currenttime
  193. if self.footer_present and not self.helper.needUpdate:
  194. return
  195. self.helper.needUpdate = False
  196. if self.footer_present:
  197. self.clearFooter()
  198. if (not self.helper.tasknumber_total or self.helper.tasknumber_current == self.helper.tasknumber_total) and not len(activetasks):
  199. return
  200. tasks = []
  201. for t in runningpids:
  202. progress = activetasks[t].get("progress", None)
  203. if progress is not None:
  204. pbar = activetasks[t].get("progressbar", None)
  205. rate = activetasks[t].get("rate", None)
  206. start_time = activetasks[t].get("starttime", None)
  207. if not pbar or pbar.bouncing != (progress < 0):
  208. if progress < 0:
  209. pbar = BBProgress("0: %s (pid %s) " % (activetasks[t]["title"], t), 100, widgets=[progressbar.BouncingSlider(), ''], extrapos=2, resize_handler=self.sigwinch_handle)
  210. pbar.bouncing = True
  211. else:
  212. pbar = BBProgress("0: %s (pid %s) " % (activetasks[t]["title"], t), 100, widgets=[progressbar.Percentage(), ' ', progressbar.Bar(), ''], extrapos=4, resize_handler=self.sigwinch_handle)
  213. pbar.bouncing = False
  214. activetasks[t]["progressbar"] = pbar
  215. tasks.append((pbar, progress, rate, start_time))
  216. else:
  217. start_time = activetasks[t].get("starttime", None)
  218. if start_time:
  219. tasks.append("%s - %ds (pid %s)" % (activetasks[t]["title"], currenttime - start_time, t))
  220. else:
  221. tasks.append("%s (pid %s)" % (activetasks[t]["title"], t))
  222. if self.main.shutdown:
  223. content = "Waiting for %s running tasks to finish:" % len(activetasks)
  224. print(content)
  225. else:
  226. if self.quiet:
  227. content = "Running tasks (%s of %s)" % (self.helper.tasknumber_current, self.helper.tasknumber_total)
  228. elif not len(activetasks):
  229. content = "No currently running tasks (%s of %s)" % (self.helper.tasknumber_current, self.helper.tasknumber_total)
  230. else:
  231. content = "Currently %2s running tasks (%s of %s)" % (len(activetasks), self.helper.tasknumber_current, self.helper.tasknumber_total)
  232. maxtask = self.helper.tasknumber_total
  233. if not self.main_progress or self.main_progress.maxval != maxtask:
  234. widgets = [' ', progressbar.Percentage(), ' ', progressbar.Bar()]
  235. self.main_progress = BBProgress("Running tasks", maxtask, widgets=widgets, resize_handler=self.sigwinch_handle)
  236. self.main_progress.start(False)
  237. self.main_progress.setmessage(content)
  238. progress = self.helper.tasknumber_current - 1
  239. if progress < 0:
  240. progress = 0
  241. content = self.main_progress.update(progress)
  242. print('')
  243. lines = 1 + int(len(content) / (self.columns + 1))
  244. if self.quiet == 0:
  245. for tasknum, task in enumerate(tasks[:(self.rows - 2)]):
  246. if isinstance(task, tuple):
  247. pbar, progress, rate, start_time = task
  248. if not pbar.start_time:
  249. pbar.start(False)
  250. if start_time:
  251. pbar.start_time = start_time
  252. pbar.setmessage('%s:%s' % (tasknum, pbar.msg.split(':', 1)[1]))
  253. if progress > -1:
  254. pbar.setextra(rate)
  255. content = pbar.update(progress)
  256. else:
  257. content = pbar.update(1)
  258. print('')
  259. else:
  260. content = "%s: %s" % (tasknum, task)
  261. print(content)
  262. lines = lines + 1 + int(len(content) / (self.columns + 1))
  263. self.footer_present = lines
  264. self.lastpids = runningpids[:]
  265. self.lastcount = self.helper.tasknumber_current
  266. def finish(self):
  267. if self.stdinbackup:
  268. fd = sys.stdin.fileno()
  269. self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup)
  270. def print_event_log(event, includelogs, loglines, termfilter):
  271. # FIXME refactor this out further
  272. logfile = event.logfile
  273. if logfile and os.path.exists(logfile):
  274. termfilter.clearFooter()
  275. bb.error("Logfile of failure stored in: %s" % logfile)
  276. if includelogs and not event.errprinted:
  277. print("Log data follows:")
  278. f = open(logfile, "r")
  279. lines = []
  280. while True:
  281. l = f.readline()
  282. if l == '':
  283. break
  284. l = l.rstrip()
  285. if loglines:
  286. lines.append(' | %s' % l)
  287. if len(lines) > int(loglines):
  288. lines.pop(0)
  289. else:
  290. print('| %s' % l)
  291. f.close()
  292. if lines:
  293. for line in lines:
  294. print(line)
  295. def _log_settings_from_server(server, observe_only):
  296. # Get values of variables which control our output
  297. includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
  298. if error:
  299. logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error)
  300. raise BaseException(error)
  301. loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
  302. if error:
  303. logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error)
  304. raise BaseException(error)
  305. if observe_only:
  306. cmd = 'getVariable'
  307. else:
  308. cmd = 'getSetVariable'
  309. consolelogfile, error = server.runCommand([cmd, "BB_CONSOLELOG"])
  310. if error:
  311. logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error)
  312. raise BaseException(error)
  313. return includelogs, loglines, consolelogfile
  314. _evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord",
  315. "bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted",
  316. "bb.event.ParseProgress", "bb.event.ParseCompleted", "bb.event.CacheLoadStarted",
  317. "bb.event.CacheLoadProgress", "bb.event.CacheLoadCompleted", "bb.command.CommandFailed",
  318. "bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit",
  319. "bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted",
  320. "bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed",
  321. "bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent",
  322. "bb.build.TaskProgress", "bb.event.ProcessStarted", "bb.event.ProcessProgress", "bb.event.ProcessFinished"]
  323. def main(server, eventHandler, params, tf = TerminalFilter):
  324. if not params.observe_only:
  325. params.updateToServer(server, os.environ.copy())
  326. includelogs, loglines, consolelogfile = _log_settings_from_server(server, params.observe_only)
  327. if sys.stdin.isatty() and sys.stdout.isatty():
  328. log_exec_tty = True
  329. else:
  330. log_exec_tty = False
  331. helper = uihelper.BBUIHelper()
  332. console = logging.StreamHandler(sys.stdout)
  333. errconsole = logging.StreamHandler(sys.stderr)
  334. format_str = "%(levelname)s: %(message)s"
  335. format = bb.msg.BBLogFormatter(format_str)
  336. if params.options.quiet == 0:
  337. forcelevel = None
  338. elif params.options.quiet > 2:
  339. forcelevel = bb.msg.BBLogFormatter.ERROR
  340. else:
  341. forcelevel = bb.msg.BBLogFormatter.WARNING
  342. bb.msg.addDefaultlogFilter(console, bb.msg.BBLogFilterStdOut, forcelevel)
  343. bb.msg.addDefaultlogFilter(errconsole, bb.msg.BBLogFilterStdErr)
  344. console.setFormatter(format)
  345. errconsole.setFormatter(format)
  346. if not bb.msg.has_console_handler(logger):
  347. logger.addHandler(console)
  348. logger.addHandler(errconsole)
  349. bb.utils.set_process_name("KnottyUI")
  350. if params.options.remote_server and params.options.kill_server:
  351. server.terminateServer()
  352. return
  353. consolelog = None
  354. if consolelogfile and not params.options.show_environment and not params.options.show_versions:
  355. bb.utils.mkdirhier(os.path.dirname(consolelogfile))
  356. conlogformat = bb.msg.BBLogFormatter(format_str)
  357. consolelog = logging.FileHandler(consolelogfile)
  358. bb.msg.addDefaultlogFilter(consolelog)
  359. consolelog.setFormatter(conlogformat)
  360. logger.addHandler(consolelog)
  361. loglink = os.path.join(os.path.dirname(consolelogfile), 'console-latest.log')
  362. bb.utils.remove(loglink)
  363. try:
  364. os.symlink(os.path.basename(consolelogfile), loglink)
  365. except OSError:
  366. pass
  367. llevel, debug_domains = bb.msg.constructLogOptions()
  368. server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
  369. universe = False
  370. if not params.observe_only:
  371. params.updateFromServer(server)
  372. cmdline = params.parseActions()
  373. if not cmdline:
  374. print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
  375. return 1
  376. if 'msg' in cmdline and cmdline['msg']:
  377. logger.error(cmdline['msg'])
  378. return 1
  379. if cmdline['action'][0] == "buildTargets" and "universe" in cmdline['action'][1]:
  380. universe = True
  381. ret, error = server.runCommand(cmdline['action'])
  382. if error:
  383. logger.error("Command '%s' failed: %s" % (cmdline, error))
  384. return 1
  385. elif ret != True:
  386. logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
  387. return 1
  388. parseprogress = None
  389. cacheprogress = None
  390. main.shutdown = 0
  391. interrupted = False
  392. return_value = 0
  393. errors = 0
  394. warnings = 0
  395. taskfailures = []
  396. termfilter = tf(main, helper, console, errconsole, format, params.options.quiet)
  397. atexit.register(termfilter.finish)
  398. while True:
  399. try:
  400. event = eventHandler.waitEvent(0)
  401. if event is None:
  402. if main.shutdown > 1:
  403. break
  404. termfilter.updateFooter()
  405. event = eventHandler.waitEvent(0.25)
  406. if event is None:
  407. continue
  408. helper.eventHandler(event)
  409. if isinstance(event, bb.runqueue.runQueueExitWait):
  410. if not main.shutdown:
  411. main.shutdown = 1
  412. continue
  413. if isinstance(event, bb.event.LogExecTTY):
  414. if log_exec_tty:
  415. tries = event.retries
  416. while tries:
  417. print("Trying to run: %s" % event.prog)
  418. if os.system(event.prog) == 0:
  419. break
  420. time.sleep(event.sleep_delay)
  421. tries -= 1
  422. if tries:
  423. continue
  424. logger.warning(event.msg)
  425. continue
  426. if isinstance(event, logging.LogRecord):
  427. if event.levelno >= format.ERROR:
  428. errors = errors + 1
  429. return_value = 1
  430. elif event.levelno == format.WARNING:
  431. warnings = warnings + 1
  432. if event.taskpid != 0:
  433. # For "normal" logging conditions, don't show note logs from tasks
  434. # but do show them if the user has changed the default log level to
  435. # include verbose/debug messages
  436. if event.levelno <= format.NOTE and (event.levelno < llevel or (event.levelno == format.NOTE and llevel != format.VERBOSE)):
  437. continue
  438. # Prefix task messages with recipe/task
  439. if event.taskpid in helper.running_tasks and event.levelno != format.PLAIN:
  440. taskinfo = helper.running_tasks[event.taskpid]
  441. event.msg = taskinfo['title'] + ': ' + event.msg
  442. if hasattr(event, 'fn'):
  443. event.msg = event.fn + ': ' + event.msg
  444. logger.handle(event)
  445. continue
  446. if isinstance(event, bb.build.TaskFailedSilent):
  447. logger.warning("Logfile for failed setscene task is %s" % event.logfile)
  448. continue
  449. if isinstance(event, bb.build.TaskFailed):
  450. return_value = 1
  451. print_event_log(event, includelogs, loglines, termfilter)
  452. if isinstance(event, bb.build.TaskBase):
  453. logger.info(event._message)
  454. continue
  455. if isinstance(event, bb.event.ParseStarted):
  456. if params.options.quiet > 1:
  457. continue
  458. if event.total == 0:
  459. continue
  460. parseprogress = new_progress("Parsing recipes", event.total).start()
  461. continue
  462. if isinstance(event, bb.event.ParseProgress):
  463. if params.options.quiet > 1:
  464. continue
  465. if parseprogress:
  466. parseprogress.update(event.current)
  467. else:
  468. bb.warn("Got ParseProgress event for parsing that never started?")
  469. continue
  470. if isinstance(event, bb.event.ParseCompleted):
  471. if params.options.quiet > 1:
  472. continue
  473. if not parseprogress:
  474. continue
  475. parseprogress.finish()
  476. pasreprogress = None
  477. if params.options.quiet == 0:
  478. print(("Parsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors."
  479. % ( event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors)))
  480. continue
  481. if isinstance(event, bb.event.CacheLoadStarted):
  482. if params.options.quiet > 1:
  483. continue
  484. cacheprogress = new_progress("Loading cache", event.total).start()
  485. continue
  486. if isinstance(event, bb.event.CacheLoadProgress):
  487. if params.options.quiet > 1:
  488. continue
  489. cacheprogress.update(event.current)
  490. continue
  491. if isinstance(event, bb.event.CacheLoadCompleted):
  492. if params.options.quiet > 1:
  493. continue
  494. cacheprogress.finish()
  495. if params.options.quiet == 0:
  496. print("Loaded %d entries from dependency cache." % event.num_entries)
  497. continue
  498. if isinstance(event, bb.command.CommandFailed):
  499. return_value = event.exitcode
  500. if event.error:
  501. errors = errors + 1
  502. logger.error(str(event))
  503. main.shutdown = 2
  504. continue
  505. if isinstance(event, bb.command.CommandExit):
  506. if not return_value:
  507. return_value = event.exitcode
  508. continue
  509. if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)):
  510. main.shutdown = 2
  511. continue
  512. if isinstance(event, bb.event.MultipleProviders):
  513. logger.info(str(event))
  514. continue
  515. if isinstance(event, bb.event.NoProvider):
  516. # For universe builds, only show these as warnings, not errors
  517. if not universe:
  518. return_value = 1
  519. errors = errors + 1
  520. logger.error(str(event))
  521. else:
  522. logger.warning(str(event))
  523. continue
  524. if isinstance(event, bb.runqueue.sceneQueueTaskStarted):
  525. logger.info("Running setscene task %d of %d (%s)" % (event.stats.completed + event.stats.active + event.stats.failed + 1, event.stats.total, event.taskstring))
  526. continue
  527. if isinstance(event, bb.runqueue.runQueueTaskStarted):
  528. if event.noexec:
  529. tasktype = 'noexec task'
  530. else:
  531. tasktype = 'task'
  532. logger.info("Running %s %d of %d (%s)",
  533. tasktype,
  534. event.stats.completed + event.stats.active +
  535. event.stats.failed + 1,
  536. event.stats.total, event.taskstring)
  537. continue
  538. if isinstance(event, bb.runqueue.runQueueTaskFailed):
  539. return_value = 1
  540. taskfailures.append(event.taskstring)
  541. logger.error(str(event))
  542. continue
  543. if isinstance(event, bb.runqueue.sceneQueueTaskFailed):
  544. logger.warning(str(event))
  545. continue
  546. if isinstance(event, bb.event.DepTreeGenerated):
  547. continue
  548. if isinstance(event, bb.event.ProcessStarted):
  549. if params.options.quiet > 1:
  550. continue
  551. parseprogress = new_progress(event.processname, event.total)
  552. parseprogress.start(False)
  553. continue
  554. if isinstance(event, bb.event.ProcessProgress):
  555. if params.options.quiet > 1:
  556. continue
  557. if parseprogress:
  558. parseprogress.update(event.progress)
  559. else:
  560. bb.warn("Got ProcessProgress event for someting that never started?")
  561. continue
  562. if isinstance(event, bb.event.ProcessFinished):
  563. if params.options.quiet > 1:
  564. continue
  565. if parseprogress:
  566. parseprogress.finish()
  567. parseprogress = None
  568. continue
  569. # ignore
  570. if isinstance(event, (bb.event.BuildBase,
  571. bb.event.MetadataEvent,
  572. bb.event.StampUpdate,
  573. bb.event.ConfigParsed,
  574. bb.event.MultiConfigParsed,
  575. bb.event.RecipeParsed,
  576. bb.event.RecipePreFinalise,
  577. bb.runqueue.runQueueEvent,
  578. bb.event.OperationStarted,
  579. bb.event.OperationCompleted,
  580. bb.event.OperationProgress,
  581. bb.event.DiskFull,
  582. bb.event.HeartbeatEvent,
  583. bb.build.TaskProgress)):
  584. continue
  585. logger.error("Unknown event: %s", event)
  586. except EnvironmentError as ioerror:
  587. termfilter.clearFooter()
  588. # ignore interrupted io
  589. if ioerror.args[0] == 4:
  590. continue
  591. sys.stderr.write(str(ioerror))
  592. if not params.observe_only:
  593. _, error = server.runCommand(["stateForceShutdown"])
  594. main.shutdown = 2
  595. except KeyboardInterrupt:
  596. termfilter.clearFooter()
  597. if params.observe_only:
  598. print("\nKeyboard Interrupt, exiting observer...")
  599. main.shutdown = 2
  600. if not params.observe_only and main.shutdown == 1:
  601. print("\nSecond Keyboard Interrupt, stopping...\n")
  602. _, error = server.runCommand(["stateForceShutdown"])
  603. if error:
  604. logger.error("Unable to cleanly stop: %s" % error)
  605. if not params.observe_only and main.shutdown == 0:
  606. print("\nKeyboard Interrupt, closing down...\n")
  607. interrupted = True
  608. _, error = server.runCommand(["stateShutdown"])
  609. if error:
  610. logger.error("Unable to cleanly shutdown: %s" % error)
  611. main.shutdown = main.shutdown + 1
  612. pass
  613. except Exception as e:
  614. import traceback
  615. sys.stderr.write(traceback.format_exc())
  616. if not params.observe_only:
  617. _, error = server.runCommand(["stateForceShutdown"])
  618. main.shutdown = 2
  619. return_value = 1
  620. try:
  621. termfilter.clearFooter()
  622. summary = ""
  623. if taskfailures:
  624. summary += pluralise("\nSummary: %s task failed:",
  625. "\nSummary: %s tasks failed:", len(taskfailures))
  626. for failure in taskfailures:
  627. summary += "\n %s" % failure
  628. if warnings:
  629. summary += pluralise("\nSummary: There was %s WARNING message shown.",
  630. "\nSummary: There were %s WARNING messages shown.", warnings)
  631. if return_value and errors:
  632. summary += pluralise("\nSummary: There was %s ERROR message shown, returning a non-zero exit code.",
  633. "\nSummary: There were %s ERROR messages shown, returning a non-zero exit code.", errors)
  634. if summary and params.options.quiet == 0:
  635. print(summary)
  636. if interrupted:
  637. print("Execution was interrupted, returning a non-zero exit code.")
  638. if return_value == 0:
  639. return_value = 1
  640. except IOError as e:
  641. import errno
  642. if e.errno == errno.EPIPE:
  643. pass
  644. if consolelog:
  645. logger.removeHandler(consolelog)
  646. consolelog.close()
  647. return return_value