summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/ui/crumbs/runningbuild.py
diff options
context:
space:
mode:
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