summaryrefslogtreecommitdiffstats
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)
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>
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 @@
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