Patchwork [bitbake-devel,v3,11/15] knotty: Merge knotty2 functionality into knotty

login
register
mail settings
Submitter Jason Wessel
Date June 6, 2012, 8:28 p.m.
Message ID <1339014519-9972-12-git-send-email-jason.wessel@windriver.com>
Download mbox | patch
Permalink /patch/29347/
State New
Headers show

Comments

Jason Wessel - June 6, 2012, 8:28 p.m.
The knotty 2 functionality can be activated in realtime
by pressing "t" for top mode or reverted with "0" for
normal mode.

Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
---
 lib/bb/ui/knotty.py |  172 ++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 150 insertions(+), 22 deletions(-)

Patch

diff --git a/lib/bb/ui/knotty.py b/lib/bb/ui/knotty.py
index 58fb8f1..765ad27 100644
--- a/lib/bb/ui/knotty.py
+++ b/lib/bb/ui/knotty.py
@@ -74,50 +74,156 @@  def pluralise(singular, plural, qty):
     else:
         return plural % qty
 
+class InteractConsoleLogFilter(logging.Filter):
+    def __init__(self, tf, format):
+        self.tf = tf
+        self.format = format
+
+    def filter(self, record):
+        if record.levelno == self.format.NOTE and (record.msg.startswith("Running") or record.msg.startswith("package ")):
+            return False
+        self.tf.clearFooter()
+        return True
+
 class TerminalFilter(object):
     def __init__(self, main, helper, console, format):
+        import curses
         self.main = main
+        self.console = console
         self.helper = helper
+        self.topMode = False
+        self.cuu = None
+        self.interactive = sys.stdout.isatty()
+        self.footer_present = False
+        self.lastpids = []
+        self.curses = curses
+        self.confilter = InteractConsoleLogFilter(self, format)
+        self.curses_init = False
+        self.columns = 80
+
+    def cursesInit(self):
+        if self.curses_init:
+            return
+        self.curses_init = True
+        if self.interactive:
+            try:
+                self.curses.setupterm()
+                self.ed = self.curses.tigetstr("ed")
+                if self.ed:
+                    self.cuu = self.curses.tigetstr("cuu")
+                self.columns = self.getTerminalColumns()
+                self.sigUpdate()
+            except Exception as e:
+                self.cuu = None
+
+    def setTopMode(self):
+        if self.topMode:
+            return
+        self.cursesInit()
+        self.topMode = True
+        self.console.addFilter(self.confilter)
+
+    def setNormalMode(self):
+        if not self.topMode:
+            return
+        self.topMode = False
+        try:
+            self.console.removeFilter(self.confilter)
+        except:
+            pass
 
     def clearFooter(self):
-        return
+        if not self.topMode:
+            return
+        if self.footer_present:
+            lines = self.footer_present
+            sys.stdout.write(self.curses.tparm(self.cuu, lines))
+            sys.stdout.write(self.curses.tparm(self.ed))
+        self.footer_present = False
 
-    def sigUpdate(self):
-        return
 
     def updateFooterForce(self):
+        self.footer_present = False
         self.printFooter(True)
 
     def updateFooter(self):
         self.printFooter(False)
 
     def printFooter(self, force_update):
-        if not force_update:
-            if not main.shutdown or not self.helper.needUpdate:
-                return
-
         activetasks = self.helper.running_tasks
+        failedtasks = self.helper.failed_tasks
         runningpids = self.helper.running_pids
+        if self.topMode:
+            if not self.cuu:
+                return
+            if self.footer_present and (self.lastpids == runningpids):
+                return
+            if self.footer_present:
+                self.clearFooter()
+        else:
+            if not force_update:
+                if not main.shutdown or not self.helper.needUpdate:
+                    return
+            if len(runningpids) == 0:
+                return
+            self.helper.getTasks()
 
-        if len(runningpids) == 0:
+        if not activetasks:
             return
-
-        self.helper.getTasks()
-
+        lines = 1
         tasks = []
         for t in runningpids:
             tasks.append("%s (pid %s)" % (activetasks[t]["title"], t))
 
-        if main.shutdown:
+        if self.main.shutdown:
             print("Waiting for %s running tasks to finish:" % len(activetasks))
         else:
-            print("Currently %s running tasks (%s of %s):" % (len(activetasks), self.helper.tasknumber_current, self.helper.tasknumber_total))
+            if self.helper.tasknumber_current == 0:
+                print("Currently %s running SetScene tasks (%s of %s):" % (len(activetasks), self.helper.sc_tasknumber_current, self.helper.sc_tasknumber_total))
+            else:
+                print("Currently %s running tasks (%s of %s):" % (len(activetasks), self.helper.tasknumber_current, self.helper.tasknumber_total))
         for tasknum, task in enumerate(tasks):
