summaryrefslogtreecommitdiff
path: root/bitbake-dev/lib/bb/ui
diff options
context:
space:
mode:
authorRichard Purdie <richard@openedhand.com>2008-09-30 15:08:33 +0000
committerRichard Purdie <richard@openedhand.com>2008-09-30 15:08:33 +0000
commitc30eddb243e7e65f67f656e62848a033cf6f2e5c (patch)
tree110dd95788b76f55d31cb8d30aac2de8400b6f4a /bitbake-dev/lib/bb/ui
parent5ef0510474004eeb2ae8a99b64e2febb1920e077 (diff)
downloadopenembedded-core-c30eddb243e7e65f67f656e62848a033cf6f2e5c.tar.gz
openembedded-core-c30eddb243e7e65f67f656e62848a033cf6f2e5c.tar.bz2
openembedded-core-c30eddb243e7e65f67f656e62848a033cf6f2e5c.zip
Add bitbake-dev to allow ease of testing and development of bitbake trunk
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@5337 311d38ba-8fff-0310-9ca6-ca027cbcb966
Diffstat (limited to 'bitbake-dev/lib/bb/ui')
-rw-r--r--bitbake-dev/lib/bb/ui/__init__.py18
-rw-r--r--bitbake-dev/lib/bb/ui/depexplorer.py271
-rw-r--r--bitbake-dev/lib/bb/ui/knotty.py157
-rw-r--r--bitbake-dev/lib/bb/ui/ncurses.py333
-rw-r--r--bitbake-dev/lib/bb/ui/uievent.py127
-rw-r--r--bitbake-dev/lib/bb/ui/uihelper.py49
6 files changed, 955 insertions, 0 deletions
diff --git a/bitbake-dev/lib/bb/ui/__init__.py b/bitbake-dev/lib/bb/ui/__init__.py
new file mode 100644
index 0000000000..c6a377a8e6
--- /dev/null
+++ b/bitbake-dev/lib/bb/ui/__init__.py
@@ -0,0 +1,18 @@
+#
+# BitBake UI Implementation
+#
+# Copyright (C) 2006-2007 Richard Purdie
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
diff --git a/bitbake-dev/lib/bb/ui/depexplorer.py b/bitbake-dev/lib/bb/ui/depexplorer.py
new file mode 100644
index 0000000000..becbb5dd5d
--- /dev/null
+++ b/bitbake-dev/lib/bb/ui/depexplorer.py
@@ -0,0 +1,271 @@
+#
+# BitBake Graphical GTK based Dependency Explorer
+#
+# Copyright (C) 2007 Ross Burton
+# Copyright (C) 2007 - 2008 Richard Purdie
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import gobject
+import gtk
+import threading
+
+# Package Model
+(COL_PKG_NAME) = (0)
+
+# Dependency Model
+(TYPE_DEP, TYPE_RDEP) = (0, 1)
+(COL_DEP_TYPE, COL_DEP_PARENT, COL_DEP_PACKAGE) = (0, 1, 2)
+
+class PackageDepView(gtk.TreeView):
+ def __init__(self, model, dep_type, label):
+ gtk.TreeView.__init__(self)
+ self.current = None
+ self.dep_type = dep_type
+ self.filter_model = model.filter_new()
+ self.filter_model.set_visible_func(self._filter)
+ self.set_model(self.filter_model)
+ #self.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
+ self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PACKAGE))
+
+ def _filter(self, model, iter):
+ (this_type, package) = model.get(iter, COL_DEP_TYPE, COL_DEP_PARENT)
+ if this_type != self.dep_type: return False
+ return package == self.current
+
+ def set_current_package(self, package):
+ self.current = package
+ self.filter_model.refilter()
+
+class PackageReverseDepView(gtk.TreeView):
+ def __init__(self, model, label):
+ gtk.TreeView.__init__(self)
+ self.current = None
+ self.filter_model = model.filter_new()
+ self.filter_model.set_visible_func(self._filter)
+ self.set_model(self.filter_model)
+ self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PARENT))
+
+ def _filter(self, model, iter):
+ package = model.get_value(iter, COL_DEP_PACKAGE)
+ return package == self.current
+
+ def set_current_package(self, package):
+ self.current = package
+ self.filter_model.refilter()
+
+class DepExplorer(gtk.Window):
+ def __init__(self):
+ gtk.Window.__init__(self)
+ self.set_title("Dependency Explorer")
+ self.set_default_size(500, 500)
+ self.connect("delete-event", gtk.main_quit)
+
+ # Create the data models
+ self.pkg_model = gtk.ListStore(gobject.TYPE_STRING)
+ self.depends_model = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING)
+
+ pane = gtk.HPaned()
+ pane.set_position(250)
+ self.add(pane)
+
+ # The master list of packages
+ scrolled = gtk.ScrolledWindow()
+ scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolled.set_shadow_type(gtk.SHADOW_IN)
+ self.pkg_treeview = gtk.TreeView(self.pkg_model)
+ self.pkg_treeview.get_selection().connect("changed", self.on_cursor_changed)
+ self.pkg_treeview.append_column(gtk.TreeViewColumn("Package", gtk.CellRendererText(), text=COL_PKG_NAME))
+ pane.add1(scrolled)
+ scrolled.add(self.pkg_treeview)
+
+ box = gtk.VBox(homogeneous=True, spacing=4)
+
+ # Runtime Depends
+ scrolled = gtk.ScrolledWindow()
+ scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolled.set_shadow_type(gtk.SHADOW_IN)
+ self.rdep_treeview = PackageDepView(self.depends_model, TYPE_RDEP, "Runtime Depends")
+ self.rdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
+ scrolled.add(self.rdep_treeview)
+ box.add(scrolled)
+
+ # Build Depends
+ scrolled = gtk.ScrolledWindow()
+ scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolled.set_shadow_type(gtk.SHADOW_IN)
+ self.dep_treeview = PackageDepView(self.depends_model, TYPE_DEP, "Build Depends")
+ self.dep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
+ scrolled.add(self.dep_treeview)
+ box.add(scrolled)
+ pane.add2(box)
+
+ # Reverse Depends
+ scrolled = gtk.ScrolledWindow()
+ scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolled.set_shadow_type(gtk.SHADOW_IN)
+ self.revdep_treeview = PackageReverseDepView(self.depends_model, "Reverse Depends")
+ self.revdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PARENT)
+ scrolled.add(self.revdep_treeview)
+ box.add(scrolled)
+ pane.add2(box)
+
+ self.show_all()
+
+ def on_package_activated(self, treeview, path, column, data_col):
+ model = treeview.get_model()
+ package = model.get_value(model.get_iter(path), data_col)
+
+ pkg_path = []
+ def finder(model, path, iter, needle):
+ package = model.get_value(iter, COL_PKG_NAME)
+ if package == needle:
+ pkg_path.append(path)
+ return True
+ else:
+ return False
+ self.pkg_model.foreach(finder, package)
+ if pkg_path:
+ self.pkg_treeview.get_selection().select_path(pkg_path[0])
+ self.pkg_treeview.scroll_to_cell(pkg_path[0])
+
+ def on_cursor_changed(self, selection):
+ (model, it) = selection.get_selected()
+ if iter is None:
+ current_package = None
+ else:
+ current_package = model.get_value(it, COL_PKG_NAME)
+ self.rdep_treeview.set_current_package(current_package)
+ self.dep_treeview.set_current_package(current_package)
+ self.revdep_treeview.set_current_package(current_package)
+
+
+def parse(depgraph, pkg_model, depends_model):
+
+ for package in depgraph["pn"]:
+ pkg_model.set(pkg_model.append(), COL_PKG_NAME, package)
+
+ for package in depgraph["depends"]:
+ for depend in depgraph["depends"][package]:
+ depends_model.set (depends_model.append(),
+ COL_DEP_TYPE, TYPE_DEP,
+ COL_DEP_PARENT, package,
+ COL_DEP_PACKAGE, depend)
+
+ for package in depgraph["rdepends-pn"]:
+ for rdepend in depgraph["rdepends-pn"][package]:
+ depends_model.set (depends_model.append(),
+ COL_DEP_TYPE, TYPE_RDEP,
+ COL_DEP_PARENT, package,
+ COL_DEP_PACKAGE, rdepend)
+
+class ProgressBar(gtk.Window):
+ def __init__(self):
+
+ gtk.Window.__init__(self)
+ self.set_title("Parsing .bb files, please wait...")
+ self.set_default_size(500, 0)
+ self.connect("delete-event", gtk.main_quit)
+
+ self.progress = gtk.ProgressBar()
+ self.add(self.progress)
+ self.show_all()
+
+class gtkthread(threading.Thread):
+ quit = threading.Event()
+ def __init__(self, shutdown):
+ threading.Thread.__init__(self)
+ self.setDaemon(True)
+ self.shutdown = shutdown
+
+ def run(self):
+ gobject.threads_init()
+ gtk.gdk.threads_init()
+ gtk.main()
+ gtkthread.quit.set()
+
+def init(server, eventHandler):
+
+ try:
+ cmdline = server.runCommand(["getCmdLineAction"])
+ if not cmdline or cmdline[0] != "generateDotGraph":
+ print "This UI is only compatible with the -g option"
+ return
+ ret = server.runCommand(["generateDepTreeEvent", cmdline[1]])
+ if ret != True:
+ print "Couldn't run command! %s" % ret
+ return
+ except xmlrpclib.Fault, x:
+ print "XMLRPC Fault getting commandline:\n %s" % x
+ return
+
+ shutdown = 0
+
+ gtkgui = gtkthread(shutdown)
+ gtkgui.start()
+
+ gtk.gdk.threads_enter()
+ pbar = ProgressBar()
+ dep = DepExplorer()
+ gtk.gdk.threads_leave()
+
+ while True:
+ try:
+ event = eventHandler.waitEvent(0.25)
+ if gtkthread.quit.isSet():
+ break
+
+ if event is None:
+ continue
+ if event[0].startswith('bb.event.ParseProgress'):
+ x = event[1]['sofar']
+ y = event[1]['total']
+ if x == y:
+ print("\nParsing finished. %d cached, %d parsed, %d skipped, %d masked, %d errors."
+ % ( event[1]['cached'], event[1]['parsed'], event[1]['skipped'], event[1]['masked'], event[1]['errors']))
+ pbar.hide()
+ gtk.gdk.threads_enter()
+ pbar.progress.set_fraction(float(x)/float(y))
+ pbar.progress.set_text("%d/%d (%2d %%)" % (x, y, x*100/y))
+ gtk.gdk.threads_leave()
+ continue
+
+ if event[0] == "bb.event.DepTreeGenerated":
+ gtk.gdk.threads_enter()
+ parse(event[1]['_depgraph'], dep.pkg_model, dep.depends_model)
+ gtk.gdk.threads_leave()
+
+ if event[0] == 'bb.command.CookerCommandCompleted':
+ continue
+ if event[0] == 'bb.command.CookerCommandFailed':
+ print "Command execution failed: %s" % event[1]['error']
+ break
+ if event[0] == 'bb.cooker.CookerExit':
+ break
+
+ continue
+
+ except KeyboardInterrupt:
+ if shutdown == 2:
+ print "\nThird Keyboard Interrupt, exit.\n"
+ break
+ if shutdown == 1:
+ print "\nSecond Keyboard Interrupt, stopping...\n"
+ server.runCommand(["stateStop"])
+ if shutdown == 0:
+ print "\nKeyboard Interrupt, closing down...\n"
+ server.runCommand(["stateShutdown"])
+ shutdown = shutdown + 1
+ pass
+
diff --git a/bitbake-dev/lib/bb/ui/knotty.py b/bitbake-dev/lib/bb/ui/knotty.py
new file mode 100644
index 0000000000..9e89660307
--- /dev/null
+++ b/bitbake-dev/lib/bb/ui/knotty.py
@@ -0,0 +1,157 @@
+#
+# BitBake (No)TTY UI Implementation
+#
+# Handling output to TTYs or files (no TTY)
+#
+# Copyright (C) 2006-2007 Richard Purdie
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import os
+import bb
+from bb import cooker
+
+import sys
+import time
+import itertools
+import xmlrpclib
+
+parsespin = itertools.cycle( r'|/-\\' )
+
+def init(server, eventHandler):
+
+ # Get values of variables which control our output
+ includelogs = server.runCommand(["readVariable", "BBINCLUDELOGS"])
+ loglines = server.runCommand(["readVariable", "BBINCLUDELOGS_LINES"])
+
+ try:
+ cmdline = server.runCommand(["getCmdLineAction"])
+ #print cmdline
+ if not cmdline:
+ return 1
+ ret = server.runCommand(cmdline)
+ if ret != True:
+ print "Couldn't get default commandline! %s" % ret
+ return 1
+ except xmlrpclib.Fault, x:
+ print "XMLRPC Fault getting commandline:\n %s" % x
+ return 1
+
+ shutdown = 0
+ return_value = 0
+ while True:
+ try:
+ event = eventHandler.waitEvent(0.25)
+ if event is None:
+ continue
+ #print event
+ if event[0].startswith('bb.event.Pkg'):
+ print "NOTE: %s" % event[1]['_message']
+ continue
+ if event[0].startswith('bb.msg.MsgPlain'):
+ print event[1]['_message']
+ continue
+ if event[0].startswith('bb.msg.MsgDebug'):
+ print 'DEBUG: ' + event[1]['_message']
+ continue
+ if event[0].startswith('bb.msg.MsgNote'):
+ print 'NOTE: ' + event[1]['_message']
+ continue
+ if event[0].startswith('bb.msg.MsgWarn'):
+ print 'WARNING: ' + event[1]['_message']
+ continue
+ if event[0].startswith('bb.msg.MsgError'):
+ return_value = 1
+ print 'ERROR: ' + event[1]['_message']
+ continue
+ if event[0].startswith('bb.build.TaskFailed'):
+ return_value = 1
+ logfile = event[1]['logfile']
+ if logfile:
+ print "ERROR: Logfile of failure stored in %s." % logfile
+ if includelogs:
+ print "Log data follows:"
+ f = open(logfile, "r")
+ lines = []
+ while True:
+ l = f.readline()
+ if l == '':
+ break
+ l = l.rstrip()
+ if loglines:
+ lines.append(' | %s' % l)
+ if len(lines) > int(loglines):
+ lines.pop(0)
+ else:
+ print '| %s' % l
+ f.close()
+ if lines:
+ for line in lines:
+ print line
+ if event[0].startswith('bb.build.Task'):
+ print "NOTE: %s" % event[1]['_message']
+ continue
+ if event[0].startswith('bb.event.ParseProgress'):
+ x = event[1]['sofar']
+ y = event[1]['total']
+ if os.isatty(sys.stdout.fileno()):
+ sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
+ sys.stdout.flush()
+ else:
+ if x == 1:
+ sys.stdout.write("Parsing .bb files, please wait...")
+ sys.stdout.flush()
+ if x == y:
+ sys.stdout.write("done.")
+ sys.stdout.flush()
+ if x == y:
+ print("\nParsing finished. %d cached, %d parsed, %d skipped, %d masked, %d errors."
+ % ( event[1]['cached'], event[1]['parsed'], event[1]['skipped'], event[1]['masked'], event[1]['errors']))
+ continue
+
+ if event[0] == 'bb.command.CookerCommandCompleted':
+ break
+ if event[0] == 'bb.command.CookerCommandFailed':
+ return_value = 1
+ print "Command execution failed: %s" % event[1]['error']
+ break
+ if event[0] == 'bb.cooker.CookerExit':
+ break
+
+ # ignore
+ if event[0].startswith('bb.event.BuildStarted'):
+ continue
+ if event[0].startswith('bb.event.BuildCompleted'):
+ continue
+ if event[0].startswith('bb.event.MultipleProviders'):
+ continue
+ if event[0].startswith('bb.runqueue.runQueue'):
+ continue
+ if event[0].startswith('bb.event.StampUpdate'):
+ continue
+ print "Unknown Event: %s" % event
+
+ except KeyboardInterrupt:
+ if shutdown == 2:
+ print "\nThird Keyboard Interrupt, exit.\n"
+ break
+ if shutdown == 1:
+ print "\nSecond Keyboard Interrupt, stopping...\n"
+ server.runCommand(["stateStop"])
+ if shutdown == 0:
+ print "\nKeyboard Interrupt, closing down...\n"
+ server.runCommand(["stateShutdown"])
+ shutdown = shutdown + 1
+ pass
+ return return_value
diff --git a/bitbake-dev/lib/bb/ui/ncurses.py b/bitbake-dev/lib/bb/ui/ncurses.py
new file mode 100644
index 0000000000..1476baa61f
--- /dev/null
+++ b/bitbake-dev/lib/bb/ui/ncurses.py
@@ -0,0 +1,333 @@
+#
+# BitBake Curses UI Implementation
+#
+# Implements an ncurses frontend for the BitBake utility.
+#
+# Copyright (C) 2006 Michael 'Mickey' Lauer
+# Copyright (C) 2006-2007 Richard Purdie
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""
+ We have the following windows:
+
+ 1.) Main Window: Shows what we are ultimately building and how far we are. Includes status bar
+ 2.) Thread Activity Window: Shows one status line for every concurrent bitbake thread.
+ 3.) Command Line Window: Contains an interactive command line where you can interact w/ Bitbake.
+
+ Basic window layout is like that:
+
+ |---------------------------------------------------------|
+ | <Main Window> | <Thread Activity Window> |
+ | | 0: foo do_compile complete|
+ | Building Gtk+-2.6.10 | 1: bar do_patch complete |
+ | Status: 60% | ... |
+ | | ... |
+ | | ... |
+ |---------------------------------------------------------|
+ |<Command Line Window> |
+ |>>> which virtual/kernel |
+ |openzaurus-kernel |
+ |>>> _ |
+ |---------------------------------------------------------|
+
+"""
+
+import os, sys, curses, time, random, threading, itertools, time
+from curses.textpad import Textbox
+import bb
+from bb import ui
+from bb.ui import uihelper
+
+parsespin = itertools.cycle( r'|/-\\' )
+
+X = 0
+Y = 1
+WIDTH = 2
+HEIGHT = 3
+
+MAXSTATUSLENGTH = 32
+
+class NCursesUI:
+ """
+ NCurses UI Class
+ """
+ class Window:
+ """Base Window Class"""
+ def __init__( self, x, y, width, height, fg=curses.COLOR_BLACK, bg=curses.COLOR_WHITE ):
+ self.win = curses.newwin( height, width, y, x )
+ self.dimensions = ( x, y, width, height )
+ """
+ if curses.has_colors():
+ color = 1
+ curses.init_pair( color, fg, bg )
+ self.win.bkgdset( ord(' '), curses.color_pair(color) )
+ else:
+ self.win.bkgdset( ord(' '), curses.A_BOLD )
+ """
+ self.erase()
+ self.setScrolling()
+ self.win.noutrefresh()
+
+ def erase( self ):
+ self.win.erase()
+
+ def setScrolling( self, b = True ):
+ self.win.scrollok( b )
+ self.win.idlok( b )
+
+ def setBoxed( self ):
+ self.boxed = True
+ self.win.box()
+ self.win.noutrefresh()
+
+ def setText( self, x, y, text, *args ):
+ self.win.addstr( y, x, text, *args )
+ self.win.noutrefresh()
+
+ def appendText( self, text, *args ):
+ self.win.addstr( text, *args )
+ self.win.noutrefresh()
+
+ def drawHline( self, y ):
+ self.win.hline( y, 0, curses.ACS_HLINE, self.dimensions[WIDTH] )
+ self.win.noutrefresh()
+
+ class DecoratedWindow( Window ):
+ """Base class for windows with a box and a title bar"""
+ def __init__( self, title, x, y, width, height, fg=curses.COLOR_BLACK, bg=curses.COLOR_WHITE ):
+ NCursesUI.Window.__init__( self, x+1, y+3, width-2, height-4, fg, bg )
+ self.decoration = NCursesUI.Window( x, y, width, height, fg, bg )
+ self.decoration.setBoxed()
+ self.decoration.win.hline( 2, 1, curses.ACS_HLINE, width-2 )
+ self.setTitle( title )
+
+ def setTitle( self, title ):
+ self.decoration.setText( 1, 1, title.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD )
+
+ #-------------------------------------------------------------------------#
+# class TitleWindow( Window ):
+ #-------------------------------------------------------------------------#
+# """Title Window"""
+# def __init__( self, x, y, width, height ):
+# NCursesUI.Window.__init__( self, x, y, width, height )
+# version = bb.__version__
+# title = "BitBake %s" % version
+# credit = "(C) 2003-2007 Team BitBake"
+# #self.win.hline( 2, 1, curses.ACS_HLINE, width-2 )
+# self.win.border()
+# self.setText( 1, 1, title.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD )
+# self.setText( 1, 2, credit.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD )
+
+ #-------------------------------------------------------------------------#
+ class ThreadActivityWindow( DecoratedWindow ):
+ #-------------------------------------------------------------------------#
+ """Thread Activity Window"""
+ def __init__( self, x, y, width, height ):
+ NCursesUI.DecoratedWindow.__init__( self, "Thread Activity", x, y, width, height )
+
+ def setStatus( self, thread, text ):
+ line = "%02d: %s" % ( thread, text )
+ width = self.dimensions[WIDTH]
+ if ( len(line) > width ):
+ line = line[:width-3] + "..."
+ else:
+ line = line.ljust( width )
+ self.setText( 0, thread, line )
+
+ #-------------------------------------------------------------------------#
+ class MainWindow( DecoratedWindow ):
+ #-------------------------------------------------------------------------#
+ """Main Window"""
+ def __init__( self, x, y, width, height ):
+ self.StatusPosition = width - MAXSTATUSLENGTH
+ NCursesUI.DecoratedWindow.__init__( self, None, x, y, width, height )
+ curses.nl()
+
+ def setTitle( self, title ):
+ title = "BitBake %s" % bb.__version__
+ self.decoration.setText( 2, 1, title, curses.A_BOLD )
+ self.decoration.setText( self.StatusPosition - 8, 1, "Status:", curses.A_BOLD )
+
+ def setStatus(self, status):
+ while len(status) < MAXSTATUSLENGTH:
+ status = status + " "
+ self.decoration.setText( self.StatusPosition, 1, status, curses.A_BOLD )
+
+
+ #-------------------------------------------------------------------------#
+ class ShellOutputWindow( DecoratedWindow ):
+ #-------------------------------------------------------------------------#
+ """Interactive Command Line Output"""
+ def __init__( self, x, y, width, height ):
+ NCursesUI.DecoratedWindow.__init__( self, "Command Line Window", x, y, width, height )
+
+ #-------------------------------------------------------------------------#
+ class ShellInputWindow( Window ):
+ #-------------------------------------------------------------------------#
+ """Interactive Command Line Input"""
+ def __init__( self, x, y, width, height ):
+ NCursesUI.Window.__init__( self, x, y, width, height )
+
+# self.textbox = Textbox( self.win )
+# t = threading.Thread()
+# t.run = self.textbox.edit
+# t.start()
+
+ #-------------------------------------------------------------------------#
+ def main(self, stdscr, server, eventHandler):
+ #-------------------------------------------------------------------------#
+ height, width = stdscr.getmaxyx()
+
+ # for now split it like that:
+ # MAIN_y + THREAD_y = 2/3 screen at the top
+ # MAIN_x = 2/3 left, THREAD_y = 1/3 right
+ # CLI_y = 1/3 of screen at the bottom
+ # CLI_x = full
+
+ main_left = 0
+ main_top = 0
+ main_height = ( height / 3 * 2 )
+ main_width = ( width / 3 ) * 2
+ clo_left = main_left
+ clo_top = main_top + main_height
+ clo_height = height - main_height - main_top - 1
+ clo_width = width
+ cli_left = main_left
+ cli_top = clo_top + clo_height
+ cli_height = 1
+ cli_width = width
+ thread_left = main_left + main_width
+ thread_top = main_top
+ thread_height = main_height
+ thread_width = width - main_width
+
+ #tw = self.TitleWindow( 0, 0, width, main_top )
+ mw = self.MainWindow( main_left, main_top, main_width, main_height )
+ taw = self.ThreadActivityWindow( thread_left, thread_top, thread_width, thread_height )
+ clo = self.ShellOutputWindow( clo_left, clo_top, clo_width, clo_height )
+ cli = self.ShellInputWindow( cli_left, cli_top, cli_width, cli_height )
+ cli.setText( 0, 0, "BB>" )
+
+ mw.setStatus("Idle")
+
+ helper = uihelper.BBUIHelper()
+ shutdown = 0
+
+ try:
+ cmdline = server.runCommand(["getCmdLineAction"])
+ if not cmdline:
+ return
+ ret = server.runCommand(cmdline)
+ if ret != True:
+ print "Couldn't get default commandlind! %s" % ret
+ return
+ except xmlrpclib.Fault, x:
+ print "XMLRPC Fault getting commandline:\n %s" % x
+ return
+
+ exitflag = False
+ while not exitflag:
+ try:
+ event = eventHandler.waitEvent(0.25)
+ if not event:
+ continue
+ helper.eventHandler(event)
+ #mw.appendText("%s\n" % event[0])
+ if event[0].startswith('bb.event.Pkg'):
+ mw.appendText("NOTE: %s\n" % event[1]['_message'])
+ if event[0].startswith('bb.build.Task'):
+ mw.appendText("NOTE: %s\n" % event[1]['_message'])
+ if event[0].startswith('bb.msg.MsgDebug'):
+ mw.appendText('DEBUG: ' + event[1]['_message'] + '\n')
+ if event[0].startswith('bb.msg.MsgNote'):
+ mw.appendText('NOTE: ' + event[1]['_message'] + '\n')
+ if event[0].startswith('bb.msg.MsgWarn'):
+ mw.appendText('WARNING: ' + event[1]['_message'] + '\n')
+ if event[0].startswith('bb.msg.MsgError'):
+ mw.appendText('ERROR: ' + event[1]['_message'] + '\n')
+ if event[0].startswith('bb.msg.MsgFatal'):
+ mw.appendText('FATAL: ' + event[1]['_message'] + '\n')
+ if event[0].startswith('bb.event.ParseProgress'):
+ x = event[1]['sofar']
+ y = event[1]['total']
+ if x == y:
+ mw.setStatus("Idle")
+ mw.appendText("Parsing finished. %d cached, %d parsed, %d skipped, %d masked."
+ % ( event[1]['cached'], event[1]['parsed'], event[1]['skipped'], event[1]['masked'] ))
+ else:
+ mw.setStatus("Parsing: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
+# if event[0].startswith('bb.build.TaskFailed'):
+# if event[1]['logfile']:
+# if data.getVar("BBINCLUDELOGS", d):
+# bb.msg.error(bb.msg.domain.Build, "log data follows (%s)" % logfile)
+# number_of_lines = data.getVar("BBINCLUDELOGS_LINES", d)
+# if number_of_lines:
+# os.system('tail -n%s %s' % (number_of_lines, logfile))
+# else:
+# f = open(logfile, "r")
+# while True:
+# l = f.readline()
+# if l == '':
+# break
+# l = l.rstrip()
+# print '| %s' % l
+# f.close()
+# else:
+# bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile)
+
+ if event[0] == 'bb.command.CookerCommandCompleted':
+ exitflag = True
+ if event[0] == 'bb.command.CookerCommandFailed':
+ mw.appendText("Command execution failed: %s" % event[1]['error'])
+ time.sleep(2)
+ exitflag = True
+ if event[0] == 'bb.cooker.CookerExit':
+ exitflag = True
+
+ if helper.needUpdate:
+ activetasks, failedtasks = helper.getTasks()
+ taw.erase()
+ taw.setText(0, 0, "")
+ if activetasks:
+ taw.appendText("Active Tasks:\n")
+ for task in activetasks:
+ taw.appendText(task)
+ if failedtasks:
+ taw.appendText("Failed Tasks:\n")
+ for task in failedtasks:
+ taw.appendText(task)
+
+ curses.doupdate()
+ except KeyboardInterrupt:
+ if shutdown == 2:
+ mw.appendText("Third Keyboard Interrupt, exit.\n")
+ exitflag = True
+ if shutdown == 1:
+ mw.appendText("Second Keyboard Interrupt, stopping...\n")
+ server.runCommand(["stateStop"])
+ if shutdown == 0:
+ mw.appendText("Keyboard Interrupt, closing down...\n")
+ server.runCommand(["stateShutdown"])
+ shutdown = shutdown + 1
+ pass
+
+def init(server, eventHandler):
+ ui = NCursesUI()
+ try:
+ curses.wrapper(ui.main, server, eventHandler)
+ except:
+ import traceback
+ traceback.print_exc()
+
diff --git a/bitbake-dev/lib/bb/ui/uievent.py b/bitbake-dev/lib/bb/ui/uievent.py
new file mode 100644
index 0000000000..9d724d7fc5
--- /dev/null
+++ b/bitbake-dev/lib/bb/ui/uievent.py
@@ -0,0 +1,127 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
+# Copyright (C) 2006 - 2007 Richard Purdie
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+"""
+Use this class to fork off a thread to recieve event callbacks from the bitbake
+server and queue them for the UI to process. This process must be used to avoid
+client/server deadlocks.
+"""
+
+import sys, socket, threading
+from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
+
+class BBUIEventQueue:
+ def __init__(self, BBServer):
+
+ self.eventQueue = []
+ self.eventQueueLock = threading.Lock()
+ self.eventQueueNotify = threading.Event()
+
+ self.BBServer = BBServer
+
+ self.t = threading.Thread()
+ self.t.setDaemon(True)
+ self.t.run = self.startCallbackHandler
+ self.t.start()
+
+ def getEvent(self):
+
+ self.eventQueueLock.acquire()
+
+ if len(self.eventQueue) == 0:
+ self.eventQueueLock.release()
+ return None
+
+ item = self.eventQueue.pop(0)
+
+ if len(self.eventQueue) == 0:
+ self.eventQueueNotify.clear()
+
+ self.eventQueueLock.release()
+
+ return item
+
+ def waitEvent(self, delay):
+ self.eventQueueNotify.wait(delay)
+ return self.getEvent()
+
+ def queue_event(self, event):
+
+ self.eventQueueLock.acquire()
+ self.eventQueue.append(event)
+ self.eventQueueNotify.set()
+ self.eventQueueLock.release()
+
+ def startCallbackHandler(self):
+
+ server = UIXMLRPCServer()
+ self.host, self.port = server.socket.getsockname()
+
+ server.register_function( self.system_quit, "event.quit" )
+ server.register_function( self.queue_event, "event.send" )
+ server.socket.settimeout(1)
+
+ self.EventHandle = self.BBServer.registerEventHandler(self.host, self.port)
+
+ self.server = server
+ while not server.quit:
+ server.handle_request()
+ server.server_close()
+
+ def system_quit( self ):
+ """
+ Shut down the callback thread
+ """
+ try:
+ self.BBServer.unregisterEventHandler(self.EventHandle)
+ except:
+ pass
+ self.server.quit = True
+
+class UIXMLRPCServer (SimpleXMLRPCServer):
+
+ def __init__( self, interface = ("localhost", 0) ):
+ self.quit = False
+ SimpleXMLRPCServer.__init__( self,
+ interface,
+ requestHandler=SimpleXMLRPCRequestHandler,
+ logRequests=False, allow_none=True)
+
+ def get_request(self):
+ while not self.quit:
+ try:
+ sock, addr = self.socket.accept()
+ sock.settimeout(1)
+ return (sock, addr)
+ except socket.timeout:
+ pass
+ return (None,None)
+
+ def close_request(self, request):
+ if request is None:
+ return
+ SimpleXMLRPCServer.close_request(self, request)
+
+ def process_request(self, request, client_address):
+ if request is None:
+ return
+ SimpleXMLRPCServer.process_request(self, request, client_address)
+
+
diff --git a/bitbake-dev/lib/bb/ui/uihelper.py b/bitbake-dev/lib/bb/ui/uihelper.py
new file mode 100644
index 0000000000..246844c9d2
--- /dev/null
+++ b/bitbake-dev/lib/bb/ui/uihelper.py
@@ -0,0 +1,49 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
+# Copyright (C) 2006 - 2007 Richard Purdie
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+class BBUIHelper:
+ def __init__(self):
+ self.needUpdate = False
+ self.running_tasks = {}
+ self.failed_tasks = {}
+
+ def eventHandler(self, event):
+ if event[0].startswith('bb.build.TaskStarted'):
+ self.running_tasks["%s %s\n" % (event[1]['_package'], event[1]['_task'])] = ""
+ self.needUpdate = True
+ if event[0].startswith('bb.build.TaskSucceeded'):
+ del self.running_tasks["%s %s\n" % (event[1]['_package'], event[1]['_task'])]
+ self.needUpdate = True
+ if event[0].startswith('bb.build.TaskFailed'):
+ del self.running_tasks["%s %s\n" % (event[1]['_package'], event[1]['_task'])]
+ self.failed_tasks["%s %s\n" % (event[1]['_package'], event[1]['_task'])] = ""
+ self.needUpdate = True
+
+ # Add runqueue event handling
+ #if event[0].startswith('bb.runqueue.runQueueTaskCompleted'):
+ # a = 1
+ #if event[0].startswith('bb.runqueue.runQueueTaskStarted'):
+ # a = 1
+ #if event[0].startswith('bb.runqueue.runQueueTaskFailed'):
+ # a = 1
+ #if event[0].startswith('bb.runqueue.runQueueExitWait'):
+ # a = 1
+
+ def getTasks(self):
+ return (self.running_tasks, self.failed_tasks)