summaryrefslogtreecommitdiff
path: root/bitbake
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake')
-rwxr-xr-xbitbake/bin/bitbake2
-rw-r--r--bitbake/lib/bb/ui/crumbs/progress.py2
-rw-r--r--bitbake/lib/bb/ui/crumbs/runningbuild.py225
-rw-r--r--bitbake/lib/bb/ui/depexp.py66
-rw-r--r--bitbake/lib/bb/ui/goggle.py32
-rw-r--r--bitbake/lib/bb/ui/knotty.py4
-rw-r--r--bitbake/lib/bb/ui/ncurses.py58
7 files changed, 299 insertions, 90 deletions
diff --git a/bitbake/bin/bitbake b/bitbake/bin/bitbake
index 61bd0194d..6d0528953 100755
--- a/bitbake/bin/bitbake
+++ b/bitbake/bin/bitbake
@@ -71,7 +71,7 @@ def get_ui(config):
return getattr(module, interface).main
except AttributeError:
sys.exit("FATAL: Invalid user interface '%s' specified.\n"
- "Valid interfaces: ncurses, depexp, knotty [default]." % interface)
+ "Valid interfaces: depexp, goggle, ncurses, knotty [default]." % interface)
# Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others"""
diff --git a/bitbake/lib/bb/ui/crumbs/progress.py b/bitbake/lib/bb/ui/crumbs/progress.py
index 8bd87108e..36eca3829 100644
--- a/bitbake/lib/bb/ui/crumbs/progress.py
+++ b/bitbake/lib/bb/ui/crumbs/progress.py
@@ -14,4 +14,4 @@ class ProgressBar(gtk.Dialog):
def update(self, x, y):
self.progress.set_fraction(float(x)/float(y))
- self.progress.set_text("%d/%d (%2d %%)" % (x, y, x*100/y))
+ self.progress.set_text("%2d %%" % (x*100/y))
diff --git a/bitbake/lib/bb/ui/crumbs/runningbuild.py b/bitbake/lib/bb/ui/crumbs/runningbuild.py
index 9730bfd47..4703e6d84 100644
--- a/bitbake/lib/bb/ui/crumbs/runningbuild.py
+++ b/bitbake/lib/bb/ui/crumbs/runningbuild.py
@@ -1,3 +1,4 @@
+
#
# BitBake Graphical GTK User Interface
#
@@ -20,9 +21,20 @@
import gtk
import gobject
+import logging
+import time
+import urllib
+import urllib2
+
+class Colors(object):
+ OK = "#ffffff"
+ RUNNING = "#aaffaa"
+ WARNING ="#f88017"
+ ERROR = "#ffaaaa"
class RunningBuildModel (gtk.TreeStore):
- (COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5)
+ (COL_LOG, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_COLOR, COL_NUM_ACTIVE) = range(7)
+
def __init__ (self):
gtk.TreeStore.__init__ (self,
gobject.TYPE_STRING,
@@ -30,7 +42,8 @@ class RunningBuildModel (gtk.TreeStore):
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_STRING,
- gobject.TYPE_BOOLEAN)
+ gobject.TYPE_STRING,
+ gobject.TYPE_INT)
class RunningBuild (gobject.GObject):
__gsignals__ = {
@@ -63,32 +76,42 @@ class RunningBuild (gobject.GObject):
# for the message.
if hasattr(event, 'pid'):
pid = event.pid
- if pid in self.pids_to_task:
- (package, task) = self.pids_to_task[pid]
- parent = self.tasks_to_iter[(package, task)]
+ if hasattr(event, 'process'):
+ pid = event.process
+
+ if pid and pid in self.pids_to_task:
+ (package, task) = self.pids_to_task[pid]
+ parent = self.tasks_to_iter[(package, task)]
- if isinstance(event, bb.msg.MsgBase):
- # Ignore the "Running task i of n .."
- if (event._message.startswith ("Running task")):
+ if(isinstance(event, logging.LogRecord)):
+ if (event.msg.startswith ("Running task")):
return # don't add these to the list
- # Set a pretty icon for the message based on it's type.
- if isinstance(event, bb.msg.MsgWarn):
- icon = "dialog-warning"
- elif isinstance(event, bb.msg.MsgError):
+ if event.levelno >= logging.ERROR:
icon = "dialog-error"
+ color = Colors.ERROR
+ elif event.levelno >= logging.WARNING:
+ icon = "dialog-warning"
+ color = Colors.WARNING
else:
icon = None
+ color = Colors.OK
+
+ # if we know which package we belong to, we'll append onto its list.
+ # otherwise, we'll jump to the top of the master list
+ if parent:
+ tree_add = self.model.append
+ else:
+ tree_add = self.model.prepend
+ tree_add(parent,
+ (None,
+ package,
+ task,
+ event.getMessage(),
+ icon,
+ color,
+ 0))
- # Add the message to the tree either at the top level if parent is
- # None otherwise as a descendent of a task.
- self.model.append (parent,
- (event.__class__.__name__.split()[-1], # e.g. MsgWarn, MsgError
- package,
- task,
- event._message,
- icon,
- False))
elif isinstance(event, bb.build.TaskStarted):
(package, task) = (event._package, event._task)
@@ -101,76 +124,142 @@ class RunningBuild (gobject.GObject):
if ((package, None) in self.tasks_to_iter):
parent = self.tasks_to_iter[(package, None)]
else:
- parent = self.model.append (None, (None,
+ parent = self.model.prepend(None, (None,
package,
None,
"Package: %s" % (package),
None,
- False))
+ Colors.OK,
+ 0))
self.tasks_to_iter[(package, None)] = parent
# Because this parent package now has an active child mark it as
# such.
- self.model.set(parent, self.model.COL_ICON, "gtk-execute")
+ # @todo if parent is already in error, don't mark it green
+ self.model.set(parent, self.model.COL_ICON, "gtk-execute",
+ self.model.COL_COLOR, Colors.RUNNING)
# Add an entry in the model for this task
i = self.model.append (parent, (None,
package,
task,
"Task: %s" % (task),
- None,
- False))
+ "gtk-execute",
+ Colors.RUNNING,
+ 0))
+
+ # update the parent's active task count
+ num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] + 1
+ self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active)
# Save out the iter so that we can find it when we have a message
# that we need to attach to a task.
self.tasks_to_iter[(package, task)] = i
- # Mark this task as active.
- self.model.set(i, self.model.COL_ICON, "gtk-execute")
-
elif isinstance(event, bb.build.TaskBase):
+ current = self.tasks_to_iter[(package, task)]
+ parent = self.tasks_to_iter[(package, None)]
+
+ # remove this task from the parent's active count
+ num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] - 1
+ self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active)
if isinstance(event, bb.build.TaskFailed):
- # Mark the task as failed
- i = self.tasks_to_iter[(package, task)]
- self.model.set(i, self.model.COL_ICON, "dialog-error")
+ # Mark the task and parent as failed
+ icon = "dialog-error"
+ color = Colors.ERROR
- # Mark the parent package as failed
- i = self.tasks_to_iter[(package, None)]
- self.model.set(i, self.model.COL_ICON, "dialog-error")
+ logfile = event.logfile
+ if logfile and os.path.exists(logfile):
+ with open(logfile) as f:
+ logdata = f.read()
+ self.model.append(current, ('pastebin', None, None, logdata, 'gtk-error', Colors.OK, 0))
+
+ for i in (current, parent):
+ self.model.set(i, self.model.COL_ICON, icon,
+ self.model.COL_COLOR, color)
else:
+ icon = None
+ color = Colors.OK
+
# Mark the task as inactive
- i = self.tasks_to_iter[(package, task)]
- self.model.set(i, self.model.COL_ICON, None)
+ self.model.set(current, self.model.COL_ICON, icon,
+ self.model.COL_COLOR, color)
- # Mark the parent package as inactive
+ # Mark the parent package as inactive, but make sure to
+ # preserve error and active states
i = self.tasks_to_iter[(package, None)]
- self.model.set(i, self.model.COL_ICON, None)
-
+ if self.model.get(parent, self.model.COL_ICON) != 'dialog-error':
+ self.model.set(parent, self.model.COL_ICON, icon)
+ if num_active == 0:
+ self.model.set(parent, self.model.COL_COLOR, Colors.OK)
# Clear the iters and the pids since when the task goes away the
# pid will no longer be used for messages
del self.tasks_to_iter[(package, task)]
del self.pids_to_task[pid]
+ elif isinstance(event, bb.event.BuildStarted):
+
+ self.model.prepend(None, (None,
+ None,
+ None,
+ "Build Started (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'),
+ None,
+ Colors.OK,
+ 0))
elif isinstance(event, bb.event.BuildCompleted):
failures = int (event._failures)
+ self.model.prepend(None, (None,
+ None,
+ None,
+ "Build Completed (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'),
+ None,
+ Colors.OK,
+ 0))
# Emit the appropriate signal depending on the number of failures
- if (failures > 1):
+ if (failures >= 1):
self.emit ("build-failed")
else:
self.emit ("build-succeeded")
+ elif isinstance(event, bb.event.CacheLoadStarted) and pbar:
+ pbar.set_title("Loading cache")
+ self.progress_total = event.total
+ pbar.update(0, self.progress_total)
+ elif isinstance(event, bb.event.CacheLoadProgress) and pbar:
+ pbar.update(event.current, self.progress_total)
+ elif isinstance(event, bb.event.CacheLoadCompleted) and pbar:
+ pbar.update(self.progress_total, self.progress_total)
+
+ elif isinstance(event, bb.event.ParseStarted) and pbar:
+ pbar.set_title("Processing recipes")
+ self.progress_total = event.total
+ pbar.update(0, self.progress_total)
elif isinstance(event, bb.event.ParseProgress) and pbar:
- x = event.sofar
- y = event.total
- if x == y:
- pbar.hide()
- return
- pbar.update(x, y)
+ pbar.update(event.current, self.progress_total)
+ elif isinstance(event, bb.event.ParseCompleted) and pbar:
+ pbar.hide()
+
+ return
+
+
+def do_pastebin(text):
+ url = 'http://pastebin.com/api_public.php'
+ params = {'paste_code': text, 'paste_format': 'text'}
+
+ req = urllib2.Request(url, urllib.urlencode(params))
+ response = urllib2.urlopen(req)
+ paste_url = response.read()
+
+ return paste_url
+
class RunningBuildTreeView (gtk.TreeView):
+ __gsignals__ = {
+ "button_press_event" : "override"
+ }
def __init__ (self):
gtk.TreeView.__init__ (self)
@@ -181,6 +270,42 @@ class RunningBuildTreeView (gtk.TreeView):
self.append_column (col)
# The message of the build.
- renderer = gtk.CellRendererText ()
- col = gtk.TreeViewColumn ("Message", renderer, text=3)
- self.append_column (col)
+ self.message_renderer = gtk.CellRendererText ()
+ self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=3)
+ self.message_column.add_attribute(self.message_renderer, 'background', 5)
+ self.message_renderer.set_property('editable', 5)
+ self.append_column (self.message_column)
+
+ def do_button_press_event(self, event):
+ gtk.TreeView.do_button_press_event(self, event)
+
+ if event.button == 3:
+ selection = super(RunningBuildTreeView, self).get_selection()
+ (model, iter) = selection.get_selected()
+ if iter is not None:
+ can_paste = model.get(iter, model.COL_LOG)[0]
+ if can_paste == 'pastebin':
+ # build a simple menu with a pastebin option
+ menu = gtk.Menu()
+ menuitem = gtk.MenuItem("Send log to pastebin")
+ menu.append(menuitem)
+ menuitem.connect("activate", self.pastebin_handler, (model, iter))
+ menuitem.show()
+ menu.show()
+ menu.popup(None, None, None, event.button, event.time)
+
+ def pastebin_handler(self, widget, data):
+ """
+ Send the log data to pastebin, then add the new paste url to the
+ clipboard.
+ """
+ (model, iter) = data
+ paste_url = do_pastebin(model.get(iter, model.COL_MESSAGE)[0])
+
+ # @todo Provide visual feedback to the user that it is done and that
+ # it worked.
+ print paste_url
+
+ clipboard = gtk.clipboard_get()
+ clipboard.set_text(paste_url)
+ clipboard.store() \ No newline at end of file
diff --git a/bitbake/lib/bb/ui/depexp.py b/bitbake/lib/bb/ui/depexp.py
index 48f6f792d..66ef96cdb 100644
--- a/bitbake/lib/bb/ui/depexp.py
+++ b/bitbake/lib/bb/ui/depexp.py
@@ -19,6 +19,7 @@
import gobject
import gtk
+import Queue
import threading
import xmlrpclib
import bb
@@ -32,6 +33,7 @@ from bb.ui.crumbs.progress import ProgressBar
(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)
@@ -52,6 +54,7 @@ class PackageDepView(gtk.TreeView):
self.current = package
self.filter_model.refilter()
+
class PackageReverseDepView(gtk.TreeView):
def __init__(self, model, label):
gtk.TreeView.__init__(self)
@@ -69,6 +72,7 @@ class PackageReverseDepView(gtk.TreeView):
self.current = package
self.filter_model.refilter()
+
class DepExplorer(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
@@ -90,9 +94,12 @@ class DepExplorer(gtk.Window):
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))
+ column = gtk.TreeViewColumn("Package", gtk.CellRendererText(), text=COL_PKG_NAME)
+ self.pkg_treeview.append_column(column)
+ column.set_sort_column_id(COL_PKG_NAME)
pane.add1(scrolled)
scrolled.add(self.pkg_treeview)
@@ -158,7 +165,6 @@ class DepExplorer(gtk.Window):
def parse(depgraph, pkg_model, depends_model):
-
for package in depgraph["pn"]:
pkg_model.set(pkg_model.append(), COL_PKG_NAME, package)
@@ -176,6 +182,7 @@ def parse(depgraph, pkg_model, depends_model):
COL_DEP_PARENT, package,
COL_DEP_PACKAGE, rdepend)
+
class gtkthread(threading.Thread):
quit = threading.Event()
def __init__(self, shutdown):
@@ -189,8 +196,8 @@ class gtkthread(threading.Thread):
gtk.main()
gtkthread.quit.set()
-def main(server, eventHandler):
+def main(server, eventHandler):
try:
cmdline = server.runCommand(["getCmdLineAction"])
if not cmdline or cmdline[0] != "generateDotGraph":
@@ -214,25 +221,54 @@ def main(server, eventHandler):
pbar = ProgressBar(dep)
gtk.gdk.threads_leave()
+ progress_total = 0
while True:
try:
event = eventHandler.waitEvent(0.25)
if gtkthread.quit.isSet():
+ server.runCommand(["stateStop"])
break
if event is None:
continue
+
+ if isinstance(event, bb.event.CacheLoadStarted):
+ progress_total = event.total
+ gtk.gdk.threads_enter()
+ pbar.set_title("Loading Cache")
+ pbar.update(0, progress_total)
+ gtk.gdk.threads_leave()
+
+ if isinstance(event, bb.event.CacheLoadProgress):
+ x = event.current
+ gtk.gdk.threads_enter()
+ pbar.update(x, progress_total)
+ gtk.gdk.threads_leave()
+ continue
+
+ if isinstance(event, bb.event.CacheLoadCompleted):
+ gtk.gdk.threads_enter()
+ pbar.update(progress_total, progress_total)
+ gtk.gdk.threads_leave()
+ continue
+
+ if isinstance(event, bb.event.ParseStarted):
+ progress_total = event.total
+ gtk.gdk.threads_enter()
+ pbar.set_title("Processing recipes")
+ pbar.update(0, progress_total)
+ gtk.gdk.threads_leave()
+
if isinstance(event, bb.event.ParseProgress):
- x = event.sofar
- y = event.total
- if x == y:
- print(("\nParsing finished. %d cached, %d parsed, %d skipped, %d masked, %d errors."
- % ( event.cached, event.parsed, event.skipped, event.masked, event.errors)))
- pbar.hide()
- continue
+ x = event.current
gtk.gdk.threads_enter()
- pbar.update(x, y)
+ pbar.update(x, progress_total)
gtk.gdk.threads_leave()
+
+ continue
+
+ if isinstance(event, bb.event.ParseCompleted):
+ pbar.hide()
continue
if isinstance(event, bb.event.DepTreeGenerated):
@@ -242,16 +278,22 @@ def main(server, eventHandler):
if isinstance(event, bb.command.CommandCompleted):
continue
+
if isinstance(event, bb.command.CommandFailed):
print("Command execution failed: %s" % event.error)
return event.exitcode
+
if isinstance(event, bb.command.CommandExit):
return event.exitcode
+
if isinstance(event, bb.cooker.CookerExit):
break
continue
-
+ except EnvironmentError as ioerror:
+ # ignore interrupted io
+ if ioerror.args[0] == 4:
+ pass
except KeyboardInterrupt:
if shutdown == 2:
print("\nThird Keyboard Interrupt, exit.\n")
diff --git a/bitbake/lib/bb/ui/goggle.py b/bitbake/lib/bb/ui/goggle.py
index 40923ba88..3c6a014dc 100644
--- a/bitbake/lib/bb/ui/goggle.py
+++ b/bitbake/lib/bb/ui/goggle.py
@@ -24,19 +24,32 @@ import xmlrpclib
from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
from bb.ui.crumbs.progress import ProgressBar
+import Queue
+
+
def event_handle_idle_func (eventHandler, build, pbar):
# Consume as many messages as we can in the time available to us
event = eventHandler.getEvent()
while event:
- build.handle_event (event, pbar)
event = eventHandler.getEvent()
+ build.handle_event (event, pbar)
return True
def scroll_tv_cb (model, path, iter, view):
view.scroll_to_cell (path)
+
+# @todo hook these into the GUI so the user has feedback...
+def running_build_failed_cb (running_build):
+ pass
+
+
+def running_build_succeeded_cb (running_build):
+ pass
+
+
class MainWindow (gtk.Window):
def __init__ (self):
gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
@@ -49,6 +62,7 @@ class MainWindow (gtk.Window):
self.set_default_size(640, 480)
scrolled_window.add (self.cur_build_tv)
+
def main (server, eventHandler):
gobject.threads_init()
gtk.gdk.threads_init()
@@ -61,9 +75,11 @@ def main (server, eventHandler):
running_build = RunningBuild ()
window.cur_build_tv.set_model (running_build.model)
running_build.model.connect("row-inserted", scroll_tv_cb, window.cur_build_tv)
+ running_build.connect ("build-succeeded", running_build_succeeded_cb)
+ running_build.connect ("build-failed", running_build_failed_cb)
+
try:
cmdline = server.runCommand(["getCmdLineAction"])
- print(cmdline)
if not cmdline:
return 1
ret = server.runCommand(cmdline)
@@ -76,10 +92,18 @@ def main (server, eventHandler):
# Use a timeout function for probing the event queue to find out if we
# have a message waiting for us.
- gobject.timeout_add (200,
+ gobject.timeout_add (100,
event_handle_idle_func,
eventHandler,
running_build,
pbar)
- gtk.main()
+ try:
+ gtk.main()
+ except EnvironmentError as ioerror:
+ # ignore interrupted io
+ if ioerror.args[0] == 4:
+ pass
+ finally:
+ server.runCommand(["stateStop"])
+
diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py
index 2e9c6f3f2..4d87a3d7c 100644
--- a/bitbake/lib/bb/ui/knotty.py
+++ b/bitbake/lib/bb/ui/knotty.py
@@ -228,6 +228,10 @@ def main(server, eventHandler):
logger.error("Unknown event: %s", event)
+ except EnvironmentError as ioerror:
+ # ignore interrupted io
+ if ioerror.args[0] == 4:
+ pass
except KeyboardInterrupt:
if shutdown == 2:
print("\nThird Keyboard Interrupt, exit.\n")
diff --git a/bitbake/lib/bb/ui/ncurses.py b/bitbake/lib/bb/ui/ncurses.py
index 1db4ec173..469f1b730 100644
--- a/bitbake/lib/bb/ui/ncurses.py
+++ b/bitbake/lib/bb/ui/ncurses.py
@@ -44,8 +44,9 @@
"""
-from __future__ import division
+from __future__ import division
+import logging
import os, sys, curses, itertools, time
import bb
import xmlrpclib
@@ -246,29 +247,35 @@ class NCursesUI:
event = eventHandler.waitEvent(0.25)
if not event:
continue
+
helper.eventHandler(event)
- #mw.appendText("%s\n" % event[0])
if isinstance(event, bb.build.TaskBase):
mw.appendText("NOTE: %s\n" % event._message)
- if isinstance(event, bb.msg.MsgDebug):
- mw.appendText('DEBUG: ' + event._message + '\n')
- if isinstance(event, bb.msg.MsgNote):
- mw.appendText('NOTE: ' + event._message + '\n')
- if isinstance(event, bb.msg.MsgWarn):
- mw.appendText('WARNING: ' + event._message + '\n')
- if isinstance(event, bb.msg.MsgError):
- mw.appendText('ERROR: ' + event._message + '\n')
- if isinstance(event, bb.msg.MsgFatal):
- mw.appendText('FATAL: ' + event._message + '\n')
+ if isinstance(event, logging.LogRecord):
+ mw.appendText(logging.getLevelName(event.levelno) + ': ' + event.getMessage() + '\n')
+
+ if isinstance(event, bb.event.CacheLoadStarted):
+ self.parse_total = event.total
+ if isinstance(event, bb.event.CacheLoadProgress):
+ x = event.current
+ y = self.parse_total
+ mw.setStatus("Loading Cache: %s [%2d %%]" % ( next(parsespin), x*100/y ) )
+ if isinstance(event, bb.event.CacheLoadCompleted):
+ mw.setStatus("Idle")
+ mw.appendText("Loaded %d entries from dependency cache.\n"
+ % ( event.num_entries))
+
+ if isinstance(event, bb.event.ParseStarted):
+ self.parse_total = event.total
if isinstance(event, bb.event.ParseProgress):
- x = event.sofar
- y = event.total
- if x == y:
- mw.setStatus("Idle")
- mw.appendText("Parsing finished. %d cached, %d parsed, %d skipped, %d masked."
+ x = event.current
+ y = self.parse_total
+ mw.setStatus("Parsing Recipes: %s [%2d %%]" % ( next(parsespin), x*100/y ) )
+ if isinstance(event, bb.event.ParseCompleted):
+ mw.setStatus("Idle")
+ mw.appendText("Parsing finished. %d cached, %d parsed, %d skipped, %d masked.\n"
% ( event.cached, event.parsed, event.skipped, event.masked ))
- else:
- mw.setStatus("Parsing: %s (%04d/%04d) [%2d %%]" % ( next(parsespin), x, y, x*100//y ) )
+
# if isinstance(event, bb.build.TaskFailed):
# if event.logfile:
# if data.getVar("BBINCLUDELOGS", d):
@@ -289,7 +296,9 @@ class NCursesUI:
# bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile)
if isinstance(event, bb.command.CommandCompleted):
- exitflag = True
+ # stop so the user can see the result of the build, but
+ # also allow them to now exit with a single ^C
+ shutdown = 2
if isinstance(event, bb.command.CommandFailed):
mw.appendText("Command execution failed: %s" % event.error)
time.sleep(2)
@@ -306,13 +315,18 @@ class NCursesUI:
if activetasks:
taw.appendText("Active Tasks:\n")
for task in activetasks.itervalues():
- taw.appendText(task["title"])
+ taw.appendText(task["title"] + '\n')
if failedtasks:
taw.appendText("Failed Tasks:\n")
for task in failedtasks:
- taw.appendText(task["title"])
+ taw.appendText(task["title"] + '\n')
curses.doupdate()
+ except EnvironmentError as ioerror:
+ # ignore interrupted io
+ if ioerror.args[0] == 4:
+ pass
+
except KeyboardInterrupt:
if shutdown == 2:
mw.appendText("Third Keyboard Interrupt, exit.\n")