summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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")