summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/ui
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/ui')
-rw-r--r--bitbake/lib/bb/ui/__init__.py18
-rw-r--r--bitbake/lib/bb/ui/crumbs/__init__.py18
-rw-r--r--bitbake/lib/bb/ui/crumbs/buildmanager.py457
-rw-r--r--bitbake/lib/bb/ui/crumbs/puccho.glade606
-rw-r--r--bitbake/lib/bb/ui/crumbs/runningbuild.py180
-rw-r--r--bitbake/lib/bb/ui/depexp.py272
-rw-r--r--bitbake/lib/bb/ui/goggle.py77
-rw-r--r--bitbake/lib/bb/ui/knotty.py162
-rw-r--r--bitbake/lib/bb/ui/ncurses.py335
-rw-r--r--bitbake/lib/bb/ui/puccho.py425
-rw-r--r--bitbake/lib/bb/ui/uievent.py125
-rw-r--r--bitbake/lib/bb/ui/uihelper.py49
12 files changed, 2724 insertions, 0 deletions
diff --git a/bitbake/lib/bb/ui/__init__.py b/bitbake/lib/bb/ui/__init__.py
new file mode 100644
index 0000000000..c6a377a8e6
--- /dev/null
+++ b/bitbake/lib/bb/ui/__init__.py
@@ -0,0 +1,18 @@
1#
2# BitBake UI Implementation
3#
4# Copyright (C) 2006-2007 Richard Purdie
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License version 2 as
8# published by the Free Software Foundation.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
diff --git a/bitbake/lib/bb/ui/crumbs/__init__.py b/bitbake/lib/bb/ui/crumbs/__init__.py
new file mode 100644
index 0000000000..c6a377a8e6
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/__init__.py
@@ -0,0 +1,18 @@
1#
2# BitBake UI Implementation
3#
4# Copyright (C) 2006-2007 Richard Purdie
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License version 2 as
8# published by the Free Software Foundation.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
diff --git a/bitbake/lib/bb/ui/crumbs/buildmanager.py b/bitbake/lib/bb/ui/crumbs/buildmanager.py
new file mode 100644
index 0000000000..f89e8eefd4
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/buildmanager.py
@@ -0,0 +1,457 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2008 Intel Corporation
5#
6# Authored by Rob Bradford <rob@linux.intel.com>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21import gtk
22import gobject
23import threading
24import os
25import datetime
26import time
27
28class BuildConfiguration:
29 """ Represents a potential *or* historic *or* concrete build. It
30 encompasses all the things that we need to tell bitbake to do to make it
31 build what we want it to build.
32
33 It also stored the metadata URL and the set of possible machines (and the
34 distros / images / uris for these. Apart from the metdata URL these are
35 not serialised to file (since they may be transient). In some ways this
36 functionality might be shifted to the loader class."""
37
38 def __init__ (self):
39 self.metadata_url = None
40
41 # Tuple of (distros, image, urls)
42 self.machine_options = {}
43
44 self.machine = None
45 self.distro = None
46 self.image = None
47 self.urls = []
48 self.extra_urls = []
49 self.extra_pkgs = []
50
51 def get_machines_model (self):
52 model = gtk.ListStore (gobject.TYPE_STRING)
53 for machine in self.machine_options.keys():
54 model.append ([machine])
55
56 return model
57
58 def get_distro_and_images_models (self, machine):
59 distro_model = gtk.ListStore (gobject.TYPE_STRING)
60
61 for distro in self.machine_options[machine][0]:
62 distro_model.append ([distro])
63
64 image_model = gtk.ListStore (gobject.TYPE_STRING)
65
66 for image in self.machine_options[machine][1]:
67 image_model.append ([image])
68
69 return (distro_model, image_model)
70
71 def get_repos (self):
72 self.urls = self.machine_options[self.machine][2]
73 return self.urls
74
75 # It might be a lot lot better if we stored these in like, bitbake conf
76 # file format.
77 @staticmethod
78 def load_from_file (filename):
79 f = open (filename, "r")
80
81 conf = BuildConfiguration()
82 for line in f.readlines():
83 data = line.split (";")[1]
84 if (line.startswith ("metadata-url;")):
85 conf.metadata_url = data.strip()
86 continue
87 if (line.startswith ("url;")):
88 conf.urls += [data.strip()]
89 continue
90 if (line.startswith ("extra-url;")):
91 conf.extra_urls += [data.strip()]
92 continue
93 if (line.startswith ("machine;")):
94 conf.machine = data.strip()
95 continue
96 if (line.startswith ("distribution;")):
97 conf.distro = data.strip()
98 continue
99 if (line.startswith ("image;")):
100 conf.image = data.strip()
101 continue
102
103 f.close ()
104 return conf
105
106 # Serialise to a file. This is part of the build process and we use this
107 # to be able to repeat a given build (using the same set of parameters)
108 # but also so that we can include the details of the image / machine /
109 # distro in the build manager tree view.
110 def write_to_file (self, filename):
111 f = open (filename, "w")
112
113 lines = []
114
115 if (self.metadata_url):
116 lines += ["metadata-url;%s\n" % (self.metadata_url)]
117
118 for url in self.urls:
119 lines += ["url;%s\n" % (url)]
120
121 for url in self.extra_urls:
122 lines += ["extra-url;%s\n" % (url)]
123
124 if (self.machine):
125 lines += ["machine;%s\n" % (self.machine)]
126
127 if (self.distro):
128 lines += ["distribution;%s\n" % (self.distro)]
129
130 if (self.image):
131 lines += ["image;%s\n" % (self.image)]
132
133 f.writelines (lines)
134 f.close ()
135
136class BuildResult(gobject.GObject):
137 """ Represents an historic build. Perhaps not successful. But it includes
138 things such as the files that are in the directory (the output from the
139 build) as well as a deserialised BuildConfiguration file that is stored in
140 ".conf" in the directory for the build.
141
142 This is GObject so that it can be included in the TreeStore."""
143
144 (STATE_COMPLETE, STATE_FAILED, STATE_ONGOING) = \
145 (0, 1, 2)
146
147 def __init__ (self, parent, identifier):
148 gobject.GObject.__init__ (self)
149 self.date = None
150
151 self.files = []
152 self.status = None
153 self.identifier = identifier
154 self.path = os.path.join (parent, identifier)
155
156 # Extract the date, since the directory name is of the
157 # format build-<year><month><day>-<ordinal> we can easily
158 # pull it out.
159 # TODO: Better to stat a file?
160 (_ , date, revision) = identifier.split ("-")
161 print date
162
163 year = int (date[0:4])
164 month = int (date[4:6])
165 day = int (date[6:8])
166
167 self.date = datetime.date (year, month, day)
168
169 self.conf = None
170
171 # By default builds are STATE_FAILED unless we find a "complete" file
172 # in which case they are STATE_COMPLETE
173 self.state = BuildResult.STATE_FAILED
174 for file in os.listdir (self.path):
175 if (file.startswith (".conf")):
176 conffile = os.path.join (self.path, file)
177 self.conf = BuildConfiguration.load_from_file (conffile)
178 elif (file.startswith ("complete")):
179 self.state = BuildResult.STATE_COMPLETE
180 else:
181 self.add_file (file)
182
183 def add_file (self, file):
184 # Just add the file for now. Don't care about the type.
185 self.files += [(file, None)]
186
187class BuildManagerModel (gtk.TreeStore):
188 """ Model for the BuildManagerTreeView. This derives from gtk.TreeStore
189 but it abstracts nicely what the columns mean and the setup of the columns
190 in the model. """
191
192 (COL_IDENT, COL_DESC, COL_MACHINE, COL_DISTRO, COL_BUILD_RESULT, COL_DATE, COL_STATE) = \
193 (0, 1, 2, 3, 4, 5, 6)
194
195 def __init__ (self):
196 gtk.TreeStore.__init__ (self,
197 gobject.TYPE_STRING,
198 gobject.TYPE_STRING,
199 gobject.TYPE_STRING,
200 gobject.TYPE_STRING,
201 gobject.TYPE_OBJECT,
202 gobject.TYPE_INT64,
203 gobject.TYPE_INT)
204
205class BuildManager (gobject.GObject):
206 """ This class manages the historic builds that have been found in the
207 "results" directory but is also used for starting a new build."""
208
209 __gsignals__ = {
210 'population-finished' : (gobject.SIGNAL_RUN_LAST,
211 gobject.TYPE_NONE,
212 ()),
213 'populate-error' : (gobject.SIGNAL_RUN_LAST,
214 gobject.TYPE_NONE,
215 ())
216 }
217
218 def update_build_result (self, result, iter):
219 # Convert the date into something we can sort by.
220 date = long (time.mktime (result.date.timetuple()))
221
222 # Add a top level entry for the build
223
224 self.model.set (iter,
225 BuildManagerModel.COL_IDENT, result.identifier,
226 BuildManagerModel.COL_DESC, result.conf.image,
227 BuildManagerModel.COL_MACHINE, result.conf.machine,
228 BuildManagerModel.COL_DISTRO, result.conf.distro,
229 BuildManagerModel.COL_BUILD_RESULT, result,
230 BuildManagerModel.COL_DATE, date,
231 BuildManagerModel.COL_STATE, result.state)
232
233 # And then we use the files in the directory as the children for the
234 # top level iter.
235 for file in result.files:
236 self.model.append (iter, (None, file[0], None, None, None, date, -1))
237
238 # This function is called as an idle by the BuildManagerPopulaterThread
239 def add_build_result (self, result):
240 gtk.gdk.threads_enter()
241 self.known_builds += [result]
242
243 self.update_build_result (result, self.model.append (None))
244
245 gtk.gdk.threads_leave()
246
247 def notify_build_finished (self):
248 # This is a bit of a hack. If we have a running build running then we
249 # will have a row in the model in STATE_ONGOING. Find it and make it
250 # as if it was a proper historic build (well, it is completed now....)
251
252 # We need to use the iters here rather than the Python iterator
253 # interface to the model since we need to pass it into
254 # update_build_result
255
256 iter = self.model.get_iter_first()
257
258 while (iter):
259 (ident, state) = self.model.get(iter,
260 BuildManagerModel.COL_IDENT,
261 BuildManagerModel.COL_STATE)
262
263 if state == BuildResult.STATE_ONGOING:
264 result = BuildResult (self.results_directory, ident)
265 self.update_build_result (result, iter)
266 iter = self.model.iter_next(iter)
267
268 def notify_build_succeeded (self):
269 # Write the "complete" file so that when we create the BuildResult
270 # object we put into the model
271
272 complete_file_path = os.path.join (self.cur_build_directory, "complete")
273 f = file (complete_file_path, "w")
274 f.close()
275 self.notify_build_finished()
276
277 def notify_build_failed (self):
278 # Without a "complete" file then this will mark the build as failed:
279 self.notify_build_finished()
280
281 # This function is called as an idle
282 def emit_population_finished_signal (self):
283 gtk.gdk.threads_enter()
284 self.emit ("population-finished")
285 gtk.gdk.threads_leave()
286
287 class BuildManagerPopulaterThread (threading.Thread):
288 def __init__ (self, manager, directory):
289 threading.Thread.__init__ (self)
290 self.manager = manager
291 self.directory = directory
292
293 def run (self):
294 # For each of the "build-<...>" directories ..
295
296 if os.path.exists (self.directory):
297 for directory in os.listdir (self.directory):
298
299 if not directory.startswith ("build-"):
300 continue
301
302 build_result = BuildResult (self.directory, directory)
303 self.manager.add_build_result (build_result)
304
305 gobject.idle_add (BuildManager.emit_population_finished_signal,
306 self.manager)
307
308 def __init__ (self, server, results_directory):
309 gobject.GObject.__init__ (self)
310
311 # The builds that we've found from walking the result directory
312 self.known_builds = []
313
314 # Save out the bitbake server, we need this for issuing commands to
315 # the cooker:
316 self.server = server
317
318 # The TreeStore that we use
319 self.model = BuildManagerModel ()
320
321 # The results directory is where we create (and look for) the
322 # build-<xyz>-<n> directories. We need to populate ourselves from
323 # directory
324 self.results_directory = results_directory
325 self.populate_from_directory (self.results_directory)
326
327 def populate_from_directory (self, directory):
328 thread = BuildManager.BuildManagerPopulaterThread (self, directory)
329 thread.start()
330
331 # Come up with the name for the next build ident by combining "build-"
332 # with the date formatted as yyyymmdd and then an ordinal. We do this by
333 # an optimistic algorithm incrementing the ordinal if we find that it
334 # already exists.
335 def get_next_build_ident (self):
336 today = datetime.date.today ()
337 datestr = str (today.year) + str (today.month) + str (today.day)
338
339 revision = 0
340 test_name = "build-%s-%d" % (datestr, revision)
341 test_path = os.path.join (self.results_directory, test_name)
342
343 while (os.path.exists (test_path)):
344 revision += 1
345 test_name = "build-%s-%d" % (datestr, revision)
346 test_path = os.path.join (self.results_directory, test_name)
347
348 return test_name
349
350 # Take a BuildConfiguration and then try and build it based on the
351 # parameters of that configuration. S
352 def do_build (self, conf):
353 server = self.server
354
355 # Work out the build directory. Note we actually create the
356 # directories here since we need to write the ".conf" file. Otherwise
357 # we could have relied on bitbake's builder thread to actually make
358 # the directories as it proceeds with the build.
359 ident = self.get_next_build_ident ()
360 build_directory = os.path.join (self.results_directory,
361 ident)
362 self.cur_build_directory = build_directory
363 os.makedirs (build_directory)
364
365 conffile = os.path.join (build_directory, ".conf")
366 conf.write_to_file (conffile)
367
368 # Add a row to the model representing this ongoing build. It's kinda a
369 # fake entry. If this build completes or fails then this gets updated
370 # with the real stuff like the historic builds
371 date = long (time.time())
372 self.model.append (None, (ident, conf.image, conf.machine, conf.distro,
373 None, date, BuildResult.STATE_ONGOING))
374 try:
375 server.runCommand(["setVariable", "BUILD_IMAGES_FROM_FEEDS", 1])
376 server.runCommand(["setVariable", "MACHINE", conf.machine])
377 server.runCommand(["setVariable", "DISTRO", conf.distro])
378 server.runCommand(["setVariable", "PACKAGE_CLASSES", "package_ipk"])
379 server.runCommand(["setVariable", "BBFILES", \
380 """${OEROOT}/meta/packages/*/*.bb ${OEROOT}/meta-moblin/packages/*/*.bb"""])
381 server.runCommand(["setVariable", "TMPDIR", "${OEROOT}/build/tmp"])
382 server.runCommand(["setVariable", "IPK_FEED_URIS", \
383 " ".join(conf.get_repos())])
384 server.runCommand(["setVariable", "DEPLOY_DIR_IMAGE",
385 build_directory])
386 server.runCommand(["buildTargets", [conf.image], "rootfs"])
387
388 except Exception, e:
389 print e
390
391class BuildManagerTreeView (gtk.TreeView):
392 """ The tree view for the build manager. This shows the historic builds
393 and so forth. """
394
395 # We use this function to control what goes in the cell since we store
396 # the date in the model as seconds since the epoch (for sorting) and so we
397 # need to make it human readable.
398 def date_format_custom_cell_data_func (self, col, cell, model, iter):
399 date = model.get (iter, BuildManagerModel.COL_DATE)[0]
400 datestr = time.strftime("%A %d %B %Y", time.localtime(date))
401 cell.set_property ("text", datestr)
402
403 # This format function controls what goes in the cell. We use this to map
404 # the integer state to a string and also to colourise the text
405 def state_format_custom_cell_data_fun (self, col, cell, model, iter):
406 state = model.get (iter, BuildManagerModel.COL_STATE)[0]
407
408 if (state == BuildResult.STATE_ONGOING):
409 cell.set_property ("text", "Active")
410 cell.set_property ("foreground", "#000000")
411 elif (state == BuildResult.STATE_FAILED):
412 cell.set_property ("text", "Failed")
413 cell.set_property ("foreground", "#ff0000")
414 elif (state == BuildResult.STATE_COMPLETE):
415 cell.set_property ("text", "Complete")
416 cell.set_property ("foreground", "#00ff00")
417 else:
418 cell.set_property ("text", "")
419
420 def __init__ (self):
421 gtk.TreeView.__init__(self)
422
423 # Misc descriptiony thing
424 renderer = gtk.CellRendererText ()
425 col = gtk.TreeViewColumn (None, renderer,
426 text=BuildManagerModel.COL_DESC)
427 self.append_column (col)
428
429 # Machine
430 renderer = gtk.CellRendererText ()
431 col = gtk.TreeViewColumn ("Machine", renderer,
432 text=BuildManagerModel.COL_MACHINE)
433 self.append_column (col)
434
435 # distro
436 renderer = gtk.CellRendererText ()
437 col = gtk.TreeViewColumn ("Distribution", renderer,
438 text=BuildManagerModel.COL_DISTRO)
439 self.append_column (col)
440
441 # date (using a custom function for formatting the cell contents it
442 # takes epoch -> human readable string)
443 renderer = gtk.CellRendererText ()
444 col = gtk.TreeViewColumn ("Date", renderer,
445 text=BuildManagerModel.COL_DATE)
446 self.append_column (col)
447 col.set_cell_data_func (renderer,
448 self.date_format_custom_cell_data_func)
449
450 # For status.
451 renderer = gtk.CellRendererText ()
452 col = gtk.TreeViewColumn ("Status", renderer,
453 text = BuildManagerModel.COL_STATE)
454 self.append_column (col)
455 col.set_cell_data_func (renderer,
456 self.state_format_custom_cell_data_fun)
457
diff --git a/bitbake/lib/bb/ui/crumbs/puccho.glade b/bitbake/lib/bb/ui/crumbs/puccho.glade
new file mode 100644
index 0000000000..d7553a6e14
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/puccho.glade
@@ -0,0 +1,606 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
3<!--Generated with glade3 3.4.5 on Mon Nov 10 12:24:12 2008 -->
4<glade-interface>
5 <widget class="GtkDialog" id="build_dialog">
6 <property name="title" translatable="yes">Start a build</property>
7 <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
8 <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
9 <property name="has_separator">False</property>
10 <child internal-child="vbox">
11 <widget class="GtkVBox" id="dialog-vbox1">
12 <property name="visible">True</property>
13 <property name="spacing">2</property>
14 <child>
15 <widget class="GtkTable" id="build_table">
16 <property name="visible">True</property>
17 <property name="border_width">6</property>
18 <property name="n_rows">7</property>
19 <property name="n_columns">3</property>
20 <property name="column_spacing">5</property>
21 <property name="row_spacing">6</property>
22 <child>
23 <widget class="GtkAlignment" id="status_alignment">
24 <property name="visible">True</property>
25 <property name="left_padding">12</property>
26 <child>
27 <widget class="GtkHBox" id="status_hbox">
28 <property name="spacing">6</property>
29 <child>
30 <widget class="GtkImage" id="status_image">
31 <property name="visible">True</property>
32 <property name="no_show_all">True</property>
33 <property name="xalign">0</property>
34 <property name="stock">gtk-dialog-error</property>
35 </widget>
36 <packing>
37 <property name="expand">False</property>
38 <property name="fill">False</property>
39 </packing>
40 </child>
41 <child>
42 <widget class="GtkLabel" id="status_label">
43 <property name="visible">True</property>
44 <property name="xalign">0</property>
45 <property name="label" translatable="yes">If you see this text something is wrong...</property>
46 <property name="use_markup">True</property>
47 <property name="use_underline">True</property>
48 </widget>
49 <packing>
50 <property name="position">1</property>
51 </packing>
52 </child>
53 </widget>
54 </child>
55 </widget>
56 <packing>
57 <property name="right_attach">3</property>
58 <property name="top_attach">2</property>
59 <property name="bottom_attach">3</property>
60 </packing>
61 </child>
62 <child>
63 <widget class="GtkLabel" id="label2">
64 <property name="visible">True</property>
65 <property name="xalign">0</property>
66 <property name="label" translatable="yes">&lt;b&gt;Build configuration&lt;/b&gt;</property>
67 <property name="use_markup">True</property>
68 </widget>
69 <packing>
70 <property name="right_attach">3</property>
71 <property name="top_attach">3</property>
72 <property name="bottom_attach">4</property>
73 <property name="y_options"></property>
74 </packing>
75 </child>
76 <child>
77 <widget class="GtkComboBox" id="image_combo">
78 <property name="visible">True</property>
79 <property name="sensitive">False</property>
80 </widget>
81 <packing>
82 <property name="left_attach">1</property>
83 <property name="right_attach">2</property>
84 <property name="top_attach">6</property>
85 <property name="bottom_attach">7</property>
86 <property name="y_options"></property>
87 </packing>
88 </child>
89 <child>
90 <widget class="GtkLabel" id="image_label">
91 <property name="visible">True</property>
92 <property name="sensitive">False</property>
93 <property name="xalign">0</property>
94 <property name="xpad">12</property>
95 <property name="label" translatable="yes">Image:</property>
96 </widget>
97 <packing>
98 <property name="top_attach">6</property>
99 <property name="bottom_attach">7</property>
100 <property name="y_options"></property>
101 </packing>
102 </child>
103 <child>
104 <widget class="GtkComboBox" id="distribution_combo">
105 <property name="visible">True</property>
106 <property name="sensitive">False</property>
107 </widget>
108 <packing>
109 <property name="left_attach">1</property>
110 <property name="right_attach">2</property>
111 <property name="top_attach">5</property>
112 <property name="bottom_attach">6</property>
113 <property name="y_options"></property>
114 </packing>
115 </child>
116 <child>
117 <widget class="GtkLabel" id="distribution_label">
118 <property name="visible">True</property>
119 <property name="sensitive">False</property>
120 <property name="xalign">0</property>
121 <property name="xpad">12</property>
122 <property name="label" translatable="yes">Distribution:</property>
123 </widget>
124 <packing>
125 <property name="top_attach">5</property>
126 <property name="bottom_attach">6</property>
127 <property name="y_options"></property>
128 </packing>
129 </child>
130 <child>
131 <widget class="GtkComboBox" id="machine_combo">
132 <property name="visible">True</property>
133 <property name="sensitive">False</property>
134 </widget>
135 <packing>
136 <property name="left_attach">1</property>
137 <property name="right_attach">2</property>
138 <property name="top_attach">4</property>
139 <property name="bottom_attach">5</property>
140 <property name="y_options"></property>
141 </packing>
142 </child>
143 <child>
144 <widget class="GtkLabel" id="machine_label">
145 <property name="visible">True</property>
146 <property name="sensitive">False</property>
147 <property name="xalign">0</property>
148 <property name="xpad">12</property>
149 <property name="label" translatable="yes">Machine:</property>
150 </widget>
151 <packing>
152 <property name="top_attach">4</property>
153 <property name="bottom_attach">5</property>
154 <property name="y_options"></property>
155 </packing>
156 </child>
157 <child>
158 <widget class="GtkButton" id="refresh_button">
159 <property name="visible">True</property>
160 <property name="sensitive">False</property>
161 <property name="can_focus">True</property>
162 <property name="receives_default">True</property>
163 <property name="label" translatable="yes">gtk-refresh</property>
164 <property name="use_stock">True</property>
165 <property name="response_id">0</property>
166 </widget>
167 <packing>
168 <property name="left_attach">2</property>
169 <property name="right_attach">3</property>
170 <property name="top_attach">1</property>
171 <property name="bottom_attach">2</property>
172 <property name="y_options"></property>
173 </packing>
174 </child>
175 <child>
176 <widget class="GtkEntry" id="location_entry">
177 <property name="visible">True</property>
178 <property name="can_focus">True</property>
179 <property name="width_chars">32</property>
180 </widget>
181 <packing>
182 <property name="left_attach">1</property>
183 <property name="right_attach">2</property>
184 <property name="top_attach">1</property>
185 <property name="bottom_attach">2</property>
186 <property name="y_options"></property>
187 </packing>
188 </child>
189 <child>
190 <widget class="GtkLabel" id="label3">
191 <property name="visible">True</property>
192 <property name="xalign">0</property>
193 <property name="xpad">12</property>
194 <property name="label" translatable="yes">Location:</property>
195 </widget>
196 <packing>
197 <property name="top_attach">1</property>
198 <property name="bottom_attach">2</property>
199 <property name="y_options"></property>
200 </packing>
201 </child>
202 <child>
203 <widget class="GtkLabel" id="label1">
204 <property name="visible">True</property>
205 <property name="xalign">0</property>
206 <property name="label" translatable="yes">&lt;b&gt;Repository&lt;/b&gt;</property>
207 <property name="use_markup">True</property>
208 </widget>
209 <packing>
210 <property name="right_attach">3</property>
211 <property name="y_options"></property>
212 </packing>
213 </child>
214 <child>
215 <widget class="GtkAlignment" id="alignment1">
216 <property name="visible">True</property>
217 <child>
218 <placeholder/>
219 </child>
220 </widget>
221 <packing>
222 <property name="left_attach">2</property>
223 <property name="right_attach">3</property>
224 <property name="top_attach">4</property>
225 <property name="bottom_attach">5</property>
226 <property name="y_options"></property>
227 </packing>
228 </child>
229 <child>
230 <widget class="GtkAlignment" id="alignment2">
231 <property name="visible">True</property>
232 <child>
233 <placeholder/>
234 </child>
235 </widget>
236 <packing>
237 <property name="left_attach">2</property>
238 <property name="right_attach">3</property>
239 <property name="top_attach">5</property>
240 <property name="bottom_attach">6</property>
241 <property name="y_options"></property>
242 </packing>
243 </child>
244 <child>
245 <widget class="GtkAlignment" id="alignment3">
246 <property name="visible">True</property>
247 <child>
248 <placeholder/>
249 </child>
250 </widget>
251 <packing>
252 <property name="left_attach">2</property>
253 <property name="right_attach">3</property>
254 <property name="top_attach">6</property>
255 <property name="bottom_attach">7</property>
256 <property name="y_options"></property>
257 </packing>
258 </child>
259 </widget>
260 <packing>
261 <property name="position">1</property>
262 </packing>
263 </child>
264 <child internal-child="action_area">
265 <widget class="GtkHButtonBox" id="dialog-action_area1">
266 <property name="visible">True</property>
267 <property name="layout_style">GTK_BUTTONBOX_END</property>
268 <child>
269 <placeholder/>
270 </child>
271 <child>
272 <placeholder/>
273 </child>
274 <child>
275 <placeholder/>
276 </child>
277 </widget>
278 <packing>
279 <property name="expand">False</property>
280 <property name="pack_type">GTK_PACK_END</property>
281 </packing>
282 </child>
283 </widget>
284 </child>
285 </widget>
286 <widget class="GtkDialog" id="dialog2">
287 <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
288 <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
289 <property name="has_separator">False</property>
290 <child internal-child="vbox">
291 <widget class="GtkVBox" id="dialog-vbox2">
292 <property name="visible">True</property>
293 <property name="spacing">2</property>
294 <child>
295 <widget class="GtkTable" id="table2">
296 <property name="visible">True</property>
297 <property name="border_width">6</property>
298 <property name="n_rows">7</property>
299 <property name="n_columns">3</property>
300 <property name="column_spacing">6</property>
301 <property name="row_spacing">6</property>
302 <child>
303 <widget class="GtkLabel" id="label7">
304 <property name="visible">True</property>
305 <property name="xalign">0</property>
306 <property name="label" translatable="yes">&lt;b&gt;Repositories&lt;/b&gt;</property>
307 <property name="use_markup">True</property>
308 </widget>
309 <packing>
310 <property name="right_attach">3</property>
311 <property name="y_options"></property>
312 </packing>
313 </child>
314 <child>
315 <widget class="GtkAlignment" id="alignment4">
316 <property name="visible">True</property>
317 <property name="xalign">0</property>
318 <property name="left_padding">12</property>
319 <child>
320 <widget class="GtkScrolledWindow" id="scrolledwindow1">
321 <property name="visible">True</property>
322 <property name="can_focus">True</property>
323 <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
324 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
325 <child>
326 <widget class="GtkTreeView" id="treeview1">
327 <property name="visible">True</property>
328 <property name="can_focus">True</property>
329 <property name="headers_clickable">True</property>
330 </widget>
331 </child>
332 </widget>
333 </child>
334 </widget>
335 <packing>
336 <property name="right_attach">3</property>
337 <property name="top_attach">2</property>
338 <property name="bottom_attach">3</property>
339 <property name="y_options"></property>
340 </packing>
341 </child>
342 <child>
343 <widget class="GtkEntry" id="entry1">
344 <property name="visible">True</property>
345 <property name="can_focus">True</property>
346 </widget>
347 <packing>
348 <property name="left_attach">1</property>
349 <property name="right_attach">3</property>
350 <property name="top_attach">1</property>
351 <property name="bottom_attach">2</property>
352 <property name="y_options"></property>
353 </packing>
354 </child>
355 <child>
356 <widget class="GtkLabel" id="label9">
357 <property name="visible">True</property>
358 <property name="xalign">0</property>
359 <property name="label" translatable="yes">&lt;b&gt;Additional packages&lt;/b&gt;</property>
360 <property name="use_markup">True</property>
361 </widget>
362 <packing>
363 <property name="right_attach">3</property>
364 <property name="top_attach">4</property>
365 <property name="bottom_attach">5</property>
366 <property name="y_options"></property>
367 </packing>
368 </child>
369 <child>
370 <widget class="GtkAlignment" id="alignment6">
371 <property name="visible">True</property>
372 <property name="xalign">0</property>
373 <property name="xscale">0</property>
374 <child>
375 <widget class="GtkLabel" id="label8">
376 <property name="visible">True</property>
377 <property name="xalign">0</property>
378 <property name="yalign">0</property>
379 <property name="xpad">12</property>
380 <property name="label" translatable="yes">Location: </property>
381 </widget>
382 </child>
383 </widget>
384 <packing>
385 <property name="top_attach">1</property>
386 <property name="bottom_attach">2</property>
387 <property name="y_options"></property>
388 </packing>
389 </child>
390 <child>
391 <widget class="GtkAlignment" id="alignment7">
392 <property name="visible">True</property>
393 <property name="xalign">1</property>
394 <property name="xscale">0</property>
395 <child>
396 <widget class="GtkHButtonBox" id="hbuttonbox1">
397 <property name="visible">True</property>
398 <property name="spacing">5</property>
399 <child>
400 <widget class="GtkButton" id="button7">
401 <property name="visible">True</property>
402 <property name="can_focus">True</property>
403 <property name="receives_default">True</property>
404 <property name="label" translatable="yes">gtk-remove</property>
405 <property name="use_stock">True</property>
406 <property name="response_id">0</property>
407 </widget>
408 </child>
409 <child>
410 <widget class="GtkButton" id="button6">
411 <property name="visible">True</property>
412 <property name="can_focus">True</property>
413 <property name="receives_default">True</property>
414 <property name="label" translatable="yes">gtk-edit</property>
415 <property name="use_stock">True</property>
416 <property name="response_id">0</property>
417 </widget>
418 <packing>
419 <property name="position">1</property>
420 </packing>
421 </child>
422 <child>
423 <widget class="GtkButton" id="button5">
424 <property name="visible">True</property>
425 <property name="can_focus">True</property>
426 <property name="receives_default">True</property>
427 <property name="label" translatable="yes">gtk-add</property>
428 <property name="use_stock">True</property>
429 <property name="response_id">0</property>
430 </widget>
431 <packing>
432 <property name="position">2</property>
433 </packing>
434 </child>
435 </widget>
436 </child>
437 </widget>
438 <packing>
439 <property name="left_attach">1</property>
440 <property name="right_attach">3</property>
441 <property name="top_attach">3</property>
442 <property name="bottom_attach">4</property>
443 <property name="y_options"></property>
444 </packing>
445 </child>
446 <child>
447 <widget class="GtkAlignment" id="alignment5">
448 <property name="visible">True</property>
449 <child>
450 <placeholder/>
451 </child>
452 </widget>
453 <packing>
454 <property name="top_attach">3</property>
455 <property name="bottom_attach">4</property>
456 <property name="y_options"></property>
457 </packing>
458 </child>
459 <child>
460 <widget class="GtkLabel" id="label10">
461 <property name="visible">True</property>
462 <property name="xalign">0</property>
463 <property name="yalign">0</property>
464 <property name="xpad">12</property>
465 <property name="label" translatable="yes">Search:</property>
466 </widget>
467 <packing>
468 <property name="top_attach">5</property>
469 <property name="bottom_attach">6</property>
470 <property name="y_options"></property>
471 </packing>
472 </child>
473 <child>
474 <widget class="GtkEntry" id="entry2">
475 <property name="visible">True</property>
476 <property name="can_focus">True</property>
477 </widget>
478 <packing>
479 <property name="left_attach">1</property>
480 <property name="right_attach">3</property>
481 <property name="top_attach">5</property>
482 <property name="bottom_attach">6</property>
483 <property name="y_options"></property>
484 </packing>
485 </child>
486 <child>
487 <widget class="GtkAlignment" id="alignment8">
488 <property name="visible">True</property>
489 <property name="xalign">0</property>
490 <property name="left_padding">12</property>
491 <child>
492 <widget class="GtkScrolledWindow" id="scrolledwindow2">
493 <property name="visible">True</property>
494 <property name="can_focus">True</property>
495 <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
496 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
497 <child>
498 <widget class="GtkTreeView" id="treeview2">
499 <property name="visible">True</property>
500 <property name="can_focus">True</property>
501 <property name="headers_clickable">True</property>
502 </widget>
503 </child>
504 </widget>
505 </child>
506 </widget>
507 <packing>
508 <property name="right_attach">3</property>
509 <property name="top_attach">6</property>
510 <property name="bottom_attach">7</property>
511 <property name="y_options"></property>
512 </packing>
513 </child>
514 </widget>
515 <packing>
516 <property name="position">1</property>
517 </packing>
518 </child>
519 <child internal-child="action_area">
520 <widget class="GtkHButtonBox" id="dialog-action_area2">
521 <property name="visible">True</property>
522 <property name="layout_style">GTK_BUTTONBOX_END</property>
523 <child>
524 <widget class="GtkButton" id="button4">
525 <property name="visible">True</property>
526 <property name="can_focus">True</property>
527 <property name="receives_default">True</property>
528 <property name="label" translatable="yes">gtk-close</property>
529 <property name="use_stock">True</property>
530 <property name="response_id">0</property>
531 </widget>
532 </child>
533 </widget>
534 <packing>
535 <property name="expand">False</property>
536 <property name="pack_type">GTK_PACK_END</property>
537 </packing>
538 </child>
539 </widget>
540 </child>
541 </widget>
542 <widget class="GtkWindow" id="main_window">
543 <child>
544 <widget class="GtkVBox" id="main_window_vbox">
545 <property name="visible">True</property>
546 <child>
547 <widget class="GtkToolbar" id="main_toolbar">
548 <property name="visible">True</property>
549 <child>
550 <widget class="GtkToolButton" id="main_toolbutton_build">
551 <property name="visible">True</property>
552 <property name="label" translatable="yes">Build</property>
553 <property name="stock_id">gtk-execute</property>
554 </widget>
555 <packing>
556 <property name="expand">False</property>
557 </packing>
558 </child>
559 </widget>
560 <packing>
561 <property name="expand">False</property>
562 </packing>
563 </child>
564 <child>
565 <widget class="GtkVPaned" id="vpaned1">
566 <property name="visible">True</property>
567 <property name="can_focus">True</property>
568 <child>
569 <widget class="GtkScrolledWindow" id="results_scrolledwindow">
570 <property name="visible">True</property>
571 <property name="can_focus">True</property>
572 <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
573 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
574 <child>
575 <placeholder/>
576 </child>
577 </widget>
578 <packing>
579 <property name="resize">False</property>
580 <property name="shrink">True</property>
581 </packing>
582 </child>
583 <child>
584 <widget class="GtkScrolledWindow" id="progress_scrolledwindow">
585 <property name="visible">True</property>
586 <property name="can_focus">True</property>
587 <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
588 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
589 <child>
590 <placeholder/>
591 </child>
592 </widget>
593 <packing>
594 <property name="resize">True</property>
595 <property name="shrink">True</property>
596 </packing>
597 </child>
598 </widget>
599 <packing>
600 <property name="position">1</property>
601 </packing>
602 </child>
603 </widget>
604 </child>
605 </widget>
606</glade-interface>
diff --git a/bitbake/lib/bb/ui/crumbs/runningbuild.py b/bitbake/lib/bb/ui/crumbs/runningbuild.py
new file mode 100644
index 0000000000..401559255b
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/runningbuild.py
@@ -0,0 +1,180 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2008 Intel Corporation
5#
6# Authored by Rob Bradford <rob@linux.intel.com>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21import gtk
22import gobject
23
24class RunningBuildModel (gtk.TreeStore):
25 (COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5)
26 def __init__ (self):
27 gtk.TreeStore.__init__ (self,
28 gobject.TYPE_STRING,
29 gobject.TYPE_STRING,
30 gobject.TYPE_STRING,
31 gobject.TYPE_STRING,
32 gobject.TYPE_STRING,
33 gobject.TYPE_BOOLEAN)
34
35class RunningBuild (gobject.GObject):
36 __gsignals__ = {
37 'build-succeeded' : (gobject.SIGNAL_RUN_LAST,
38 gobject.TYPE_NONE,
39 ()),
40 'build-failed' : (gobject.SIGNAL_RUN_LAST,
41 gobject.TYPE_NONE,
42 ())
43 }
44 pids_to_task = {}
45 tasks_to_iter = {}
46
47 def __init__ (self):
48 gobject.GObject.__init__ (self)
49 self.model = RunningBuildModel()
50
51 def handle_event (self, event):
52 # Handle an event from the event queue, this may result in updating
53 # the model and thus the UI. Or it may be to tell us that the build
54 # has finished successfully (or not, as the case may be.)
55
56 parent = None
57 pid = 0
58 package = None
59 task = None
60
61 # If we have a pid attached to this message/event try and get the
62 # (package, task) pair for it. If we get that then get the parent iter
63 # for the message.
64 if hassattr(event, 'pid'):
65 pid = event.pid
66 if self.pids_to_task.has_key(pid):
67 (package, task) = self.pids_to_task[pid]
68 parent = self.tasks_to_iter[(package, task)]
69
70 if isinstance(event, bb.msg.Msg):
71 # Set a pretty icon for the message based on it's type.
72 if isinstance(event, bb.msg.MsgWarn):
73 icon = "dialog-warning"
74 elif isinstance(event, bb.msg.MsgErr):
75 icon = "dialog-error"
76 else:
77 icon = None
78
79 # Ignore the "Running task i of n .." messages
80 if (event._message.startswith ("Running task")):
81 return
82
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.__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):
93 (package, task) = (event._package, event._task)
94
95 # Save out this PID.
96 self.pids_to_task[pid] = (package,task)
97
98 # Check if we already have this package in our model. If so then
99 # that can be the parent for the task. Otherwise we create a new
100 # top level for the package.
101 if (self.tasks_to_iter.has_key ((package, None))):
102 parent = self.tasks_to_iter[(package, None)]
103 else:
104 parent = self.model.append (None, (None,
105 package,
106 None,
107 "Package: %s" % (package),
108 None,
109 False))
110 self.tasks_to_iter[(package, None)] = parent
111
112 # Because this parent package now has an active child mark it as
113 # such.
114 self.model.set(parent, self.model.COL_ICON, "gtk-execute")
115
116 # Add an entry in the model for this task
117 i = self.model.append (parent, (None,
118 package,
119 task,
120 "Task: %s" % (task),
121 None,
122 False))
123
124 # Save out the iter so that we can find it when we have a message
125 # that we need to attach to a task.
126 self.tasks_to_iter[(package, task)] = i
127
128 # Mark this task as active.
129 self.model.set(i, self.model.COL_ICON, "gtk-execute")
130
131 elif isinstance(event, bb.build.Task):
132
133 if isinstance(event, bb.build.TaskFailed):
134 # Mark the task as failed
135 i = self.tasks_to_iter[(package, task)]
136 self.model.set(i, self.model.COL_ICON, "dialog-error")
137
138 # Mark the parent package as failed
139 i = self.tasks_to_iter[(package, None)]
140 self.model.set(i, self.model.COL_ICON, "dialog-error")
141 else:
142 # Mark the task as inactive
143 i = self.tasks_to_iter[(package, task)]
144 self.model.set(i, self.model.COL_ICON, None)
145
146 # Mark the parent package as inactive
147 i = self.tasks_to_iter[(package, None)]
148 self.model.set(i, self.model.COL_ICON, None)
149
150
151 # Clear the iters and the pids since when the task goes away the
152 # pid will no longer be used for messages
153 del self.tasks_to_iter[(package, task)]
154 del self.pids_to_task[pid]
155
156 elif isinstance(event, bb.event.BuildCompleted):
157 failures = int (event._failures)
158
159 # Emit the appropriate signal depending on the number of failures
160 if (failures > 1):
161 self.emit ("build-failed")
162 else:
163 self.emit ("build-succeeded")
164
165class RunningBuildTreeView (gtk.TreeView):
166 def __init__ (self):
167 gtk.TreeView.__init__ (self)
168
169 # The icon that indicates whether we're building or failed.
170 renderer = gtk.CellRendererPixbuf ()
171 col = gtk.TreeViewColumn ("Status", renderer)
172 col.add_attribute (renderer, "icon-name", 4)
173 self.append_column (col)
174
175 # The message of the build.
176 renderer = gtk.CellRendererText ()
177 col = gtk.TreeViewColumn ("Message", renderer, text=3)
178 self.append_column (col)
179
180
diff --git a/bitbake/lib/bb/ui/depexp.py b/bitbake/lib/bb/ui/depexp.py
new file mode 100644
index 0000000000..cfa5b6564e
--- /dev/null
+++ b/bitbake/lib/bb/ui/depexp.py
@@ -0,0 +1,272 @@
1#
2# BitBake Graphical GTK based Dependency Explorer
3#
4# Copyright (C) 2007 Ross Burton
5# Copyright (C) 2007 - 2008 Richard Purdie
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20import gobject
21import gtk
22import threading
23import xmlrpclib
24
25# Package Model
26(COL_PKG_NAME) = (0)
27
28# Dependency Model
29(TYPE_DEP, TYPE_RDEP) = (0, 1)
30(COL_DEP_TYPE, COL_DEP_PARENT, COL_DEP_PACKAGE) = (0, 1, 2)
31
32class PackageDepView(gtk.TreeView):
33 def __init__(self, model, dep_type, label):
34 gtk.TreeView.__init__(self)
35 self.current = None
36 self.dep_type = dep_type
37 self.filter_model = model.filter_new()
38 self.filter_model.set_visible_func(self._filter)
39 self.set_model(self.filter_model)
40 #self.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
41 self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PACKAGE))
42
43 def _filter(self, model, iter):
44 (this_type, package) = model.get(iter, COL_DEP_TYPE, COL_DEP_PARENT)
45 if this_type != self.dep_type: return False
46 return package == self.current
47
48 def set_current_package(self, package):
49 self.current = package
50 self.filter_model.refilter()
51
52class PackageReverseDepView(gtk.TreeView):
53 def __init__(self, model, label):
54 gtk.TreeView.__init__(self)
55 self.current = None
56 self.filter_model = model.filter_new()
57 self.filter_model.set_visible_func(self._filter)
58 self.set_model(self.filter_model)
59 self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PARENT))
60
61 def _filter(self, model, iter):
62 package = model.get_value(iter, COL_DEP_PACKAGE)
63 return package == self.current
64
65 def set_current_package(self, package):
66 self.current = package
67 self.filter_model.refilter()
68
69class DepExplorer(gtk.Window):
70 def __init__(self):
71 gtk.Window.__init__(self)
72 self.set_title("Dependency Explorer")
73 self.set_default_size(500, 500)
74 self.connect("delete-event", gtk.main_quit)
75
76 # Create the data models
77 self.pkg_model = gtk.ListStore(gobject.TYPE_STRING)
78 self.depends_model = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING)
79
80 pane = gtk.HPaned()
81 pane.set_position(250)
82 self.add(pane)
83
84 # The master list of packages
85 scrolled = gtk.ScrolledWindow()
86 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
87 scrolled.set_shadow_type(gtk.SHADOW_IN)
88 self.pkg_treeview = gtk.TreeView(self.pkg_model)
89 self.pkg_treeview.get_selection().connect("changed", self.on_cursor_changed)
90 self.pkg_treeview.append_column(gtk.TreeViewColumn("Package", gtk.CellRendererText(), text=COL_PKG_NAME))
91 pane.add1(scrolled)
92 scrolled.add(self.pkg_treeview)
93
94 box = gtk.VBox(homogeneous=True, spacing=4)
95
96 # Runtime Depends
97 scrolled = gtk.ScrolledWindow()
98 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
99 scrolled.set_shadow_type(gtk.SHADOW_IN)
100 self.rdep_treeview = PackageDepView(self.depends_model, TYPE_RDEP, "Runtime Depends")
101 self.rdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
102 scrolled.add(self.rdep_treeview)
103 box.add(scrolled)
104
105 # Build Depends
106 scrolled = gtk.ScrolledWindow()
107 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
108 scrolled.set_shadow_type(gtk.SHADOW_IN)
109 self.dep_treeview = PackageDepView(self.depends_model, TYPE_DEP, "Build Depends")
110 self.dep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
111 scrolled.add(self.dep_treeview)
112 box.add(scrolled)
113 pane.add2(box)
114
115 # Reverse Depends
116 scrolled = gtk.ScrolledWindow()
117 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
118 scrolled.set_shadow_type(gtk.SHADOW_IN)
119 self.revdep_treeview = PackageReverseDepView(self.depends_model, "Reverse Depends")
120 self.revdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PARENT)
121 scrolled.add(self.revdep_treeview)
122 box.add(scrolled)
123 pane.add2(box)
124
125 self.show_all()
126
127 def on_package_activated(self, treeview, path, column, data_col):
128 model = treeview.get_model()
129 package = model.get_value(model.get_iter(path), data_col)
130
131 pkg_path = []
132 def finder(model, path, iter, needle):
133 package = model.get_value(iter, COL_PKG_NAME)
134 if package == needle:
135 pkg_path.append(path)
136 return True
137 else:
138 return False
139 self.pkg_model.foreach(finder, package)
140 if pkg_path:
141 self.pkg_treeview.get_selection().select_path(pkg_path[0])
142 self.pkg_treeview.scroll_to_cell(pkg_path[0])
143
144 def on_cursor_changed(self, selection):
145 (model, it) = selection.get_selected()
146 if iter is None:
147 current_package = None
148 else:
149 current_package = model.get_value(it, COL_PKG_NAME)
150 self.rdep_treeview.set_current_package(current_package)
151 self.dep_treeview.set_current_package(current_package)
152 self.revdep_treeview.set_current_package(current_package)
153
154
155def parse(depgraph, pkg_model, depends_model):
156
157 for package in depgraph["pn"]:
158 pkg_model.set(pkg_model.append(), COL_PKG_NAME, package)
159
160 for package in depgraph["depends"]:
161 for depend in depgraph["depends"][package]:
162 depends_model.set (depends_model.append(),
163 COL_DEP_TYPE, TYPE_DEP,
164 COL_DEP_PARENT, package,
165 COL_DEP_PACKAGE, depend)
166
167 for package in depgraph["rdepends-pn"]:
168 for rdepend in depgraph["rdepends-pn"][package]:
169 depends_model.set (depends_model.append(),
170 COL_DEP_TYPE, TYPE_RDEP,
171 COL_DEP_PARENT, package,
172 COL_DEP_PACKAGE, rdepend)
173
174class ProgressBar(gtk.Window):
175 def __init__(self):
176
177 gtk.Window.__init__(self)
178 self.set_title("Parsing .bb files, please wait...")
179 self.set_default_size(500, 0)
180 self.connect("delete-event", gtk.main_quit)
181
182 self.progress = gtk.ProgressBar()
183 self.add(self.progress)
184 self.show_all()
185
186class gtkthread(threading.Thread):
187 quit = threading.Event()
188 def __init__(self, shutdown):
189 threading.Thread.__init__(self)
190 self.setDaemon(True)
191 self.shutdown = shutdown
192
193 def run(self):
194 gobject.threads_init()
195 gtk.gdk.threads_init()
196 gtk.main()
197 gtkthread.quit.set()
198
199def init(server, eventHandler):
200
201 try:
202 cmdline = server.runCommand(["getCmdLineAction"])
203 if not cmdline or cmdline[0] != "generateDotGraph":
204 print "This UI is only compatible with the -g option"
205 return
206 ret = server.runCommand(["generateDepTreeEvent", cmdline[1], cmdline[2]])
207 if ret != True:
208 print "Couldn't run command! %s" % ret
209 return
210 except xmlrpclib.Fault, x:
211 print "XMLRPC Fault getting commandline:\n %s" % x
212 return
213
214 shutdown = 0
215
216 gtkgui = gtkthread(shutdown)
217 gtkgui.start()
218
219 gtk.gdk.threads_enter()
220 pbar = ProgressBar()
221 dep = DepExplorer()
222 gtk.gdk.threads_leave()
223
224 while True:
225 try:
226 event = eventHandler.waitEvent(0.25)
227 if gtkthread.quit.isSet():
228 break
229
230 if event is None:
231 continue
232 if isinstance(event, bb.event.ParseProgress):
233 x = event.sofar
234 y = event.total
235 if x == y:
236 print("\nParsing finished. %d cached, %d parsed, %d skipped, %d masked, %d errors."
237 % ( event.cached, event.parsed, event.skipped, event.masked, event.errors))
238 pbar.hide()
239 gtk.gdk.threads_enter()
240 pbar.progress.set_fraction(float(x)/float(y))
241 pbar.progress.set_text("%d/%d (%2d %%)" % (x, y, x*100/y))
242 gtk.gdk.threads_leave()
243 continue
244
245 if isinstance(event, bb.event.DepTreeGenerated):
246 gtk.gdk.threads_enter()
247 parse(event._depgraph, dep.pkg_model, dep.depends_model)
248 gtk.gdk.threads_leave()
249
250 if isinstance(event, bb.command.CookerCommandCompleted):
251 continue
252 if isinstance(event, bb.command.CookerCommandFailed):
253 print "Command execution failed: %s" % event.error
254 break
255 if isinstance(event, bb.cooker.CookerExit):
256 break
257
258 continue
259
260 except KeyboardInterrupt:
261 if shutdown == 2:
262 print "\nThird Keyboard Interrupt, exit.\n"
263 break
264 if shutdown == 1:
265 print "\nSecond Keyboard Interrupt, stopping...\n"
266 server.runCommand(["stateStop"])
267 if shutdown == 0:
268 print "\nKeyboard Interrupt, closing down...\n"
269 server.runCommand(["stateShutdown"])
270 shutdown = shutdown + 1
271 pass
272
diff --git a/bitbake/lib/bb/ui/goggle.py b/bitbake/lib/bb/ui/goggle.py
new file mode 100644
index 0000000000..94995d82db
--- /dev/null
+++ b/bitbake/lib/bb/ui/goggle.py
@@ -0,0 +1,77 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2008 Intel Corporation
5#
6# Authored by Rob Bradford <rob@linux.intel.com>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21import gobject
22import gtk
23import xmlrpclib
24from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
25
26def event_handle_idle_func (eventHandler, build):
27
28 # Consume as many messages as we can in the time available to us
29 event = eventHandler.getEvent()
30 while event:
31 build.handle_event (event)
32 event = eventHandler.getEvent()
33
34 return True
35
36class MainWindow (gtk.Window):
37 def __init__ (self):
38 gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
39
40 # Setup tree view and the scrolled window
41 scrolled_window = gtk.ScrolledWindow ()
42 self.add (scrolled_window)
43 self.cur_build_tv = RunningBuildTreeView()
44 scrolled_window.add (self.cur_build_tv)
45
46def init (server, eventHandler):
47 gobject.threads_init()
48 gtk.gdk.threads_init()
49
50 window = MainWindow ()
51 window.show_all ()
52
53 # Create the object for the current build
54 running_build = RunningBuild ()
55 window.cur_build_tv.set_model (running_build.model)
56 try:
57 cmdline = server.runCommand(["getCmdLineAction"])
58 print cmdline
59 if not cmdline:
60 return 1
61 ret = server.runCommand(cmdline)
62 if ret != True:
63 print "Couldn't get default commandline! %s" % ret
64 return 1
65 except xmlrpclib.Fault, x:
66 print "XMLRPC Fault getting commandline:\n %s" % x
67 return 1
68
69 # Use a timeout function for probing the event queue to find out if we
70 # have a message waiting for us.
71 gobject.timeout_add (200,
72 event_handle_idle_func,
73 eventHandler,
74 running_build)
75
76 gtk.main()
77
diff --git a/bitbake/lib/bb/ui/knotty.py b/bitbake/lib/bb/ui/knotty.py
new file mode 100644
index 0000000000..c69fd6ca64
--- /dev/null
+++ b/bitbake/lib/bb/ui/knotty.py
@@ -0,0 +1,162 @@
1#
2# BitBake (No)TTY UI Implementation
3#
4# Handling output to TTYs or files (no TTY)
5#
6# Copyright (C) 2006-2007 Richard Purdie
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21import os
22
23import sys
24import itertools
25import xmlrpclib
26
27parsespin = itertools.cycle( r'|/-\\' )
28
29def init(server, eventHandler):
30
31 # Get values of variables which control our output
32 includelogs = server.runCommand(["getVariable", "BBINCLUDELOGS"])
33 loglines = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
34
35 try:
36 cmdline = server.runCommand(["getCmdLineAction"])
37 #print cmdline
38 if not cmdline:
39 return 1
40 ret = server.runCommand(cmdline)
41 if ret != True:
42 print "Couldn't get default commandline! %s" % ret
43 return 1
44 except xmlrpclib.Fault, x:
45 print "XMLRPC Fault getting commandline:\n %s" % x
46 return 1
47
48 shutdown = 0
49 return_value = 0
50 while True:
51 try:
52 event = eventHandler.waitEvent(0.25)
53 if event is None:
54 continue
55 #print event
56 if isinstance(event, bb.msg.MsgPlain):
57 print event._message
58 continue
59 if isinstance(event, bb.msg.MsgDebug):
60 print 'DEBUG: ' + event._message
61 continue
62 if isinstance(event, bb.msg.MsgNote):
63 print 'NOTE: ' + event._message
64 continue
65 if isinstance(event, bb.msg.MsgWarn):
66 print 'WARNING: ' + event._message
67 continue
68 if isinstance(event, bb.msg.MsgError):
69 return_value = 1
70 print 'ERROR: ' + event._message
71 continue
72 if isinstance(event, bb.msg.MsgFatal):
73 return_value = 1
74 print 'FATAL: ' + event._message
75 break
76 if isinstance(event, bb.build.TaskFailed):
77 return_value = 1
78 logfile = event.logfile
79 if logfile:
80 print "ERROR: Logfile of failure stored in %s." % logfile
81 if 1 or includelogs:
82 print "Log data follows:"
83 f = open(logfile, "r")
84 lines = []
85 while True:
86 l = f.readline()
87 if l == '':
88 break
89 l = l.rstrip()
90 if loglines:
91 lines.append(' | %s' % l)
92 if len(lines) > int(loglines):
93 lines.pop(0)
94 else:
95 print '| %s' % l
96 f.close()
97 if lines:
98 for line in lines:
99 print line
100 if isinstance(event, bb.build.TaskBase):
101 print "NOTE: %s" % event._message
102 continue
103 if isinstance(event, bb.event.ParseProgress):
104 x = event.sofar
105 y = event.total
106 if os.isatty(sys.stdout.fileno()):
107 sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
108 sys.stdout.flush()
109 else:
110 if x == 1:
111 sys.stdout.write("Parsing .bb files, please wait...")
112 sys.stdout.flush()
113 if x == y:
114 sys.stdout.write("done.")
115 sys.stdout.flush()
116 if x == y:
117 print("\nParsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors."
118 % ( event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors))
119 continue
120
121 if isinstance(event, bb.command.CookerCommandCompleted):
122 break
123 if isinstance(event, bb.command.CookerCommandSetExitCode):
124 return_value = event.exitcode
125 continue
126 if isinstance(event, bb.command.CookerCommandFailed):
127 return_value = 1
128 print "Command execution failed: %s" % event.error
129 break
130 if isinstance(event, bb.cooker.CookerExit):
131 break
132
133 # ignore
134 if isinstance(event, bb.event.BuildStarted):
135 continue
136 if isinstance(event, bb.event.BuildCompleted):
137 continue
138 if isinstance(event, bb.event.MultipleProviders):
139 continue
140 if isinstance(event, bb.runqueue.runQueueEvent):
141 continue
142 if isinstance(event, bb.event.StampUpdate):
143 continue
144 if isinstance(event, bb.event.ConfigParsed):
145 continue
146 if isinstance(event, bb.event.RecipeParsed):
147 continue
148 print "Unknown Event: %s" % event
149
150 except KeyboardInterrupt:
151 if shutdown == 2:
152 print "\nThird Keyboard Interrupt, exit.\n"
153 break
154 if shutdown == 1:
155 print "\nSecond Keyboard Interrupt, stopping...\n"
156 server.runCommand(["stateStop"])
157 if shutdown == 0:
158 print "\nKeyboard Interrupt, closing down...\n"
159 server.runCommand(["stateShutdown"])
160 shutdown = shutdown + 1
161 pass
162 return return_value
diff --git a/bitbake/lib/bb/ui/ncurses.py b/bitbake/lib/bb/ui/ncurses.py
new file mode 100644
index 0000000000..14310dc124
--- /dev/null
+++ b/bitbake/lib/bb/ui/ncurses.py
@@ -0,0 +1,335 @@
1#
2# BitBake Curses UI Implementation
3#
4# Implements an ncurses frontend for the BitBake utility.
5#
6# Copyright (C) 2006 Michael 'Mickey' Lauer
7# Copyright (C) 2006-2007 Richard Purdie
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"""
23 We have the following windows:
24
25 1.) Main Window: Shows what we are ultimately building and how far we are. Includes status bar
26 2.) Thread Activity Window: Shows one status line for every concurrent bitbake thread.
27 3.) Command Line Window: Contains an interactive command line where you can interact w/ Bitbake.
28
29 Basic window layout is like that:
30
31 |---------------------------------------------------------|
32 | <Main Window> | <Thread Activity Window> |
33 | | 0: foo do_compile complete|
34 | Building Gtk+-2.6.10 | 1: bar do_patch complete |
35 | Status: 60% | ... |
36 | | ... |
37 | | ... |
38 |---------------------------------------------------------|
39 |<Command Line Window> |
40 |>>> which virtual/kernel |
41 |openzaurus-kernel |
42 |>>> _ |
43 |---------------------------------------------------------|
44
45"""
46
47import os, sys, curses, itertools, time
48import bb
49import xmlrpclib
50from bb import ui
51from bb.ui import uihelper
52
53parsespin = itertools.cycle( r'|/-\\' )
54
55X = 0
56Y = 1
57WIDTH = 2
58HEIGHT = 3
59
60MAXSTATUSLENGTH = 32
61
62class NCursesUI:
63 """
64 NCurses UI Class
65 """
66 class Window:
67 """Base Window Class"""
68 def __init__( self, x, y, width, height, fg=curses.COLOR_BLACK, bg=curses.COLOR_WHITE ):
69 self.win = curses.newwin( height, width, y, x )
70 self.dimensions = ( x, y, width, height )
71 """
72 if curses.has_colors():
73 color = 1
74 curses.init_pair( color, fg, bg )
75 self.win.bkgdset( ord(' '), curses.color_pair(color) )
76 else:
77 self.win.bkgdset( ord(' '), curses.A_BOLD )
78 """
79 self.erase()
80 self.setScrolling()
81 self.win.noutrefresh()
82
83 def erase( self ):
84 self.win.erase()
85
86 def setScrolling( self, b = True ):
87 self.win.scrollok( b )
88 self.win.idlok( b )
89
90 def setBoxed( self ):
91 self.boxed = True
92 self.win.box()
93 self.win.noutrefresh()
94
95 def setText( self, x, y, text, *args ):
96 self.win.addstr( y, x, text, *args )
97 self.win.noutrefresh()
98
99 def appendText( self, text, *args ):
100 self.win.addstr( text, *args )
101 self.win.noutrefresh()
102
103 def drawHline( self, y ):
104 self.win.hline( y, 0, curses.ACS_HLINE, self.dimensions[WIDTH] )
105 self.win.noutrefresh()
106
107 class DecoratedWindow( Window ):
108 """Base class for windows with a box and a title bar"""
109 def __init__( self, title, x, y, width, height, fg=curses.COLOR_BLACK, bg=curses.COLOR_WHITE ):
110 NCursesUI.Window.__init__( self, x+1, y+3, width-2, height-4, fg, bg )
111 self.decoration = NCursesUI.Window( x, y, width, height, fg, bg )
112 self.decoration.setBoxed()
113 self.decoration.win.hline( 2, 1, curses.ACS_HLINE, width-2 )
114 self.setTitle( title )
115
116 def setTitle( self, title ):
117 self.decoration.setText( 1, 1, title.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD )
118
119 #-------------------------------------------------------------------------#
120# class TitleWindow( Window ):
121 #-------------------------------------------------------------------------#
122# """Title Window"""
123# def __init__( self, x, y, width, height ):
124# NCursesUI.Window.__init__( self, x, y, width, height )
125# version = bb.__version__
126# title = "BitBake %s" % version
127# credit = "(C) 2003-2007 Team BitBake"
128# #self.win.hline( 2, 1, curses.ACS_HLINE, width-2 )
129# self.win.border()
130# self.setText( 1, 1, title.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD )
131# self.setText( 1, 2, credit.center( self.dimensions[WIDTH]-2 ), curses.A_BOLD )
132
133 #-------------------------------------------------------------------------#
134 class ThreadActivityWindow( DecoratedWindow ):
135 #-------------------------------------------------------------------------#
136 """Thread Activity Window"""
137 def __init__( self, x, y, width, height ):
138 NCursesUI.DecoratedWindow.__init__( self, "Thread Activity", x, y, width, height )
139
140 def setStatus( self, thread, text ):
141 line = "%02d: %s" % ( thread, text )
142 width = self.dimensions[WIDTH]
143 if ( len(line) > width ):
144 line = line[:width-3] + "..."
145 else:
146 line = line.ljust( width )
147 self.setText( 0, thread, line )
148
149 #-------------------------------------------------------------------------#
150 class MainWindow( DecoratedWindow ):
151 #-------------------------------------------------------------------------#
152 """Main Window"""
153 def __init__( self, x, y, width, height ):
154 self.StatusPosition = width - MAXSTATUSLENGTH
155 NCursesUI.DecoratedWindow.__init__( self, None, x, y, width, height )
156 curses.nl()
157
158 def setTitle( self, title ):
159 title = "BitBake %s" % bb.__version__
160 self.decoration.setText( 2, 1, title, curses.A_BOLD )
161 self.decoration.setText( self.StatusPosition - 8, 1, "Status:", curses.A_BOLD )
162
163 def setStatus(self, status):
164 while len(status) < MAXSTATUSLENGTH:
165 status = status + " "
166 self.decoration.setText( self.StatusPosition, 1, status, curses.A_BOLD )
167
168
169 #-------------------------------------------------------------------------#
170 class ShellOutputWindow( DecoratedWindow ):
171 #-------------------------------------------------------------------------#
172 """Interactive Command Line Output"""
173 def __init__( self, x, y, width, height ):
174 NCursesUI.DecoratedWindow.__init__( self, "Command Line Window", x, y, width, height )
175
176 #-------------------------------------------------------------------------#
177 class ShellInputWindow( Window ):
178 #-------------------------------------------------------------------------#
179 """Interactive Command Line Input"""
180 def __init__( self, x, y, width, height ):
181 NCursesUI.Window.__init__( self, x, y, width, height )
182
183# put that to the top again from curses.textpad import Textbox
184# self.textbox = Textbox( self.win )
185# t = threading.Thread()
186# t.run = self.textbox.edit
187# t.start()
188
189 #-------------------------------------------------------------------------#
190 def main(self, stdscr, server, eventHandler):
191 #-------------------------------------------------------------------------#
192 height, width = stdscr.getmaxyx()
193
194 # for now split it like that:
195 # MAIN_y + THREAD_y = 2/3 screen at the top
196 # MAIN_x = 2/3 left, THREAD_y = 1/3 right
197 # CLI_y = 1/3 of screen at the bottom
198 # CLI_x = full
199
200 main_left = 0
201 main_top = 0
202 main_height = ( height / 3 * 2 )
203 main_width = ( width / 3 ) * 2
204 clo_left = main_left
205 clo_top = main_top + main_height
206 clo_height = height - main_height - main_top - 1
207 clo_width = width
208 cli_left = main_left
209 cli_top = clo_top + clo_height
210 cli_height = 1
211 cli_width = width
212 thread_left = main_left + main_width
213 thread_top = main_top
214 thread_height = main_height
215 thread_width = width - main_width
216
217 #tw = self.TitleWindow( 0, 0, width, main_top )
218 mw = self.MainWindow( main_left, main_top, main_width, main_height )
219 taw = self.ThreadActivityWindow( thread_left, thread_top, thread_width, thread_height )
220 clo = self.ShellOutputWindow( clo_left, clo_top, clo_width, clo_height )
221 cli = self.ShellInputWindow( cli_left, cli_top, cli_width, cli_height )
222 cli.setText( 0, 0, "BB>" )
223
224 mw.setStatus("Idle")
225
226 helper = uihelper.BBUIHelper()
227 shutdown = 0
228
229 try:
230 cmdline = server.runCommand(["getCmdLineAction"])
231 if not cmdline:
232 return
233 ret = server.runCommand(cmdline)
234 if ret != True:
235 print "Couldn't get default commandlind! %s" % ret
236 return
237 except xmlrpclib.Fault, x:
238 print "XMLRPC Fault getting commandline:\n %s" % x
239 return
240
241 exitflag = False
242 while not exitflag:
243 try:
244 event = eventHandler.waitEvent(0.25)
245 if not event:
246 continue
247 helper.eventHandler(event)
248 #mw.appendText("%s\n" % event[0])
249 if isinstance(event, bb.build.Task):
250 mw.appendText("NOTE: %s\n" % event._message)
251 if isinstance(event, bb.msg.MsgDebug):
252 mw.appendText('DEBUG: ' + event._message + '\n')
253 if isinstance(event, bb.msg.MsgNote):
254 mw.appendText('NOTE: ' + event._message + '\n')
255 if isinstance(event, bb.msg.MsgWarn):
256 mw.appendText('WARNING: ' + event._message + '\n')
257 if isinstance(event, bb.msg.MsgError):
258 mw.appendText('ERROR: ' + event._message + '\n')
259 if isinstance(event, bb.msg.MsgFatal):
260 mw.appendText('FATAL: ' + event._message + '\n')
261 if isinstance(event, bb.event.ParseProgress):
262 x = event.sofar
263 y = event.total
264 if x == y:
265 mw.setStatus("Idle")
266 mw.appendText("Parsing finished. %d cached, %d parsed, %d skipped, %d masked."
267 % ( event.cached, event.parsed, event.skipped, event.masked ))
268 else:
269 mw.setStatus("Parsing: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
270# if isinstance(event, bb.build.TaskFailed):
271# if event.logfile:
272# if data.getVar("BBINCLUDELOGS", d):
273# bb.msg.error(bb.msg.domain.Build, "log data follows (%s)" % logfile)
274# number_of_lines = data.getVar("BBINCLUDELOGS_LINES", d)
275# if number_of_lines:
276# os.system('tail -n%s %s' % (number_of_lines, logfile))
277# else:
278# f = open(logfile, "r")
279# while True:
280# l = f.readline()
281# if l == '':
282# break
283# l = l.rstrip()
284# print '| %s' % l
285# f.close()
286# else:
287# bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile)
288
289 if isinstance(event, bb.command.CookerCommandCompleted):
290 exitflag = True
291 if isinstance(event, bb.command.CookerCommandFailed):
292 mw.appendText("Command execution failed: %s" % event.error)
293 time.sleep(2)
294 exitflag = True
295 if isinstance(event, bb.cooker.CookerExit):
296 exitflag = True
297
298 if helper.needUpdate:
299 activetasks, failedtasks = helper.getTasks()
300 taw.erase()
301 taw.setText(0, 0, "")
302 if activetasks:
303 taw.appendText("Active Tasks:\n")
304 for task in activetasks:
305 taw.appendText(task)
306 if failedtasks:
307 taw.appendText("Failed Tasks:\n")
308 for task in failedtasks:
309 taw.appendText(task)
310
311 curses.doupdate()
312 except KeyboardInterrupt:
313 if shutdown == 2:
314 mw.appendText("Third Keyboard Interrupt, exit.\n")
315 exitflag = True
316 if shutdown == 1:
317 mw.appendText("Second Keyboard Interrupt, stopping...\n")
318 server.runCommand(["stateStop"])
319 if shutdown == 0:
320 mw.appendText("Keyboard Interrupt, closing down...\n")
321 server.runCommand(["stateShutdown"])
322 shutdown = shutdown + 1
323 pass
324
325def init(server, eventHandler):
326 if not os.isatty(sys.stdout.fileno()):
327 print "FATAL: Unable to run 'ncurses' UI without a TTY."
328 return
329 ui = NCursesUI()
330 try:
331 curses.wrapper(ui.main, server, eventHandler)
332 except:
333 import traceback
334 traceback.print_exc()
335
diff --git a/bitbake/lib/bb/ui/puccho.py b/bitbake/lib/bb/ui/puccho.py
new file mode 100644
index 0000000000..713aa1f4a6
--- /dev/null
+++ b/bitbake/lib/bb/ui/puccho.py
@@ -0,0 +1,425 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2008 Intel Corporation
5#
6# Authored by Rob Bradford <rob@linux.intel.com>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21import gtk
22import gobject
23import gtk.glade
24import threading
25import urllib2
26import os
27
28from bb.ui.crumbs.buildmanager import BuildManager, BuildConfiguration
29from bb.ui.crumbs.buildmanager import BuildManagerTreeView
30
31from bb.ui.crumbs.runningbuild import RunningBuild, RunningBuildTreeView
32
33# The metadata loader is used by the BuildSetupDialog to download the
34# available options to populate the dialog
35class MetaDataLoader(gobject.GObject):
36 """ This class provides the mechanism for loading the metadata (the
37 fetching and parsing) from a given URL. The metadata encompasses details
38 on what machines are available. The distribution and images available for
39 the machine and the the uris to use for building the given machine."""
40 __gsignals__ = {
41 'success' : (gobject.SIGNAL_RUN_LAST,
42 gobject.TYPE_NONE,
43 ()),
44 'error' : (gobject.SIGNAL_RUN_LAST,
45 gobject.TYPE_NONE,
46 (gobject.TYPE_STRING,))
47 }
48
49 # We use these little helper functions to ensure that we take the gdk lock
50 # when emitting the signal. These functions are called as idles (so that
51 # they happen in the gtk / main thread's main loop.
52 def emit_error_signal (self, remark):
53 gtk.gdk.threads_enter()
54 self.emit ("error", remark)
55 gtk.gdk.threads_leave()
56
57 def emit_success_signal (self):
58 gtk.gdk.threads_enter()
59 self.emit ("success")
60 gtk.gdk.threads_leave()
61
62 def __init__ (self):
63 gobject.GObject.__init__ (self)
64
65 class LoaderThread(threading.Thread):
66 """ This class provides an asynchronous loader for the metadata (by
67 using threads and signals). This is useful since the metadata may be
68 at a remote URL."""
69 class LoaderImportException (Exception):
70 pass
71
72 def __init__(self, loader, url):
73 threading.Thread.__init__ (self)
74 self.url = url
75 self.loader = loader
76
77 def run (self):
78 result = {}
79 try:
80 f = urllib2.urlopen (self.url)
81
82 # Parse the metadata format. The format is....
83 # <machine>;<default distro>|<distro>...;<default image>|<image>...;<type##url>|...
84 for line in f.readlines():
85 components = line.split(";")
86 if (len (components) < 4):
87 raise MetaDataLoader.LoaderThread.LoaderImportException
88 machine = components[0]
89 distros = components[1].split("|")
90 images = components[2].split("|")
91 urls = components[3].split("|")
92
93 result[machine] = (distros, images, urls)
94
95 # Create an object representing this *potential*
96 # configuration. It can become concrete if the machine, distro
97 # and image are all chosen in the UI
98 configuration = BuildConfiguration()
99 configuration.metadata_url = self.url
100 configuration.machine_options = result
101 self.loader.configuration = configuration
102
103 # Emit that we've actually got a configuration
104 gobject.idle_add (MetaDataLoader.emit_success_signal,
105 self.loader)
106
107 except MetaDataLoader.LoaderThread.LoaderImportException, e:
108 gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader,
109 "Repository metadata corrupt")
110 except Exception, e:
111 gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader,
112 "Unable to download repository metadata")
113 print e
114
115 def try_fetch_from_url (self, url):
116 # Try and download the metadata. Firing a signal if successful
117 thread = MetaDataLoader.LoaderThread(self, url)
118 thread.start()
119
120class BuildSetupDialog (gtk.Dialog):
121 RESPONSE_BUILD = 1
122
123 # A little helper method that just sets the states on the widgets based on
124 # whether we've got good metadata or not.
125 def set_configurable (self, configurable):
126 if (self.configurable == configurable):
127 return
128
129 self.configurable = configurable
130 for widget in self.conf_widgets:
131 widget.set_sensitive (configurable)
132
133 if not configurable:
134 self.machine_combo.set_active (-1)
135 self.distribution_combo.set_active (-1)
136 self.image_combo.set_active (-1)
137
138 # GTK widget callbacks
139 def refresh_button_clicked (self, button):
140 # Refresh button clicked.
141
142 url = self.location_entry.get_chars (0, -1)
143 self.loader.try_fetch_from_url(url)
144
145 def repository_entry_editable_changed (self, entry):
146 if (len (entry.get_chars (0, -1)) > 0):
147 self.refresh_button.set_sensitive (True)
148 else:
149 self.refresh_button.set_sensitive (False)
150 self.clear_status_message()
151
152 # If we were previously configurable we are no longer since the
153 # location entry has been changed
154 self.set_configurable (False)
155
156 def machine_combo_changed (self, combobox):
157 active_iter = combobox.get_active_iter()
158
159 if not active_iter:
160 return
161
162 model = combobox.get_model()
163
164 if model:
165 chosen_machine = model.get (active_iter, 0)[0]
166
167 (distros_model, images_model) = \
168 self.loader.configuration.get_distro_and_images_models (chosen_machine)
169
170 self.distribution_combo.set_model (distros_model)
171 self.image_combo.set_model (images_model)
172
173 # Callbacks from the loader
174 def loader_success_cb (self, loader):
175 self.status_image.set_from_icon_name ("info",
176 gtk.ICON_SIZE_BUTTON)
177 self.status_image.show()
178 self.status_label.set_label ("Repository metadata successfully downloaded")
179
180 # Set the models on the combo boxes based on the models generated from
181 # the configuration that the loader has created
182
183 # We just need to set the machine here, that then determines the
184 # distro and image options. Cunning huh? :-)
185
186 self.configuration = self.loader.configuration
187 model = self.configuration.get_machines_model ()
188 self.machine_combo.set_model (model)
189
190 self.set_configurable (True)
191
192 def loader_error_cb (self, loader, message):
193 self.status_image.set_from_icon_name ("error",
194 gtk.ICON_SIZE_BUTTON)
195 self.status_image.show()
196 self.status_label.set_text ("Error downloading repository metadata")
197 for widget in self.conf_widgets:
198 widget.set_sensitive (False)
199
200 def clear_status_message (self):
201 self.status_image.hide()
202 self.status_label.set_label (
203 """<i>Enter the repository location and press _Refresh</i>""")
204
205 def __init__ (self):
206 gtk.Dialog.__init__ (self)
207
208 # Cancel
209 self.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
210
211 # Build
212 button = gtk.Button ("_Build", None, True)
213 image = gtk.Image ()
214 image.set_from_stock (gtk.STOCK_EXECUTE,gtk.ICON_SIZE_BUTTON)
215 button.set_image (image)
216 self.add_action_widget (button, BuildSetupDialog.RESPONSE_BUILD)
217 button.show_all ()
218
219 # Pull in *just* the table from the Glade XML data.
220 gxml = gtk.glade.XML (os.path.dirname(__file__) + "/crumbs/puccho.glade",
221 root = "build_table")
222 table = gxml.get_widget ("build_table")
223 self.vbox.pack_start (table, True, False, 0)
224
225 # Grab all the widgets that we need to turn on/off when we refresh...
226 self.conf_widgets = []
227 self.conf_widgets += [gxml.get_widget ("machine_label")]
228 self.conf_widgets += [gxml.get_widget ("distribution_label")]
229 self.conf_widgets += [gxml.get_widget ("image_label")]
230 self.conf_widgets += [gxml.get_widget ("machine_combo")]
231 self.conf_widgets += [gxml.get_widget ("distribution_combo")]
232 self.conf_widgets += [gxml.get_widget ("image_combo")]
233
234 # Grab the status widgets
235 self.status_image = gxml.get_widget ("status_image")
236 self.status_label = gxml.get_widget ("status_label")
237
238 # Grab the refresh button and connect to the clicked signal
239 self.refresh_button = gxml.get_widget ("refresh_button")
240 self.refresh_button.connect ("clicked", self.refresh_button_clicked)
241
242 # Grab the location entry and connect to editable::changed
243 self.location_entry = gxml.get_widget ("location_entry")
244 self.location_entry.connect ("changed",
245 self.repository_entry_editable_changed)
246
247 # Grab the machine combo and hook onto the changed signal. This then
248 # allows us to populate the distro and image combos
249 self.machine_combo = gxml.get_widget ("machine_combo")
250 self.machine_combo.connect ("changed", self.machine_combo_changed)
251
252 # Setup the combo
253 cell = gtk.CellRendererText()
254 self.machine_combo.pack_start(cell, True)
255 self.machine_combo.add_attribute(cell, 'text', 0)
256
257 # Grab the distro and image combos. We need these to populate with
258 # models once the machine is chosen
259 self.distribution_combo = gxml.get_widget ("distribution_combo")
260 cell = gtk.CellRendererText()
261 self.distribution_combo.pack_start(cell, True)
262 self.distribution_combo.add_attribute(cell, 'text', 0)
263
264 self.image_combo = gxml.get_widget ("image_combo")
265 cell = gtk.CellRendererText()
266 self.image_combo.pack_start(cell, True)
267 self.image_combo.add_attribute(cell, 'text', 0)
268
269 # Put the default descriptive text in the status box
270 self.clear_status_message()
271
272 # Mark as non-configurable, this is just greys out the widgets the
273 # user can't yet use
274 self.configurable = False
275 self.set_configurable(False)
276
277 # Show the table
278 table.show_all ()
279
280 # The loader and some signals connected to it to update the status
281 # area
282 self.loader = MetaDataLoader()
283 self.loader.connect ("success", self.loader_success_cb)
284 self.loader.connect ("error", self.loader_error_cb)
285
286 def update_configuration (self):
287 """ A poorly named function but it updates the internal configuration
288 from the widgets. This can make that configuration concrete and can
289 thus be used for building """
290 # Extract the chosen machine from the combo
291 model = self.machine_combo.get_model()
292 active_iter = self.machine_combo.get_active_iter()
293 if (active_iter):
294 self.configuration.machine = model.get(active_iter, 0)[0]
295
296 # Extract the chosen distro from the combo
297 model = self.distribution_combo.get_model()
298 active_iter = self.distribution_combo.get_active_iter()
299 if (active_iter):
300 self.configuration.distro = model.get(active_iter, 0)[0]
301
302 # Extract the chosen image from the combo
303 model = self.image_combo.get_model()
304 active_iter = self.image_combo.get_active_iter()
305 if (active_iter):
306 self.configuration.image = model.get(active_iter, 0)[0]
307
308# This function operates to pull events out from the event queue and then push
309# them into the RunningBuild (which then drives the RunningBuild which then
310# pushes through and updates the progress tree view.)
311#
312# TODO: Should be a method on the RunningBuild class
313def event_handle_timeout (eventHandler, build):
314 # Consume as many messages as we can ...
315 event = eventHandler.getEvent()
316 while event:
317 build.handle_event (event)
318 event = eventHandler.getEvent()
319 return True
320
321class MainWindow (gtk.Window):
322
323 # Callback that gets fired when the user hits a button in the
324 # BuildSetupDialog.
325 def build_dialog_box_response_cb (self, dialog, response_id):
326 conf = None
327 if (response_id == BuildSetupDialog.RESPONSE_BUILD):
328 dialog.update_configuration()
329 print dialog.configuration.machine, dialog.configuration.distro, \
330 dialog.configuration.image
331 conf = dialog.configuration
332
333 dialog.destroy()
334
335 if conf:
336 self.manager.do_build (conf)
337
338 def build_button_clicked_cb (self, button):
339 dialog = BuildSetupDialog ()
340
341 # For some unknown reason Dialog.run causes nice little deadlocks ... :-(
342 dialog.connect ("response", self.build_dialog_box_response_cb)
343 dialog.show()
344
345 def __init__ (self):
346 gtk.Window.__init__ (self)
347
348 # Pull in *just* the main vbox from the Glade XML data and then pack
349 # that inside the window
350 gxml = gtk.glade.XML (os.path.dirname(__file__) + "/crumbs/puccho.glade",
351 root = "main_window_vbox")
352 vbox = gxml.get_widget ("main_window_vbox")
353 self.add (vbox)
354
355 # Create the tree views for the build manager view and the progress view
356 self.build_manager_view = BuildManagerTreeView()
357 self.running_build_view = RunningBuildTreeView()
358
359 # Grab the scrolled windows that we put the tree views into
360 self.results_scrolledwindow = gxml.get_widget ("results_scrolledwindow")
361 self.progress_scrolledwindow = gxml.get_widget ("progress_scrolledwindow")
362
363 # Put the tree views inside ...
364 self.results_scrolledwindow.add (self.build_manager_view)
365 self.progress_scrolledwindow.add (self.running_build_view)
366
367 # Hook up the build button...
368 self.build_button = gxml.get_widget ("main_toolbutton_build")
369 self.build_button.connect ("clicked", self.build_button_clicked_cb)
370
371# I'm not very happy about the current ownership of the RunningBuild. I have
372# my suspicions that this object should be held by the BuildManager since we
373# care about the signals in the manager
374
375def running_build_succeeded_cb (running_build, manager):
376 # Notify the manager that a build has succeeded. This is necessary as part
377 # of the 'hack' that we use for making the row in the model / view
378 # representing the ongoing build change into a row representing the
379 # completed build. Since we know only one build can be running a time then
380 # we can handle this.
381
382 # FIXME: Refactor all this so that the RunningBuild is owned by the
383 # BuildManager. It can then hook onto the signals directly and drive
384 # interesting things it cares about.
385 manager.notify_build_succeeded ()
386 print "build succeeded"
387
388def running_build_failed_cb (running_build, manager):
389 # As above
390 print "build failed"
391 manager.notify_build_failed ()
392
393def init (server, eventHandler):
394 # Initialise threading...
395 gobject.threads_init()
396 gtk.gdk.threads_init()
397
398 main_window = MainWindow ()
399 main_window.show_all ()
400
401 # Set up the build manager stuff in general
402 builds_dir = os.path.join (os.getcwd(), "results")
403 manager = BuildManager (server, builds_dir)
404 main_window.build_manager_view.set_model (manager.model)
405
406 # Do the running build setup
407 running_build = RunningBuild ()
408 main_window.running_build_view.set_model (running_build.model)
409 running_build.connect ("build-succeeded", running_build_succeeded_cb,
410 manager)
411 running_build.connect ("build-failed", running_build_failed_cb, manager)
412
413 # We need to save the manager into the MainWindow so that the toolbar
414 # button can use it.
415 # FIXME: Refactor ?
416 main_window.manager = manager
417
418 # Use a timeout function for probing the event queue to find out if we
419 # have a message waiting for us.
420 gobject.timeout_add (200,
421 event_handle_timeout,
422 eventHandler,
423 running_build)
424
425 gtk.main()
diff --git a/bitbake/lib/bb/ui/uievent.py b/bitbake/lib/bb/ui/uievent.py
new file mode 100644
index 0000000000..36302f4da7
--- /dev/null
+++ b/bitbake/lib/bb/ui/uievent.py
@@ -0,0 +1,125 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
5# Copyright (C) 2006 - 2007 Richard Purdie
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
21"""
22Use this class to fork off a thread to recieve event callbacks from the bitbake
23server and queue them for the UI to process. This process must be used to avoid
24client/server deadlocks.
25"""
26
27import socket, threading, pickle
28from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
29
30class BBUIEventQueue:
31 def __init__(self, BBServer):
32
33 self.eventQueue = []
34 self.eventQueueLock = threading.Lock()
35 self.eventQueueNotify = threading.Event()
36
37 self.BBServer = BBServer
38
39 self.t = threading.Thread()
40 self.t.setDaemon(True)
41 self.t.run = self.startCallbackHandler
42 self.t.start()
43
44 def getEvent(self):
45
46 self.eventQueueLock.acquire()
47
48 if len(self.eventQueue) == 0:
49 self.eventQueueLock.release()
50 return None
51
52 item = self.eventQueue.pop(0)
53
54 if len(self.eventQueue) == 0:
55 self.eventQueueNotify.clear()
56
57 self.eventQueueLock.release()
58 return item
59
60 def waitEvent(self, delay):
61 self.eventQueueNotify.wait(delay)
62 return self.getEvent()
63
64 def queue_event(self, event):
65 self.eventQueueLock.acquire()
66 self.eventQueue.append(pickle.loads(event))
67 self.eventQueueNotify.set()
68 self.eventQueueLock.release()
69
70 def startCallbackHandler(self):
71
72 server = UIXMLRPCServer()
73 self.host, self.port = server.socket.getsockname()
74
75 server.register_function( self.system_quit, "event.quit" )
76 server.register_function( self.queue_event, "event.send" )
77 server.socket.settimeout(1)
78
79 self.EventHandle = self.BBServer.registerEventHandler(self.host, self.port)
80
81 self.server = server
82 while not server.quit:
83 server.handle_request()
84 server.server_close()
85
86 def system_quit( self ):
87 """
88 Shut down the callback thread
89 """
90 try:
91 self.BBServer.unregisterEventHandler(self.EventHandle)
92 except:
93 pass
94 self.server.quit = True
95
96class UIXMLRPCServer (SimpleXMLRPCServer):
97
98 def __init__( self, interface = ("localhost", 0) ):
99 self.quit = False
100 SimpleXMLRPCServer.__init__( self,
101 interface,
102 requestHandler=SimpleXMLRPCRequestHandler,
103 logRequests=False, allow_none=True)
104
105 def get_request(self):
106 while not self.quit:
107 try:
108 sock, addr = self.socket.accept()
109 sock.settimeout(1)
110 return (sock, addr)
111 except socket.timeout:
112 pass
113 return (None,None)
114
115 def close_request(self, request):
116 if request is None:
117 return
118 SimpleXMLRPCServer.close_request(self, request)
119
120 def process_request(self, request, client_address):
121 if request is None:
122 return
123 SimpleXMLRPCServer.process_request(self, request, client_address)
124
125
diff --git a/bitbake/lib/bb/ui/uihelper.py b/bitbake/lib/bb/ui/uihelper.py
new file mode 100644
index 0000000000..151ffc5854
--- /dev/null
+++ b/bitbake/lib/bb/ui/uihelper.py
@@ -0,0 +1,49 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
5# Copyright (C) 2006 - 2007 Richard Purdie
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20class BBUIHelper:
21 def __init__(self):
22 self.needUpdate = False
23 self.running_tasks = {}
24 self.failed_tasks = {}
25
26 def eventHandler(self, event):
27 if isinstance(event, bb.build.TaskStarted):
28 self.running_tasks["%s %s\n" % (event._package, event._task)] = ""
29 self.needUpdate = True
30 if isinstance(event, bb.build.TaskSucceeded):
31 del self.running_tasks["%s %s\n" % (event._package, event._task)]
32 self.needUpdate = True
33 if isinstance(event, bb.build.TaskFailed):
34 del self.running_tasks["%s %s\n" % (event._package, event._task)]
35 self.failed_tasks["%s %s\n" % (event._package, event._task)] = ""
36 self.needUpdate = True
37
38 # Add runqueue event handling
39 #if isinstance(event, bb.runqueue.runQueueTaskCompleted):
40 # a = 1
41 #if isinstance(event, bb.runqueue.runQueueTaskStarted):
42 # a = 1
43 #if isinstance(event, bb.runqueue.runQueueTaskFailed):
44 # a = 1
45 #if isinstance(event, bb.runqueue.runQueueExitWait):
46 # a = 1
47
48 def getTasks(self):
49 return (self.running_tasks, self.failed_tasks)