summaryrefslogtreecommitdiffstats
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
parent25ac24e02e3e96945e8ac83e16fe27a6b24789b1 (diff)
downloadpoky-2e0ef25a50c6a31cd6de52dfb31a04b77e694da3.tar.gz
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>
-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 61bd0194d6..6d0528953c 100755
--- a/bitbake/bin/bitbake
+++ b/bitbake/bin/bitbake
@@ -71,7 +71,7 @@ def get_ui(config):
71 return getattr(module, interface).main 71 return getattr(module, interface).main
72 except AttributeError: 72 except AttributeError:
73 sys.exit("FATAL: Invalid user interface '%s' specified.\n" 73 sys.exit("FATAL: Invalid user interface '%s' specified.\n"
74 "Valid interfaces: ncurses, depexp, knotty [default]." % interface) 74 "Valid interfaces: depexp, goggle, ncurses, knotty [default]." % interface)
75 75
76 76
77# Display bitbake/OE warnings via the BitBake.Warnings logger, ignoring others""" 77# 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 8bd87108e6..36eca38294 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):
14 14
15 def update(self, x, y): 15 def update(self, x, y):
16 self.progress.set_fraction(float(x)/float(y)) 16 self.progress.set_fraction(float(x)/float(y))
17 self.progress.set_text("%d/%d (%2d %%)" % (x, y, x*100/y)) 17 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 9730bfd472..4703e6d844 100644
--- a/bitbake/lib/bb/ui/crumbs/runningbuild.py
+++ b/bitbake/lib/bb/ui/crumbs/runningbuild.py
@@ -1,3 +1,4 @@
1
1# 2#
2# BitBake Graphical GTK User Interface 3# BitBake Graphical GTK User Interface
3# 4#
@@ -20,9 +21,20 @@
20 21
21import gtk 22import gtk
22import gobject 23import gobject
24import logging
25import time
26import urllib
27import urllib2
28
29class Colors(object):
30 OK = "#ffffff"
31 RUNNING = "#aaffaa"
32 WARNING ="#f88017"
33 ERROR = "#ffaaaa"
23 34
24class RunningBuildModel (gtk.TreeStore): 35class RunningBuildModel (gtk.TreeStore):
25 (COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5) 36 (COL_LOG, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_COLOR, COL_NUM_ACTIVE) = range(7)
37
26 def __init__ (self): 38 def __init__ (self):
27 gtk.TreeStore.__init__ (self, 39 gtk.TreeStore.__init__ (self,
28 gobject.TYPE_STRING, 40 gobject.TYPE_STRING,
@@ -30,7 +42,8 @@ class RunningBuildModel (gtk.TreeStore):
30 gobject.TYPE_STRING, 42 gobject.TYPE_STRING,
31 gobject.TYPE_STRING, 43 gobject.TYPE_STRING,
32 gobject.TYPE_STRING, 44 gobject.TYPE_STRING,
33 gobject.TYPE_BOOLEAN) 45 gobject.TYPE_STRING,
46 gobject.TYPE_INT)
34 47
35class RunningBuild (gobject.GObject): 48class RunningBuild (gobject.GObject):
36 __gsignals__ = { 49 __gsignals__ = {
@@ -63,32 +76,42 @@ class RunningBuild (gobject.GObject):
63 # for the message. 76 # for the message.
64 if hasattr(event, 'pid'): 77 if hasattr(event, 'pid'):
65 pid = event.pid 78 pid = event.pid
66 if pid in self.pids_to_task: 79 if hasattr(event, 'process'):
67 (package, task) = self.pids_to_task[pid] 80 pid = event.process
68 parent = self.tasks_to_iter[(package, task)] 81
82 if pid and pid in self.pids_to_task:
83 (package, task) = self.pids_to_task[pid]
84 parent = self.tasks_to_iter[(package, task)]
69 85
70 if isinstance(event, bb.msg.MsgBase): 86 if(isinstance(event, logging.LogRecord)):
71 # Ignore the "Running task i of n .." 87 if (event.msg.startswith ("Running task")):
72 if (event._message.startswith ("Running task")):
73 return # don't add these to the list 88 return # don't add these to the list
74 89
75 # Set a pretty icon for the message based on it's type. 90 if event.levelno >= logging.ERROR:
76 if isinstance(event, bb.msg.MsgWarn):
77 icon = "dialog-warning"
78 elif isinstance(event, bb.msg.MsgError):
79 icon = "dialog-error" 91 icon = "dialog-error"
92 color = Colors.ERROR
93 elif event.levelno >= logging.WARNING:
94 icon = "dialog-warning"
95 color = Colors.WARNING
80 else: 96 else:
81 icon = None 97 icon = None
98 color = Colors.OK
99
100 # if we know which package we belong to, we'll append onto its list.
101 # otherwise, we'll jump to the top of the master list
102 if parent:
103 tree_add = self.model.append
104 else:
105 tree_add = self.model.prepend
106 tree_add(parent,
107 (None,
108 package,
109 task,
110 event.getMessage(),
111 icon,
112 color,
113 0))
82 114
83 # Add the message to the tree either at the top level if parent is
84 # None otherwise as a descendent of a task.
85 self.model.append (parent,
86 (event.__class__.__name__.split()[-1], # e.g. MsgWarn, MsgError
87 package,
88 task,
89 event._message,
90 icon,
91 False))
92 elif isinstance(event, bb.build.TaskStarted): 115 elif isinstance(event, bb.build.TaskStarted):
93 (package, task) = (event._package, event._task) 116 (package, task) = (event._package, event._task)
94 117
@@ -101,76 +124,142 @@ class RunningBuild (gobject.GObject):
101 if ((package, None) in self.tasks_to_iter): 124 if ((package, None) in self.tasks_to_iter):
102 parent = self.tasks_to_iter[(package, None)] 125 parent = self.tasks_to_iter[(package, None)]
103 else: 126 else:
104 parent = self.model.append (None, (None, 127 parent = self.model.prepend(None, (None,
105 package, 128 package,
106 None, 129 None,
107 "Package: %s" % (package), 130 "Package: %s" % (package),
108 None, 131 None,
109 False)) 132 Colors.OK,
133 0))
110 self.tasks_to_iter[(package, None)] = parent 134 self.tasks_to_iter[(package, None)] = parent
111 135
112 # Because this parent package now has an active child mark it as 136 # Because this parent package now has an active child mark it as
113 # such. 137 # such.
114 self.model.set(parent, self.model.COL_ICON, "gtk-execute") 138 # @todo if parent is already in error, don't mark it green
139 self.model.set(parent, self.model.COL_ICON, "gtk-execute",
140 self.model.COL_COLOR, Colors.RUNNING)
115 141
116 # Add an entry in the model for this task 142 # Add an entry in the model for this task
117 i = self.model.append (parent, (None, 143 i = self.model.append (parent, (None,
118 package, 144 package,
119 task, 145 task,
120 "Task: %s" % (task), 146 "Task: %s" % (task),
121 None, 147 "gtk-execute",
122 False)) 148 Colors.RUNNING,
149 0))
150
151 # update the parent's active task count
152 num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] + 1
153 self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active)
123 154
124 # Save out the iter so that we can find it when we have a message 155 # Save out the iter so that we can find it when we have a message
125 # that we need to attach to a task. 156 # that we need to attach to a task.
126 self.tasks_to_iter[(package, task)] = i 157 self.tasks_to_iter[(package, task)] = i
127 158
128 # Mark this task as active.
129 self.model.set(i, self.model.COL_ICON, "gtk-execute")
130
131 elif isinstance(event, bb.build.TaskBase): 159 elif isinstance(event, bb.build.TaskBase):
160 current = self.tasks_to_iter[(package, task)]
161 parent = self.tasks_to_iter[(package, None)]
162
163 # remove this task from the parent's active count
164 num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] - 1
165 self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active)
132 166
133 if isinstance(event, bb.build.TaskFailed): 167 if isinstance(event, bb.build.TaskFailed):
134 # Mark the task as failed 168 # Mark the task and parent as failed
135 i = self.tasks_to_iter[(package, task)] 169 icon = "dialog-error"
136 self.model.set(i, self.model.COL_ICON, "dialog-error") 170 color = Colors.ERROR
137 171
138 # Mark the parent package as failed 172 logfile = event.logfile
139 i = self.tasks_to_iter[(package, None)] 173 if logfile and os.path.exists(logfile):
140 self.model.set(i, self.model.COL_ICON, "dialog-error") 174 with open(logfile) as f:
175 logdata = f.read()
176 self.model.append(current, ('pastebin', None, None, logdata, 'gtk-error', Colors.OK, 0))
177
178 for i in (current, parent):
179 self.model.set(i, self.model.COL_ICON, icon,
180 self.model.COL_COLOR, color)
141 else: 181 else:
182 icon = None
183 color = Colors.OK
184
142 # Mark the task as inactive 185 # Mark the task as inactive
143 i = self.tasks_to_iter[(package, task)] 186 self.model.set(current, self.model.COL_ICON, icon,
144 self.model.set(i, self.model.COL_ICON, None) 187 self.model.COL_COLOR, color)
145 188
146 # Mark the parent package as inactive 189 # Mark the parent package as inactive, but make sure to
190 # preserve error and active states
147 i = self.tasks_to_iter[(package, None)] 191 i = self.tasks_to_iter[(package, None)]
148 self.model.set(i, self.model.COL_ICON, None) 192 if self.model.get(parent, self.model.COL_ICON) != 'dialog-error':
149 193 self.model.set(parent, self.model.COL_ICON, icon)
194 if num_active == 0:
195 self.model.set(parent, self.model.COL_COLOR, Colors.OK)
150 196
151 # Clear the iters and the pids since when the task goes away the 197 # Clear the iters and the pids since when the task goes away the
152 # pid will no longer be used for messages 198 # pid will no longer be used for messages
153 del self.tasks_to_iter[(package, task)] 199 del self.tasks_to_iter[(package, task)]
154 del self.pids_to_task[pid] 200 del self.pids_to_task[pid]
155 201
202 elif isinstance(event, bb.event.BuildStarted):
203
204 self.model.prepend(None, (None,
205 None,
206 None,
207 "Build Started (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'),
208 None,
209 Colors.OK,
210 0))
156 elif isinstance(event, bb.event.BuildCompleted): 211 elif isinstance(event, bb.event.BuildCompleted):
157 failures = int (event._failures) 212 failures = int (event._failures)
213 self.model.prepend(None, (None,
214 None,
215 None,
216 "Build Completed (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'),
217 None,
218 Colors.OK,
219 0))
158 220
159 # Emit the appropriate signal depending on the number of failures 221 # Emit the appropriate signal depending on the number of failures
160 if (failures > 1): 222 if (failures >= 1):
161 self.emit ("build-failed") 223 self.emit ("build-failed")
162 else: 224 else:
163 self.emit ("build-succeeded") 225 self.emit ("build-succeeded")
164 226
227 elif isinstance(event, bb.event.CacheLoadStarted) and pbar:
228 pbar.set_title("Loading cache")
229 self.progress_total = event.total
230 pbar.update(0, self.progress_total)
231 elif isinstance(event, bb.event.CacheLoadProgress) and pbar:
232 pbar.update(event.current, self.progress_total)
233 elif isinstance(event, bb.event.CacheLoadCompleted) and pbar:
234 pbar.update(self.progress_total, self.progress_total)
235
236 elif isinstance(event, bb.event.ParseStarted) and pbar:
237 pbar.set_title("Processing recipes")
238 self.progress_total = event.total
239 pbar.update(0, self.progress_total)
165 elif isinstance(event, bb.event.ParseProgress) and pbar: 240 elif isinstance(event, bb.event.ParseProgress) and pbar:
166 x = event.sofar 241 pbar.update(event.current, self.progress_total)
167 y = event.total 242 elif isinstance(event, bb.event.ParseCompleted) and pbar:
168 if x == y: 243 pbar.hide()
169 pbar.hide() 244
170 return 245 return
171 pbar.update(x, y) 246
247
248def do_pastebin(text):
249 url = 'http://pastebin.com/api_public.php'
250 params = {'paste_code': text, 'paste_format': 'text'}
251
252 req = urllib2.Request(url, urllib.urlencode(params))
253 response = urllib2.urlopen(req)
254 paste_url = response.read()
255
256 return paste_url
257
172 258
173class RunningBuildTreeView (gtk.TreeView): 259class RunningBuildTreeView (gtk.TreeView):
260 __gsignals__ = {
261 "button_press_event" : "override"
262 }
174 def __init__ (self): 263 def __init__ (self):
175 gtk.TreeView.__init__ (self) 264 gtk.TreeView.__init__ (self)
176 265
@@ -181,6 +270,42 @@ class RunningBuildTreeView (gtk.TreeView):
181 self.append_column (col) 270 self.append_column (col)
182 271
183 # The message of the build. 272 # The message of the build.
184 renderer = gtk.CellRendererText () 273 self.message_renderer = gtk.CellRendererText ()
185 col = gtk.TreeViewColumn ("Message", renderer, text=3) 274 self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=3)
186 self.append_column (col) 275 self.message_column.add_attribute(self.message_renderer, 'background', 5)
276 self.message_renderer.set_property('editable', 5)
277 self.append_column (self.message_column)
278
279 def do_button_press_event(self, event):
280 gtk.TreeView.do_button_press_event(self, event)
281
282 if event.button == 3:
283 selection = super(RunningBuildTreeView, self).get_selection()
284 (model, iter) = selection.get_selected()
285 if iter is not None:
286 can_paste = model.get(iter, model.COL_LOG)[0]
287 if can_paste == 'pastebin':
288 # build a simple menu with a pastebin option
289 menu = gtk.Menu()
290 menuitem = gtk.MenuItem("Send log to pastebin")
291 menu.append(menuitem)
292 menuitem.connect("activate", self.pastebin_handler, (model, iter))
293 menuitem.show()
294 menu.show()
295 menu.popup(None, None, None, event.button, event.time)
296
297 def pastebin_handler(self, widget, data):
298 """
299 Send the log data to pastebin, then add the new paste url to the
300 clipboard.
301 """
302 (model, iter) = data
303 paste_url = do_pastebin(model.get(iter, model.COL_MESSAGE)[0])
304
305 # @todo Provide visual feedback to the user that it is done and that
306 # it worked.
307 print paste_url
308
309 clipboard = gtk.clipboard_get()
310 clipboard.set_text(paste_url)
311 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 48f6f792db..66ef96cdbc 100644
--- a/bitbake/lib/bb/ui/depexp.py
+++ b/bitbake/lib/bb/ui/depexp.py
@@ -19,6 +19,7 @@
19 19
20import gobject 20import gobject
21import gtk 21import gtk
22import Queue
22import threading 23import threading
23import xmlrpclib 24import xmlrpclib
24import bb 25import bb
@@ -32,6 +33,7 @@ from bb.ui.crumbs.progress import ProgressBar
32(TYPE_DEP, TYPE_RDEP) = (0, 1) 33(TYPE_DEP, TYPE_RDEP) = (0, 1)
33(COL_DEP_TYPE, COL_DEP_PARENT, COL_DEP_PACKAGE) = (0, 1, 2) 34(COL_DEP_TYPE, COL_DEP_PARENT, COL_DEP_PACKAGE) = (0, 1, 2)
34 35
36
35class PackageDepView(gtk.TreeView): 37class PackageDepView(gtk.TreeView):
36 def __init__(self, model, dep_type, label): 38 def __init__(self, model, dep_type, label):
37 gtk.TreeView.__init__(self) 39 gtk.TreeView.__init__(self)
@@ -52,6 +54,7 @@ class PackageDepView(gtk.TreeView):
52 self.current = package 54 self.current = package
53 self.filter_model.refilter() 55 self.filter_model.refilter()
54 56
57
55class PackageReverseDepView(gtk.TreeView): 58class PackageReverseDepView(gtk.TreeView):
56 def __init__(self, model, label): 59 def __init__(self, model, label):
57 gtk.TreeView.__init__(self) 60 gtk.TreeView.__init__(self)
@@ -69,6 +72,7 @@ class PackageReverseDepView(gtk.TreeView):
69 self.current = package 72 self.current = package
70 self.filter_model.refilter() 73 self.filter_model.refilter()
71 74
75
72class DepExplorer(gtk.Window): 76class DepExplorer(gtk.Window):
73 def __init__(self): 77 def __init__(self):
74 gtk.Window.__init__(self) 78 gtk.Window.__init__(self)
@@ -90,9 +94,12 @@ class DepExplorer(gtk.Window):
90 scrolled = gtk.ScrolledWindow() 94 scrolled = gtk.ScrolledWindow()
91 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 95 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
92 scrolled.set_shadow_type(gtk.SHADOW_IN) 96 scrolled.set_shadow_type(gtk.SHADOW_IN)
97
93 self.pkg_treeview = gtk.TreeView(self.pkg_model) 98 self.pkg_treeview = gtk.TreeView(self.pkg_model)
94 self.pkg_treeview.get_selection().connect("changed", self.on_cursor_changed) 99 self.pkg_treeview.get_selection().connect("changed", self.on_cursor_changed)
95 self.pkg_treeview.append_column(gtk.TreeViewColumn("Package", gtk.CellRendererText(), text=COL_PKG_NAME)) 100 column = gtk.TreeViewColumn("Package", gtk.CellRendererText(), text=COL_PKG_NAME)
101 self.pkg_treeview.append_column(column)
102 column.set_sort_column_id(COL_PKG_NAME)
96 pane.add1(scrolled) 103 pane.add1(scrolled)
97 scrolled.add(self.pkg_treeview) 104 scrolled.add(self.pkg_treeview)
98 105
@@ -158,7 +165,6 @@ class DepExplorer(gtk.Window):
158 165
159 166
160def parse(depgraph, pkg_model, depends_model): 167def parse(depgraph, pkg_model, depends_model):
161
162 for package in depgraph["pn"]: 168 for package in depgraph["pn"]:
163 pkg_model.set(pkg_model.append(), COL_PKG_NAME, package) 169 pkg_model.set(pkg_model.append(), COL_PKG_NAME, package)
164 170
@@ -176,6 +182,7 @@ def parse(depgraph, pkg_model, depends_model):
176 COL_DEP_PARENT, package, 182 COL_DEP_PARENT, package,
177 COL_DEP_PACKAGE, rdepend) 183 COL_DEP_PACKAGE, rdepend)
178 184
185
179class gtkthread(threading.Thread): 186class gtkthread(threading.Thread):
180 quit = threading.Event() 187 quit = threading.Event()
181 def __init__(self, shutdown): 188 def __init__(self, shutdown):
@@ -189,8 +196,8 @@ class gtkthread(threading.Thread):
189 gtk.main() 196 gtk.main()
190 gtkthread.quit.set() 197 gtkthread.quit.set()
191 198
192def main(server, eventHandler):
193 199
200def main(server, eventHandler):
194 try: 201 try:
195 cmdline = server.runCommand(["getCmdLineAction"]) 202 cmdline = server.runCommand(["getCmdLineAction"])
196 if not cmdline or cmdline[0] != "generateDotGraph": 203 if not cmdline or cmdline[0] != "generateDotGraph":
@@ -214,25 +221,54 @@ def main(server, eventHandler):
214 pbar = ProgressBar(dep) 221 pbar = ProgressBar(dep)
215 gtk.gdk.threads_leave() 222 gtk.gdk.threads_leave()
216 223
224 progress_total = 0
217 while True: 225 while True:
218 try: 226 try:
219 event = eventHandler.waitEvent(0.25) 227 event = eventHandler.waitEvent(0.25)
220 if gtkthread.quit.isSet(): 228 if gtkthread.quit.isSet():
229 server.runCommand(["stateStop"])
221 break 230 break
222 231
223 if event is None: 232 if event is None:
224 continue 233 continue
234
235 if isinstance(event, bb.event.CacheLoadStarted):
236 progress_total = event.total
237 gtk.gdk.threads_enter()
238 pbar.set_title("Loading Cache")
239 pbar.update(0, progress_total)
240 gtk.gdk.threads_leave()
241
242 if isinstance(event, bb.event.CacheLoadProgress):
243 x = event.current
244 gtk.gdk.threads_enter()
245 pbar.update(x, progress_total)
246 gtk.gdk.threads_leave()
247 continue
248
249 if isinstance(event, bb.event.CacheLoadCompleted):
250 gtk.gdk.threads_enter()
251 pbar.update(progress_total, progress_total)
252 gtk.gdk.threads_leave()
253 continue
254
255 if isinstance(event, bb.event.ParseStarted):
256 progress_total = event.total
257 gtk.gdk.threads_enter()
258 pbar.set_title("Processing recipes")
259 pbar.update(0, progress_total)
260 gtk.gdk.threads_leave()
261
225 if isinstance(event, bb.event.ParseProgress): 262 if isinstance(event, bb.event.ParseProgress):
226 x = event.sofar 263 x = event.current
227 y = event.total
228 if x == y:
229 print(("\nParsing finished. %d cached, %d parsed, %d skipped, %d masked, %d errors."
230 % ( event.cached, event.parsed, event.skipped, event.masked, event.errors)))
231 pbar.hide()
232 continue
233 gtk.gdk.threads_enter() 264 gtk.gdk.threads_enter()
234 pbar.update(x, y) 265 pbar.update(x, progress_total)
235 gtk.gdk.threads_leave() 266 gtk.gdk.threads_leave()
267
268 continue
269
270 if isinstance(event, bb.event.ParseCompleted):
271 pbar.hide()
236 continue 272 continue
237 273
238 if isinstance(event, bb.event.DepTreeGenerated): 274 if isinstance(event, bb.event.DepTreeGenerated):
@@ -242,16 +278,22 @@ def main(server, eventHandler):
242 278
243 if isinstance(event, bb.command.CommandCompleted): 279 if isinstance(event, bb.command.CommandCompleted):
244 continue 280 continue
281
245 if isinstance(event, bb.command.CommandFailed): 282 if isinstance(event, bb.command.CommandFailed):
246 print("Command execution failed: %s" % event.error) 283 print("Command execution failed: %s" % event.error)
247 return event.exitcode 284 return event.exitcode
285
248 if isinstance(event, bb.command.CommandExit): 286 if isinstance(event, bb.command.CommandExit):
249 return event.exitcode 287 return event.exitcode
288
250 if isinstance(event, bb.cooker.CookerExit): 289 if isinstance(event, bb.cooker.CookerExit):
251 break 290 break
252 291
253 continue 292 continue
254 293 except EnvironmentError as ioerror:
294 # ignore interrupted io
295 if ioerror.args[0] == 4:
296 pass
255 except KeyboardInterrupt: 297 except KeyboardInterrupt:
256 if shutdown == 2: 298 if shutdown == 2:
257 print("\nThird Keyboard Interrupt, exit.\n") 299 print("\nThird Keyboard Interrupt, exit.\n")
diff --git a/bitbake/lib/bb/ui/goggle.py b/bitbake/lib/bb/ui/goggle.py
index 40923ba88d..3c6a014dc2 100644
--- a/bitbake/lib/bb/ui/goggle.py
+++ b/bitbake/lib/bb/ui/goggle.py
@@ -24,19 +24,32 @@ import xmlrpclib
24from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild 24from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
25from bb.ui.crumbs.progress import ProgressBar 25from bb.ui.crumbs.progress import ProgressBar
26 26
27import Queue
28
29
27def event_handle_idle_func (eventHandler, build, pbar): 30def event_handle_idle_func (eventHandler, build, pbar):
28 31
29 # Consume as many messages as we can in the time available to us 32 # Consume as many messages as we can in the time available to us
30 event = eventHandler.getEvent() 33 event = eventHandler.getEvent()
31 while event: 34 while event:
32 build.handle_event (event, pbar)
33 event = eventHandler.getEvent() 35 event = eventHandler.getEvent()
36 build.handle_event (event, pbar)
34 37
35 return True 38 return True
36 39
37def scroll_tv_cb (model, path, iter, view): 40def scroll_tv_cb (model, path, iter, view):
38 view.scroll_to_cell (path) 41 view.scroll_to_cell (path)
39 42
43
44# @todo hook these into the GUI so the user has feedback...
45def running_build_failed_cb (running_build):
46 pass
47
48
49def running_build_succeeded_cb (running_build):
50 pass
51
52
40class MainWindow (gtk.Window): 53class MainWindow (gtk.Window):
41 def __init__ (self): 54 def __init__ (self):
42 gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL) 55 gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
@@ -49,6 +62,7 @@ class MainWindow (gtk.Window):
49 self.set_default_size(640, 480) 62 self.set_default_size(640, 480)
50 scrolled_window.add (self.cur_build_tv) 63 scrolled_window.add (self.cur_build_tv)
51 64
65
52def main (server, eventHandler): 66def main (server, eventHandler):
53 gobject.threads_init() 67 gobject.threads_init()
54 gtk.gdk.threads_init() 68 gtk.gdk.threads_init()
@@ -61,9 +75,11 @@ def main (server, eventHandler):
61 running_build = RunningBuild () 75 running_build = RunningBuild ()
62 window.cur_build_tv.set_model (running_build.model) 76 window.cur_build_tv.set_model (running_build.model)
63 running_build.model.connect("row-inserted", scroll_tv_cb, window.cur_build_tv) 77 running_build.model.connect("row-inserted", scroll_tv_cb, window.cur_build_tv)
78 running_build.connect ("build-succeeded", running_build_succeeded_cb)
79 running_build.connect ("build-failed", running_build_failed_cb)
80
64 try: 81 try:
65 cmdline = server.runCommand(["getCmdLineAction"]) 82 cmdline = server.runCommand(["getCmdLineAction"])
66 print(cmdline)
67 if not cmdline: 83 if not cmdline:
68 return 1 84 return 1
69 ret = server.runCommand(cmdline) 85 ret = server.runCommand(cmdline)
@@ -76,10 +92,18 @@ def main (server, eventHandler):
76 92
77 # Use a timeout function for probing the event queue to find out if we 93 # Use a timeout function for probing the event queue to find out if we
78 # have a message waiting for us. 94 # have a message waiting for us.
79 gobject.timeout_add (200, 95 gobject.timeout_add (100,
80 event_handle_idle_func, 96 event_handle_idle_func,
81 eventHandler, 97 eventHandler,
82 running_build, 98 running_build,
83 pbar) 99 pbar)
84 100
85 gtk.main() 101 try:
102 gtk.main()
103 except EnvironmentError as ioerror:
104 # ignore interrupted io
105 if ioerror.args[0] == 4:
106 pass
107 finally:
108 server.runCommand(["stateStop"])
109
diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py
index 2e9c6f3f22..4d87a3d7cd 100644
--- a/bitbake/lib/bb/ui/knotty.py
+++ b/bitbake/lib/bb/ui/knotty.py
@@ -228,6 +228,10 @@ def main(server, eventHandler):
228 228
229 logger.error("Unknown event: %s", event) 229 logger.error("Unknown event: %s", event)
230 230
231 except EnvironmentError as ioerror:
232 # ignore interrupted io
233 if ioerror.args[0] == 4:
234 pass
231 except KeyboardInterrupt: 235 except KeyboardInterrupt:
232 if shutdown == 2: 236 if shutdown == 2:
233 print("\nThird Keyboard Interrupt, exit.\n") 237 print("\nThird Keyboard Interrupt, exit.\n")
diff --git a/bitbake/lib/bb/ui/ncurses.py b/bitbake/lib/bb/ui/ncurses.py
index 1db4ec173b..469f1b7309 100644
--- a/bitbake/lib/bb/ui/ncurses.py
+++ b/bitbake/lib/bb/ui/ncurses.py
@@ -44,8 +44,9 @@
44 44
45""" 45"""
46 46
47from __future__ import division
48 47
48from __future__ import division
49import logging
49import os, sys, curses, itertools, time 50import os, sys, curses, itertools, time
50import bb 51import bb
51import xmlrpclib 52import xmlrpclib
@@ -246,29 +247,35 @@ class NCursesUI:
246 event = eventHandler.waitEvent(0.25) 247 event = eventHandler.waitEvent(0.25)
247 if not event: 248 if not event:
248 continue 249 continue
250
249 helper.eventHandler(event) 251 helper.eventHandler(event)
250 #mw.appendText("%s\n" % event[0])
251 if isinstance(event, bb.build.TaskBase): 252 if isinstance(event, bb.build.TaskBase):
252 mw.appendText("NOTE: %s\n" % event._message) 253 mw.appendText("NOTE: %s\n" % event._message)
253 if isinstance(event, bb.msg.MsgDebug): 254 if isinstance(event, logging.LogRecord):
254 mw.appendText('DEBUG: ' + event._message + '\n') 255 mw.appendText(logging.getLevelName(event.levelno) + ': ' + event.getMessage() + '\n')
255 if isinstance(event, bb.msg.MsgNote): 256
256 mw.appendText('NOTE: ' + event._message + '\n') 257 if isinstance(event, bb.event.CacheLoadStarted):
257 if isinstance(event, bb.msg.MsgWarn): 258 self.parse_total = event.total
258 mw.appendText('WARNING: ' + event._message + '\n') 259 if isinstance(event, bb.event.CacheLoadProgress):
259 if isinstance(event, bb.msg.MsgError): 260 x = event.current
260 mw.appendText('ERROR: ' + event._message + '\n') 261 y = self.parse_total
261 if isinstance(event, bb.msg.MsgFatal): 262 mw.setStatus("Loading Cache: %s [%2d %%]" % ( next(parsespin), x*100/y ) )
262 mw.appendText('FATAL: ' + event._message + '\n') 263 if isinstance(event, bb.event.CacheLoadCompleted):
264 mw.setStatus("Idle")
265 mw.appendText("Loaded %d entries from dependency cache.\n"
266 % ( event.num_entries))
267
268 if isinstance(event, bb.event.ParseStarted):
269 self.parse_total = event.total
263 if isinstance(event, bb.event.ParseProgress): 270 if isinstance(event, bb.event.ParseProgress):
264 x = event.sofar 271 x = event.current
265 y = event.total 272 y = self.parse_total
266 if x == y: 273 mw.setStatus("Parsing Recipes: %s [%2d %%]" % ( next(parsespin), x*100/y ) )
267 mw.setStatus("Idle") 274 if isinstance(event, bb.event.ParseCompleted):
268 mw.appendText("Parsing finished. %d cached, %d parsed, %d skipped, %d masked." 275 mw.setStatus("Idle")
276 mw.appendText("Parsing finished. %d cached, %d parsed, %d skipped, %d masked.\n"
269 % ( event.cached, event.parsed, event.skipped, event.masked )) 277 % ( event.cached, event.parsed, event.skipped, event.masked ))
270 else: 278
271 mw.setStatus("Parsing: %s (%04d/%04d) [%2d %%]" % ( next(parsespin), x, y, x*100//y ) )
272# if isinstance(event, bb.build.TaskFailed): 279# if isinstance(event, bb.build.TaskFailed):
273# if event.logfile: 280# if event.logfile:
274# if data.getVar("BBINCLUDELOGS", d): 281# if data.getVar("BBINCLUDELOGS", d):
@@ -289,7 +296,9 @@ class NCursesUI:
289# bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile) 296# bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile)
290 297
291 if isinstance(event, bb.command.CommandCompleted): 298 if isinstance(event, bb.command.CommandCompleted):
292 exitflag = True 299 # stop so the user can see the result of the build, but
300 # also allow them to now exit with a single ^C
301 shutdown = 2
293 if isinstance(event, bb.command.CommandFailed): 302 if isinstance(event, bb.command.CommandFailed):
294 mw.appendText("Command execution failed: %s" % event.error) 303 mw.appendText("Command execution failed: %s" % event.error)
295 time.sleep(2) 304 time.sleep(2)
@@ -306,13 +315,18 @@ class NCursesUI:
306 if activetasks: 315 if activetasks:
307 taw.appendText("Active Tasks:\n") 316 taw.appendText("Active Tasks:\n")
308 for task in activetasks.itervalues(): 317 for task in activetasks.itervalues():
309 taw.appendText(task["title"]) 318 taw.appendText(task["title"] + '\n')
310 if failedtasks: 319 if failedtasks:
311 taw.appendText("Failed Tasks:\n") 320 taw.appendText("Failed Tasks:\n")
312 for task in failedtasks: 321 for task in failedtasks:
313 taw.appendText(task["title"]) 322 taw.appendText(task["title"] + '\n')
314 323
315 curses.doupdate() 324 curses.doupdate()
325 except EnvironmentError as ioerror:
326 # ignore interrupted io
327 if ioerror.args[0] == 4:
328 pass
329
316 except KeyboardInterrupt: 330 except KeyboardInterrupt:
317 if shutdown == 2: 331 if shutdown == 2:
318 mw.appendText("Third Keyboard Interrupt, exit.\n") 332 mw.appendText("Third Keyboard Interrupt, exit.\n")