summaryrefslogtreecommitdiff
path: root/bitbake/lib/bb/ui/crumbs/runningbuild.py
diff options
context:
space:
mode:
authorBob Foerster <robert@erafx.com>2010-12-17 23:20:39 +0800
committerRichard Purdie <rpurdie@linux.intel.com>2011-01-05 11:13:48 +0000
commit2e0ef25a50c6a31cd6de52dfb31a04b77e694da3 (patch)
treefee195184ef180928f6712b05c1ed223382a6c96 /bitbake/lib/bb/ui/crumbs/runningbuild.py
parent25ac24e02e3e96945e8ac83e16fe27a6b24789b1 (diff)
downloadopenembedded-core-2e0ef25a50c6a31cd6de52dfb31a04b77e694da3.tar.gz
openembedded-core-2e0ef25a50c6a31cd6de52dfb31a04b77e694da3.tar.bz2
openembedded-core-2e0ef25a50c6a31cd6de52dfb31a04b77e694da3.zip
Resurrect alternative UIs
The various alternative UIs have been updated to once again be functional with the latest bitbake internals. Each of the UIs still have much room for functional improvement. In particular, they have been updated to: - interact with the new process based server - handle the current set of events and notifications fired from the server and its associated subsystems (Bitbake rev: b947e7aa405966262c0614cae02e7978ec637095) Signed-off-by: Bob Foerster <robert@erafx.com> Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
Diffstat (limited to 'bitbake/lib/bb/ui/crumbs/runningbuild.py')
-rw-r--r--bitbake/lib/bb/ui/crumbs/runningbuild.py225
1 files changed, 175 insertions, 50 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/runningbuild.py b/bitbake/lib/bb/ui/crumbs/runningbuild.py
index 9730bfd472..4703e6d844 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