diff options
author | Bob Foerster <robert@erafx.com> | 2010-12-17 23:20:39 +0800 |
---|---|---|
committer | Richard Purdie <rpurdie@linux.intel.com> | 2011-01-05 11:13:48 +0000 |
commit | 2e0ef25a50c6a31cd6de52dfb31a04b77e694da3 (patch) | |
tree | fee195184ef180928f6712b05c1ed223382a6c96 | |
parent | 25ac24e02e3e96945e8ac83e16fe27a6b24789b1 (diff) | |
download | poky-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-x | bitbake/bin/bitbake | 2 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/progress.py | 2 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/runningbuild.py | 225 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/depexp.py | 66 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/goggle.py | 32 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/knotty.py | 4 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/ncurses.py | 58 |
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 | ||
21 | import gtk | 22 | import gtk |
22 | import gobject | 23 | import gobject |
24 | import logging | ||
25 | import time | ||
26 | import urllib | ||
27 | import urllib2 | ||
28 | |||
29 | class Colors(object): | ||
30 | OK = "#ffffff" | ||
31 | RUNNING = "#aaffaa" | ||
32 | WARNING ="#f88017" | ||
33 | ERROR = "#ffaaaa" | ||
23 | 34 | ||
24 | class RunningBuildModel (gtk.TreeStore): | 35 | class 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 | ||
35 | class RunningBuild (gobject.GObject): | 48 | class 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 | |||
248 | def 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 | ||
173 | class RunningBuildTreeView (gtk.TreeView): | 259 | class 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 | ||
20 | import gobject | 20 | import gobject |
21 | import gtk | 21 | import gtk |
22 | import Queue | ||
22 | import threading | 23 | import threading |
23 | import xmlrpclib | 24 | import xmlrpclib |
24 | import bb | 25 | import 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 | |||
35 | class PackageDepView(gtk.TreeView): | 37 | class 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 | |||
55 | class PackageReverseDepView(gtk.TreeView): | 58 | class 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 | |||
72 | class DepExplorer(gtk.Window): | 76 | class 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 | ||
160 | def parse(depgraph, pkg_model, depends_model): | 167 | def 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 | |||
179 | class gtkthread(threading.Thread): | 186 | class 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 | ||
192 | def main(server, eventHandler): | ||
193 | 199 | ||
200 | def 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 | |||
24 | from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild | 24 | from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild |
25 | from bb.ui.crumbs.progress import ProgressBar | 25 | from bb.ui.crumbs.progress import ProgressBar |
26 | 26 | ||
27 | import Queue | ||
28 | |||
29 | |||
27 | def event_handle_idle_func (eventHandler, build, pbar): | 30 | def 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 | ||
37 | def scroll_tv_cb (model, path, iter, view): | 40 | def 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... | ||
45 | def running_build_failed_cb (running_build): | ||
46 | pass | ||
47 | |||
48 | |||
49 | def running_build_succeeded_cb (running_build): | ||
50 | pass | ||
51 | |||
52 | |||
40 | class MainWindow (gtk.Window): | 53 | class 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 | |||
52 | def main (server, eventHandler): | 66 | def 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 | ||
47 | from __future__ import division | ||
48 | 47 | ||
48 | from __future__ import division | ||
49 | import logging | ||
49 | import os, sys, curses, itertools, time | 50 | import os, sys, curses, itertools, time |
50 | import bb | 51 | import bb |
51 | import xmlrpclib | 52 | import 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") |