knotty.py 34 KB

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