Skip to contentSkip to conversation

ui/knotty: Add a footer to the build output for interactive terminals

RFC PATCH New Richard Purdie 1 participant 1 message Nov 26, 2011

lore
Share & tools
lore
#0 lore raw

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
index 7c645ad..0f50ce4 100644
@@ -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