-            print("%s: %s" % (tasknum, task))
+            content = "%s: %s" % (tasknum, task)
+            print content
+            lines = lines + 1 + int(len(content) / (self.columns + 1))
+        self.footer_present = lines
+        self.lastpids = runningpids[:]
 
     def finish(self):
         return
 
+    def sigwinch_handle(self, sig, data):
+        self.columns = self.getTerminalColumns()
+
+    def sigUpdate(self):
+        import signal
+        if self.interactive and self.cuu:
+            signal.signal(signal.SIGWINCH, self.sigwinch_handle)
+
+    def getTerminalColumns(self):
+        def ioctl_GWINSZ(fd):
+            try:
+                import fcntl, termios, struct, os
+                cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
+            except:
+                return None
+            return cr
+        cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
+        if not cr:
+            try:
+                fd = os.open(os.ctermid(), os.O_RDONLY)
+                cr = ioctl_GWINSZ(fd)
+                os.close(fd)
+            except:
+                pass
+        if not cr:
+            try:
+                cr = (env['LINES'], env['COLUMNS'])
+            except:
+                cr = (25, 80)
+        return cr[1]
+
+
 class StdinMgr:
     def __init__(self):
         self.stdinbackup = None
@@ -145,23 +251,26 @@  class StdinMgr:
             termios.tcsetattr(self.fd, termios.TCSANOW, new)
 
 class RtLogLevel:
-    def __init__(self, handler, logfilter, mlt):
+    def __init__(self, handler, logfilter, mlt, tf):
         self.displaytail = False
         self.handler = handler
         self.logfilter = logfilter
         self.defaultLevel = logfilter.getFiltLevel()
         self.mlt = mlt
+        self.tf = tf
 
     def displayLogs(self):
         if self.displaytail:
             self.mlt.displayLogs()
 
     def setLevel(self, input, verbose):
-        if input == "1":
+        if input == "1" or input == "0":
             if verbose:
                 print "NOTE: Turning off real time log tail"
             self.logfilter.setFiltLevel(self.handler, self.defaultLevel)
             self.displaytail = False
+            if input == "0" and isinstance(self.tf, TerminalFilter):
+                self.tf.setNormalMode()
         elif input == "2":
             if verbose:
                 print "NOTE: Turning on real time log tail"
@@ -177,6 +286,23 @@  class RtLogLevel:
                 print "NOTE: Turning on DEBUG logging + real time log tail"
             self.logfilter.setFiltLevel(self.handler, logging.DEBUG)
             self.displaytail = True
+        elif input == "t":
+            if verbose:
+                print "NOTE: Activing task \"top\" mode"
+            if isinstance(self.tf, TerminalFilter):
+                self.tf.setTopMode()
+        elif input == "h":
+            print "============================================="
+            print "Interaction help commands:"
+            print " 0 - default with default logs"
+            print " 1 - turn off real time log tail"
+            print " 2 - turn on real time log tail"
+            print " 3 - turn on debug logging"
+            print " 4 - turn on debug logging and real time log tail"
+            print " t - Display tasks in \"top\" mode (formerly knotty2 mode)"
+            print " h - display commands"
+            return False
+        return True
 
 def main(server, eventHandler, tf = TerminalFilter):
 
@@ -201,10 +327,6 @@  def main(server, eventHandler, tf = TerminalFilter):
     console = logging.StreamHandler(sys.stdout)
     format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
     logfilter = bb.msg.addDefaultlogFilter(console)
-    rtloglevel = RtLogLevel(console, logfilter, mlt)
-    bb_rt_loglevel = server.runCommand(["getVariable", "BB_RT_LOGLEVEL"])
-    if bb_rt_loglevel and bb_rt_loglevel != "":
-        rtloglevel.setLevel(bb_rt_loglevel, False)
     console.setFormatter(format)
     logger.addHandler(console)
     if consolelogfile:
@@ -241,14 +363,20 @@  def main(server, eventHandler, tf = TerminalFilter):
     termfilter = tf(main, helper, console, format)
     stdin_mgr = StdinMgr()
 
+    rtloglevel = RtLogLevel(console, logfilter, mlt, termfilter)
+    bb_rt_loglevel = server.runCommand(["getVariable", "BB_RT_LOGLEVEL"])
+    if bb_rt_loglevel and bb_rt_loglevel != "":
+        for inputkey in bb_rt_loglevel:
+            rtloglevel.setLevel(inputkey, False)
     while True:
         try:
             termfilter.updateFooter()
             event = eventHandler.waitEvent(0.25)
             if stdin_mgr.poll():
                 keyinput = sys.stdin.read(1)
-                rtloglevel.setLevel(keyinput, True)
-                termfilter.updateFooterForce()
+                termfilter.clearFooter()
+                if (rtloglevel.setLevel(keyinput, True)):
+                    termfilter.updateFooterForce()
                 sys.stdout.flush()
 
             # Always try printing any accumulated log files first