ui/knotty: Add a footer to the build output for interactive terminals
RFC PATCH 1/1 ui/knotty: Add a footer to the build output for interactive terminals
On terminals which support it, add summary information to the end of the build output about the number of tasks currently running and how many tasks we've run so far.
This provides a summary at a glace of what the current state of the build is and what the build is currently doing which is lacking in the current UI.
Also disable echo of characters on stdin since this corrupts the disable, particularly Crtl+C.
The "waiting for X tasks" code can be merged into this code too since that is only useful on interactive terminals and this improves the readability of that output too.
---
This is something I've been experimenting with. I'd appreciate feedback on whether people feel this is an improvement over the current terminal output. Currently the existing output continues to scroll above the footer. We could cut down on the number of log messages going there though so warnings and errors would become more visible. Full logs would remain in the BB_CONSOLELOG file on disk for reference.
bitbake/lib/bb/ui/knotty.py +57 -6
@@ -31,6 +31,25 @@ from bb.ui import uihelper logger = logging.getLogger("BitBake") interactive = sys.stdout.isatty() +cuu = None +stdinbackup = None +if interactive: + import curses + import termios + import copy + try: + fd = sys.stdin.fileno() + stdinbackup = termios.tcgetattr(fd) + new = copy.deepcopy(stdinbackup) + new[3] = new[3] & ~termios.ECHO + termios.tcsetattr(fd, termios.TCSADRAIN, new) + curses.setupterm() + ed = curses.tigetstr("ed") + if ed: + cuu = curses.tigetstr("cuu") + except: + cuu = None + class BBProgress(progressbar.ProgressBar): def __init__(self, msg, maxval): self.msg = msg
@@ -99,21 +124,49 @@ def main(server, eventHandler): cacheprogress = None shutdown = 0 return_value = 0 + main.footer_present = False + main.footer_update = True + + def updateFooter(): + if not main.footer_update: + return + if main.footer_present: + clearFooter() + if not cuu: + return + activetasks = helper.running_tasks + failedtasks = helper.failed_tasks + if not activetasks: + return + lines = 1 + if shutdown: + print("Waiting for %s running tasks to finish:" % len(activetasks)) + else: + print("Currently %s running tasks (%s of %s):" % (len(activetasks), helper.tasknumber_current, helper.tasknumber_total)) + for tasknum, task in enumerate(activetasks): + print("%s: %s (pid %s)" % (tasknum, activetasks[task]["title"], task)) + lines = lines + 1 + main.footer_present = lines + + def clearFooter(): + if main.footer_present: + lines = main.footer_present + sys.stdout.write(curses.tparm(cuu, lines)) + sys.stdout.write(curses.tparm(ed)) + main.footer_present = False + main.footer_update = True + while True: try: + updateFooter() event = eventHandler.waitEvent(0.25) if event is None: continue + clearFooter() helper.eventHandler(event) if isinstance(event, bb.runqueue.runQueueExitWait): if not shutdown: shutdown = 1 - if shutdown and helper.needUpdate: - activetasks, failedtasks = helper.getTasks() - if activetasks: - print("Waiting for %s active tasks to finish:" % len(activetasks)) - for tasknum, task in enumerate(activetasks): - print("%s: %s (pid %s)" % (tasknum, activetasks[task]["title"], task)) if isinstance(event, logging.LogRecord): if event.levelno >= format.ERROR:
@@ -246,10 +299,12 @@ def main(server, eventHandler): logger.error("Unknown event: %s", event) except EnvironmentError as ioerror: + clearFooter() # ignore interrupted io if ioerror.args[0] == 4: pass except KeyboardInterrupt: + clearFooter() if shutdown == 2: print("\nThird Keyboard Interrupt, exit.\n") break
@@ -261,4 +316,6 @@ def main(server, eventHandler): server.runCommand(["stateShutdown"]) shutdown = shutdown + 1 pass + if stdinbackup: + termios.tcsetattr(fd, termios.TCSADRAIN, stdinbackup) return return_value