knotty.py 29 KB

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