diff options
Diffstat (limited to 'bitbake/lib')
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/progress.py | 2 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/runningbuild.py | 225 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/depexp.py | 66 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/goggle.py | 32 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/knotty.py | 4 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/ncurses.py | 58 |
6 files changed, 298 insertions, 89 deletions
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") |