knotty.py 29 KB

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