diff options
| author | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-05-13 14:11:02 +0100 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-06-02 08:24:03 +0100 |
| commit | 8a93f5f32e141b4d153156152b9aeb1af25325ba (patch) | |
| tree | 53e312db1ade4f78829221fb16c8cb713a846f53 /bitbake/lib/bb/ui/crumbs/runningbuild.py | |
| parent | 685628ebc459dd1a09ce0dafc88a159aaaead732 (diff) | |
| download | poky-8a93f5f32e141b4d153156152b9aeb1af25325ba.tar.gz | |
bitbake: goggle/image-writer: Drop since bitrotting and no longer used
The upgrade to python3 is the final nail in the coffin for image-writer
and the goggle UI. Neither seem used or recieve patches and are based
on old versions of GTK+ so drop them, and the remaining crumbs support
pieces.
(Bitbake rev: ee7df1ca00c76f755057c157c093294efb9078d8)
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb/ui/crumbs/runningbuild.py')
| -rw-r--r-- | bitbake/lib/bb/ui/crumbs/runningbuild.py | 551 |
1 files changed, 0 insertions, 551 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/runningbuild.py b/bitbake/lib/bb/ui/crumbs/runningbuild.py deleted file mode 100644 index 9b695ac2ed..0000000000 --- a/bitbake/lib/bb/ui/crumbs/runningbuild.py +++ /dev/null | |||
| @@ -1,551 +0,0 @@ | |||
| 1 | |||
| 2 | # | ||
| 3 | # BitBake Graphical GTK User Interface | ||
| 4 | # | ||
| 5 | # Copyright (C) 2008 Intel Corporation | ||
| 6 | # | ||
| 7 | # Authored by Rob Bradford <rob@linux.intel.com> | ||
| 8 | # | ||
| 9 | # This program is free software; you can redistribute it and/or modify | ||
| 10 | # it under the terms of the GNU General Public License version 2 as | ||
| 11 | # published by the Free Software Foundation. | ||
| 12 | # | ||
| 13 | # This program is distributed in the hope that it will be useful, | ||
| 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | # GNU General Public License for more details. | ||
| 17 | # | ||
| 18 | # You should have received a copy of the GNU General Public License along | ||
| 19 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
| 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 21 | |||
| 22 | import gtk | ||
| 23 | import gobject | ||
| 24 | import logging | ||
| 25 | import time | ||
| 26 | import urllib.request, urllib.parse, urllib.error | ||
| 27 | import urllib.request, urllib.error, urllib.parse | ||
| 28 | import pango | ||
| 29 | from bb.ui.crumbs.hobcolor import HobColors | ||
| 30 | from bb.ui.crumbs.hobwidget import HobWarpCellRendererText, HobCellRendererPixbuf | ||
| 31 | |||
| 32 | class RunningBuildModel (gtk.TreeStore): | ||
| 33 | (COL_LOG, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_COLOR, COL_NUM_ACTIVE) = list(range(7)) | ||
| 34 | |||
| 35 | def __init__ (self): | ||
| 36 | gtk.TreeStore.__init__ (self, | ||
| 37 | gobject.TYPE_STRING, | ||
| 38 | gobject.TYPE_STRING, | ||
| 39 | gobject.TYPE_STRING, | ||
| 40 | gobject.TYPE_STRING, | ||
| 41 | gobject.TYPE_STRING, | ||
| 42 | gobject.TYPE_STRING, | ||
| 43 | gobject.TYPE_INT) | ||
| 44 | |||
| 45 | def failure_model_filter(self, model, it): | ||
| 46 | color = model.get(it, self.COL_COLOR)[0] | ||
| 47 | if not color: | ||
| 48 | return False | ||
| 49 | if color == HobColors.ERROR or color == HobColors.WARNING: | ||
| 50 | return True | ||
| 51 | return False | ||
| 52 | |||
| 53 | def failure_model(self): | ||
| 54 | model = self.filter_new() | ||
| 55 | model.set_visible_func(self.failure_model_filter) | ||
| 56 | return model | ||
| 57 | |||
| 58 | def foreach_cell_func(self, model, path, iter, usr_data=None): | ||
| 59 | if model.get_value(iter, self.COL_ICON) == "gtk-execute": | ||
| 60 | model.set(iter, self.COL_ICON, "") | ||
| 61 | |||
| 62 | def close_task_refresh(self): | ||
| 63 | self.foreach(self.foreach_cell_func, None) | ||
| 64 | |||
| 65 | class RunningBuild (gobject.GObject): | ||
| 66 | __gsignals__ = { | ||
| 67 | 'build-started' : (gobject.SIGNAL_RUN_LAST, | ||
| 68 | gobject.TYPE_NONE, | ||
| 69 | ()), | ||
| 70 | 'build-succeeded' : (gobject.SIGNAL_RUN_LAST, | ||
| 71 | gobject.TYPE_NONE, | ||
| 72 | ()), | ||
| 73 | 'build-failed' : (gobject.SIGNAL_RUN_LAST, | ||
| 74 | gobject.TYPE_NONE, | ||
| 75 | ()), | ||
| 76 | 'build-complete' : (gobject.SIGNAL_RUN_LAST, | ||
| 77 | gobject.TYPE_NONE, | ||
| 78 | ()), | ||
| 79 | 'build-aborted' : (gobject.SIGNAL_RUN_LAST, | ||
| 80 | gobject.TYPE_NONE, | ||
| 81 | ()), | ||
| 82 | 'task-started' : (gobject.SIGNAL_RUN_LAST, | ||
| 83 | gobject.TYPE_NONE, | ||
| 84 | (gobject.TYPE_PYOBJECT,)), | ||
| 85 | 'log-error' : (gobject.SIGNAL_RUN_LAST, | ||
| 86 | gobject.TYPE_NONE, | ||
| 87 | ()), | ||
| 88 | 'log-warning' : (gobject.SIGNAL_RUN_LAST, | ||
| 89 | gobject.TYPE_NONE, | ||
| 90 | ()), | ||
| 91 | 'disk-full' : (gobject.SIGNAL_RUN_LAST, | ||
| 92 | gobject.TYPE_NONE, | ||
| 93 | ()), | ||
| 94 | 'no-provider' : (gobject.SIGNAL_RUN_LAST, | ||
| 95 | gobject.TYPE_NONE, | ||
| 96 | (gobject.TYPE_PYOBJECT,)), | ||
| 97 | 'log' : (gobject.SIGNAL_RUN_LAST, | ||
| 98 | gobject.TYPE_NONE, | ||
| 99 | (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT,)), | ||
| 100 | } | ||
| 101 | pids_to_task = {} | ||
| 102 | tasks_to_iter = {} | ||
| 103 | |||
| 104 | def __init__ (self, sequential=False): | ||
| 105 | gobject.GObject.__init__ (self) | ||
| 106 | self.model = RunningBuildModel() | ||
| 107 | self.sequential = sequential | ||
| 108 | self.buildaborted = False | ||
| 109 | |||
| 110 | def reset (self): | ||
| 111 | self.pids_to_task.clear() | ||
| 112 | self.tasks_to_iter.clear() | ||
| 113 | self.model.clear() | ||
| 114 | |||
| 115 | def handle_event (self, event, pbar=None): | ||
| 116 | # Handle an event from the event queue, this may result in updating | ||
| 117 | # the model and thus the UI. Or it may be to tell us that the build | ||
| 118 | # has finished successfully (or not, as the case may be.) | ||
| 119 | |||
| 120 | parent = None | ||
| 121 | pid = 0 | ||
| 122 | package = None | ||
| 123 | task = None | ||
| 124 | |||
| 125 | # If we have a pid attached to this message/event try and get the | ||
| 126 | # (package, task) pair for it. If we get that then get the parent iter | ||
| 127 | # for the message. | ||
| 128 | if hasattr(event, 'pid'): | ||
| 129 | pid = event.pid | ||
| 130 | if hasattr(event, 'process'): | ||
| 131 | pid = event.process | ||
| 132 | |||
| 133 | if pid and pid in self.pids_to_task: | ||
| 134 | (package, task) = self.pids_to_task[pid] | ||
| 135 | parent = self.tasks_to_iter[(package, task)] | ||
| 136 | |||
| 137 | if(isinstance(event, logging.LogRecord)): | ||
| 138 | if event.taskpid == 0 or event.levelno > logging.INFO: | ||
| 139 | self.emit("log", "handle", event) | ||
| 140 | # FIXME: this is a hack! More info in Yocto #1433 | ||
| 141 | # http://bugzilla.pokylinux.org/show_bug.cgi?id=1433, temporarily | ||
| 142 | # mask the error message as it's not informative for the user. | ||
| 143 | if event.msg.startswith("Execution of event handler 'run_buildstats' failed"): | ||
| 144 | return | ||
| 145 | |||
| 146 | if (event.levelno < logging.INFO or | ||
| 147 | event.msg.startswith("Running task")): | ||
| 148 | return # don't add these to the list | ||
| 149 | |||
| 150 | if event.levelno >= logging.ERROR: | ||
| 151 | icon = "dialog-error" | ||
| 152 | color = HobColors.ERROR | ||
| 153 | self.emit("log-error") | ||
| 154 | elif event.levelno >= logging.WARNING: | ||
| 155 | icon = "dialog-warning" | ||
| 156 | color = HobColors.WARNING | ||
| 157 | self.emit("log-warning") | ||
| 158 | else: | ||
| 159 | icon = None | ||
| 160 | color = HobColors.OK | ||
| 161 | |||
| 162 | # if we know which package we belong to, we'll append onto its list. | ||
| 163 | # otherwise, we'll jump to the top of the master list | ||
| 164 | if self.sequential or not parent: | ||
| 165 | tree_add = self.model.append | ||
| 166 | else: | ||
| 167 | tree_add = self.model.prepend | ||
| 168 | tree_add(parent, | ||
| 169 | (None, | ||
| 170 | package, | ||
| 171 | task, | ||
| 172 | event.getMessage(), | ||
| 173 | icon, | ||
| 174 | color, | ||
| 175 | 0)) | ||
| 176 | |||
| 177 | # if there are warnings while processing a package | ||
| 178 | # (parent), mark the task with warning color; | ||
| 179 | # in case there are errors, the updates will be | ||
| 180 | # handled on TaskFailed. | ||
| 181 | if color == HobColors.WARNING and parent: | ||
| 182 | self.model.set(parent, self.model.COL_COLOR, color) | ||
| 183 | if task: #then we have a parent (package), and update it's color | ||
| 184 | self.model.set(self.tasks_to_iter[(package, None)], self.model.COL_COLOR, color) | ||
| 185 | |||
| 186 | elif isinstance(event, bb.build.TaskStarted): | ||
| 187 | (package, task) = (event._package, event._task) | ||
| 188 | |||
| 189 | # Save out this PID. | ||
| 190 | self.pids_to_task[pid] = (package, task) | ||
| 191 | |||
| 192 | # Check if we already have this package in our model. If so then | ||
| 193 | # that can be the parent for the task. Otherwise we create a new | ||
| 194 | # top level for the package. | ||
| 195 | if ((package, None) in self.tasks_to_iter): | ||
| 196 | parent = self.tasks_to_iter[(package, None)] | ||
| 197 | else: | ||
| 198 | if self.sequential: | ||
| 199 | add = self.model.append | ||
| 200 | else: | ||
| 201 | add = self.model.prepend | ||
| 202 | parent = add(None, (None, | ||
| 203 | package, | ||
| 204 | None, | ||
| 205 | "Package: %s" % (package), | ||
| 206 | None, | ||
| 207 | HobColors.OK, | ||
| 208 | 0)) | ||
| 209 | self.tasks_to_iter[(package, None)] = parent | ||
| 210 | |||
| 211 | # Because this parent package now has an active child mark it as | ||
| 212 | # such. | ||
| 213 | self.model.set(parent, self.model.COL_ICON, "gtk-execute") | ||
| 214 | parent_color = self.model.get(parent, self.model.COL_COLOR)[0] | ||
| 215 | if parent_color != HobColors.ERROR and parent_color != HobColors.WARNING: | ||
| 216 | self.model.set(parent, self.model.COL_COLOR, HobColors.RUNNING) | ||
| 217 | |||
| 218 | # Add an entry in the model for this task | ||
| 219 | i = self.model.append (parent, (None, | ||
| 220 | package, | ||
| 221 | task, | ||
| 222 | "Task: %s" % (task), | ||
| 223 | "gtk-execute", | ||
| 224 | HobColors.RUNNING, | ||
| 225 | 0)) | ||
| 226 | |||
| 227 | # update the parent's active task count | ||
| 228 | num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] + 1 | ||
| 229 | self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active) | ||
| 230 | |||
| 231 | # Save out the iter so that we can find it when we have a message | ||
| 232 | # that we need to attach to a task. | ||
| 233 | self.tasks_to_iter[(package, task)] = i | ||
| 234 | |||
| 235 | elif isinstance(event, bb.build.TaskBase): | ||
| 236 | self.emit("log", "info", event._message) | ||
| 237 | current = self.tasks_to_iter[(package, task)] | ||
| 238 | parent = self.tasks_to_iter[(package, None)] | ||
| 239 | |||
| 240 | # remove this task from the parent's active count | ||
| 241 | num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] - 1 | ||
| 242 | self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active) | ||
| 243 | |||
| 244 | if isinstance(event, bb.build.TaskFailed): | ||
| 245 | # Mark the task and parent as failed | ||
| 246 | icon = "dialog-error" | ||
| 247 | color = HobColors.ERROR | ||
| 248 | |||
| 249 | logfile = event.logfile | ||
| 250 | if logfile and os.path.exists(logfile): | ||
| 251 | with open(logfile) as f: | ||
| 252 | logdata = f.read() | ||
| 253 | self.model.append(current, ('pastebin', None, None, logdata, 'gtk-error', HobColors.OK, 0)) | ||
| 254 | |||
| 255 | for i in (current, parent): | ||
| 256 | self.model.set(i, self.model.COL_ICON, icon, | ||
| 257 | self.model.COL_COLOR, color) | ||
| 258 | else: | ||
| 259 | # Mark the parent package and the task as inactive, | ||
| 260 | # but make sure to preserve error, warnings and active | ||
| 261 | # states | ||
| 262 | parent_color = self.model.get(parent, self.model.COL_COLOR)[0] | ||
| 263 | task_color = self.model.get(current, self.model.COL_COLOR)[0] | ||
| 264 | |||
| 265 | # Mark the task as inactive | ||
| 266 | self.model.set(current, self.model.COL_ICON, None) | ||
| 267 | if task_color != HobColors.ERROR: | ||
| 268 | if task_color == HobColors.WARNING: | ||
| 269 | self.model.set(current, self.model.COL_ICON, 'dialog-warning') | ||
| 270 | else: | ||
| 271 | self.model.set(current, self.model.COL_COLOR, HobColors.OK) | ||
| 272 | |||
| 273 | # Mark the parent as inactive | ||
| 274 | if parent_color != HobColors.ERROR: | ||
| 275 | if parent_color == HobColors.WARNING: | ||
| 276 | self.model.set(parent, self.model.COL_ICON, "dialog-warning") | ||
| 277 | else: | ||
| 278 | self.model.set(parent, self.model.COL_ICON, None) | ||
| 279 | if num_active == 0: | ||
| 280 | self.model.set(parent, self.model.COL_COLOR, HobColors.OK) | ||
| 281 | |||
| 282 | # Clear the iters and the pids since when the task goes away the | ||
| 283 | # pid will no longer be used for messages | ||
| 284 | del self.tasks_to_iter[(package, task)] | ||
| 285 | del self.pids_to_task[pid] | ||
| 286 | |||
| 287 | elif isinstance(event, bb.event.BuildStarted): | ||
| 288 | |||
| 289 | self.emit("build-started") | ||
| 290 | self.model.prepend(None, (None, | ||
| 291 | None, | ||
| 292 | None, | ||
| 293 | "Build Started (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'), | ||
| 294 | None, | ||
| 295 | HobColors.OK, | ||
| 296 | 0)) | ||
| 297 | if pbar: | ||
| 298 | pbar.update(0, self.progress_total) | ||
| 299 | pbar.set_title(bb.event.getName(event)) | ||
| 300 | |||
| 301 | elif isinstance(event, bb.event.BuildCompleted): | ||
| 302 | failures = int (event._failures) | ||
| 303 | self.model.prepend(None, (None, | ||
| 304 | None, | ||
| 305 | None, | ||
| 306 | "Build Completed (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'), | ||
| 307 | None, | ||
| 308 | HobColors.OK, | ||
| 309 | 0)) | ||
| 310 | |||
| 311 | # Emit the appropriate signal depending on the number of failures | ||
| 312 | if self.buildaborted: | ||
| 313 | self.emit ("build-aborted") | ||
| 314 | self.buildaborted = False | ||
| 315 | elif (failures >= 1): | ||
| 316 | self.emit ("build-failed") | ||
| 317 | else: | ||
| 318 | self.emit ("build-succeeded") | ||
| 319 | # Emit a generic "build-complete" signal for things wishing to | ||
| 320 | # handle when the build is finished | ||
| 321 | self.emit("build-complete") | ||
| 322 | # reset the all cell's icon indicator | ||
| 323 | self.model.close_task_refresh() | ||
| 324 | if pbar: | ||
| 325 | pbar.set_text(event.msg) | ||
| 326 | |||
| 327 | elif isinstance(event, bb.event.DiskFull): | ||
| 328 | self.buildaborted = True | ||
| 329 | self.emit("disk-full") | ||
| 330 | |||
| 331 | elif isinstance(event, bb.command.CommandFailed): | ||
| 332 | self.emit("log", "error", "Command execution failed: %s" % (event.error)) | ||
| 333 | if event.error.startswith("Exited with"): | ||
| 334 | # If the command fails with an exit code we're done, emit the | ||
| 335 | # generic signal for the UI to notify the user | ||
| 336 | self.emit("build-complete") | ||
| 337 | # reset the all cell's icon indicator | ||
| 338 | self.model.close_task_refresh() | ||
| 339 | |||
| 340 | elif isinstance(event, bb.event.CacheLoadStarted) and pbar: | ||
| 341 | pbar.set_title("Loading cache") | ||
| 342 | self.progress_total = event.total | ||
| 343 | pbar.update(0, self.progress_total) | ||
| 344 | elif isinstance(event, bb.event.CacheLoadProgress) and pbar: | ||
| 345 | pbar.update(event.current, self.progress_total) | ||
| 346 | elif isinstance(event, bb.event.CacheLoadCompleted) and pbar: | ||
| 347 | pbar.update(self.progress_total, self.progress_total) | ||
| 348 | pbar.hide() | ||
| 349 | elif isinstance(event, bb.event.ParseStarted) and pbar: | ||
| 350 | if event.total == 0: | ||
| 351 | return | ||
| 352 | pbar.set_title("Processing recipes") | ||
| 353 | self.progress_total = event.total | ||
| 354 | pbar.update(0, self.progress_total) | ||
| 355 | elif isinstance(event, bb.event.ParseProgress) and pbar: | ||
| 356 | pbar.update(event.current, self.progress_total) | ||
| 357 | elif isinstance(event, bb.event.ParseCompleted) and pbar: | ||
| 358 | pbar.hide() | ||
| 359 | #using runqueue events as many as possible to update the progress bar | ||
| 360 | elif isinstance(event, bb.runqueue.runQueueTaskFailed): | ||
| 361 | self.emit("log", "error", "Task %s (%s) failed with exit code '%s'" % (event.taskid, event.taskstring, event.exitcode)) | ||
| 362 | elif isinstance(event, bb.runqueue.sceneQueueTaskFailed): | ||
| 363 | self.emit("log", "warn", "Setscene task %s (%s) failed with exit code '%s' - real task will be run instead" \ | ||
| 364 | % (event.taskid, event.taskstring, event.exitcode)) | ||
| 365 | elif isinstance(event, (bb.runqueue.runQueueTaskStarted, bb.runqueue.sceneQueueTaskStarted)): | ||
| 366 | if isinstance(event, bb.runqueue.sceneQueueTaskStarted): | ||
| 367 | self.emit("log", "info", "Running setscene task %d of %d (%s)" % \ | ||
| 368 | (event.stats.completed + event.stats.active + event.stats.failed + 1, | ||
| 369 | event.stats.total, event.taskstring)) | ||
| 370 | else: | ||
| 371 | if event.noexec: | ||
| 372 | tasktype = 'noexec task' | ||
| 373 | else: | ||
| 374 | tasktype = 'task' | ||
| 375 | self.emit("log", "info", "Running %s %s of %s (ID: %s, %s)" % \ | ||
| 376 | (tasktype, event.stats.completed + event.stats.active + event.stats.failed + 1, | ||
| 377 | event.stats.total, event.taskid, event.taskstring)) | ||
| 378 | message = {} | ||
| 379 | message["eventname"] = bb.event.getName(event) | ||
| 380 | num_of_completed = event.stats.completed + event.stats.failed | ||
| 381 | message["current"] = num_of_completed | ||
| 382 | message["total"] = event.stats.total | ||
| 383 | message["title"] = "" | ||
| 384 | message["task"] = event.taskstring | ||
| 385 | self.emit("task-started", message) | ||
| 386 | elif isinstance(event, bb.event.MultipleProviders): | ||
| 387 | self.emit("log", "info", "multiple providers are available for %s%s (%s)" \ | ||
| 388 | % (event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates))) | ||
| 389 | self.emit("log", "info", "consider defining a PREFERRED_PROVIDER entry to match %s" % (event._item)) | ||
| 390 | elif isinstance(event, bb.event.NoProvider): | ||
| 391 | msg = "" | ||
| 392 | if event._runtime: | ||
| 393 | r = "R" | ||
| 394 | else: | ||
| 395 | r = "" | ||
| 396 | |||
| 397 | extra = '' | ||
| 398 | if not event._reasons: | ||
| 399 | if event._close_matches: | ||
| 400 | extra = ". Close matches:\n %s" % '\n '.join(event._close_matches) | ||
| 401 | |||
| 402 | if event._dependees: | ||
| 403 | msg = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s\n" % (r, event._item, ", ".join(event._dependees), r, extra) | ||
| 404 | else: | ||
| 405 | msg = "Nothing %sPROVIDES '%s'%s\n" % (r, event._item, extra) | ||
| 406 | if event._reasons: | ||
| 407 | for reason in event._reasons: | ||
| 408 | msg += ("%s\n" % reason) | ||
| 409 | self.emit("no-provider", msg) | ||
| 410 | self.emit("log", "error", msg) | ||
| 411 | elif isinstance(event, bb.event.LogExecTTY): | ||
| 412 | icon = "dialog-warning" | ||
| 413 | color = HobColors.WARNING | ||
| 414 | if self.sequential or not parent: | ||
| 415 | tree_add = self.model.append | ||
| 416 | else: | ||
| 417 | tree_add = self.model.prepend | ||
| 418 | tree_add(parent, | ||
| 419 | (None, | ||
| 420 | package, | ||
| 421 | task, | ||
| 422 | event.msg, | ||
| 423 | icon, | ||
| 424 | color, | ||
| 425 | 0)) | ||
| 426 | else: | ||
| 427 | if not isinstance(event, (bb.event.BuildBase, | ||
| 428 | bb.event.StampUpdate, | ||
| 429 | bb.event.ConfigParsed, | ||
| 430 | bb.event.RecipeParsed, | ||
| 431 | bb.event.RecipePreFinalise, | ||
| 432 | bb.runqueue.runQueueEvent, | ||
| 433 | bb.runqueue.runQueueExitWait, | ||
| 434 | bb.event.OperationStarted, | ||
| 435 | bb.event.OperationCompleted, | ||
| 436 | bb.event.OperationProgress)): | ||
| 437 | self.emit("log", "error", "Unknown event: %s" % (event.error if hasattr(event, 'error') else 'error')) | ||
| 438 | |||
| 439 | return | ||
| 440 | |||
| 441 | |||
| 442 | def do_pastebin(text): | ||
| 443 | url = 'http://pastebin.com/api_public.php' | ||
| 444 | params = {'paste_code': text, 'paste_format': 'text'} | ||
| 445 | |||
| 446 | req = urllib.request.Request(url, urllib.parse.urlencode(params)) | ||
| 447 | response = urllib.request.urlopen(req) | ||
| 448 | paste_url = response.read() | ||
| 449 | |||
| 450 | return paste_url | ||
| 451 | |||
| 452 | |||
| 453 | class RunningBuildTreeView (gtk.TreeView): | ||
| 454 | __gsignals__ = { | ||
| 455 | "button_press_event" : "override" | ||
| 456 | } | ||
| 457 | def __init__ (self, readonly=False, hob=False): | ||
| 458 | gtk.TreeView.__init__ (self) | ||
| 459 | self.readonly = readonly | ||
| 460 | |||
| 461 | # The icon that indicates whether we're building or failed. | ||
| 462 | # add 'hob' flag because there has not only hob to share this code | ||
| 463 | if hob: | ||
| 464 | renderer = HobCellRendererPixbuf () | ||
| 465 | else: | ||
| 466 | renderer = gtk.CellRendererPixbuf() | ||
| 467 | col = gtk.TreeViewColumn ("Status", renderer) | ||
| 468 | col.add_attribute (renderer, "icon-name", 4) | ||
| 469 | self.append_column (col) | ||
| 470 | |||
| 471 | # The message of the build. | ||
| 472 | # add 'hob' flag because there has not only hob to share this code | ||
| 473 | if hob: | ||
| 474 | self.message_renderer = HobWarpCellRendererText (col_number=1) | ||
| 475 | else: | ||
| 476 | self.message_renderer = gtk.CellRendererText () | ||
| 477 | self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=3) | ||
| 478 | self.message_column.add_attribute(self.message_renderer, 'background', 5) | ||
| 479 | self.message_renderer.set_property('editable', (not self.readonly)) | ||
| 480 | self.append_column (self.message_column) | ||
| 481 | |||
| 482 | def do_button_press_event(self, event): | ||
| 483 | gtk.TreeView.do_button_press_event(self, event) | ||
| 484 | |||
| 485 | if event.button == 3: | ||
| 486 | selection = super(RunningBuildTreeView, self).get_selection() | ||
| 487 | (model, it) = selection.get_selected() | ||
| 488 | if it is not None: | ||
| 489 | can_paste = model.get(it, model.COL_LOG)[0] | ||
| 490 | if can_paste == 'pastebin': | ||
| 491 | # build a simple menu with a pastebin option | ||
| 492 | menu = gtk.Menu() | ||
| 493 | menuitem = gtk.MenuItem("Copy") | ||
| 494 | menu.append(menuitem) | ||
| 495 | menuitem.connect("activate", self.clipboard_handler, (model, it)) | ||
| 496 | menuitem.show() | ||
| 497 | menuitem = gtk.MenuItem("Send log to pastebin") | ||
| 498 | menu.append(menuitem) | ||
| 499 | menuitem.connect("activate", self.pastebin_handler, (model, it)) | ||
| 500 | menuitem.show() | ||
| 501 | menu.show() | ||
| 502 | menu.popup(None, None, None, event.button, event.time) | ||
| 503 | |||
| 504 | def _add_to_clipboard(self, clipping): | ||
| 505 | """ | ||
| 506 | Add the contents of clipping to the system clipboard. | ||
| 507 | """ | ||
| 508 | clipboard = gtk.clipboard_get() | ||
| 509 | clipboard.set_text(clipping) | ||
| 510 | clipboard.store() | ||
| 511 | |||
| 512 | def pastebin_handler(self, widget, data): | ||
| 513 | """ | ||
| 514 | Send the log data to pastebin, then add the new paste url to the | ||
| 515 | clipboard. | ||
| 516 | """ | ||
| 517 | (model, it) = data | ||
| 518 | paste_url = do_pastebin(model.get(it, model.COL_MESSAGE)[0]) | ||
| 519 | |||
| 520 | # @todo Provide visual feedback to the user that it is done and that | ||
| 521 | # it worked. | ||
| 522 | print(paste_url) | ||
| 523 | |||
| 524 | self._add_to_clipboard(paste_url) | ||
| 525 | |||
| 526 | def clipboard_handler(self, widget, data): | ||
| 527 | """ | ||
| 528 | """ | ||
| 529 | (model, it) = data | ||
| 530 | message = model.get(it, model.COL_MESSAGE)[0] | ||
| 531 | |||
| 532 | self._add_to_clipboard(message) | ||
| 533 | |||
| 534 | class BuildFailureTreeView(gtk.TreeView): | ||
| 535 | |||
| 536 | def __init__ (self): | ||
| 537 | gtk.TreeView.__init__(self) | ||
| 538 | self.set_rules_hint(False) | ||
| 539 | self.set_headers_visible(False) | ||
| 540 | self.get_selection().set_mode(gtk.SELECTION_SINGLE) | ||
| 541 | |||
| 542 | # The icon that indicates whether we're building or failed. | ||
| 543 | renderer = HobCellRendererPixbuf () | ||
| 544 | col = gtk.TreeViewColumn ("Status", renderer) | ||
| 545 | col.add_attribute (renderer, "icon-name", RunningBuildModel.COL_ICON) | ||
| 546 | self.append_column (col) | ||
| 547 | |||
| 548 | # The message of the build. | ||
| 549 | self.message_renderer = HobWarpCellRendererText (col_number=1) | ||
| 550 | self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=RunningBuildModel.COL_MESSAGE, background=RunningBuildModel.COL_COLOR) | ||
| 551 | self.append_column (self.message_column) | ||
