summaryrefslogtreecommitdiffstats
path: root/bitbake-dev
diff options
context:
space:
mode:
authorRob Bradford <rob@linux.intel.com>2008-11-14 14:14:08 +0000
committerRichard Purdie <rpurdie@linux.intel.com>2008-12-01 20:50:34 +0000
commit199828c20ee67984d2efd45e81f110f33f5bfa8e (patch)
tree57262126b6a24b9bbaa47c318354ce501a2c377b /bitbake-dev
parent340b2b5612875e6544fd0f6e45e37e7206dd6db2 (diff)
downloadpoky-199828c20ee67984d2efd45e81f110f33f5bfa8e.tar.gz
bitbake-dev: Add basics of "puccho" image builder UI
Diffstat (limited to 'bitbake-dev')
-rw-r--r--bitbake-dev/lib/bb/ui/crumbs/buildmanager.py459
-rw-r--r--bitbake-dev/lib/bb/ui/crumbs/puccho.glade606
-rw-r--r--bitbake-dev/lib/bb/ui/crumbs/runningbuild.py22
-rw-r--r--bitbake-dev/lib/bb/ui/puccho.py426
4 files changed, 1508 insertions, 5 deletions
diff --git a/bitbake-dev/lib/bb/ui/crumbs/buildmanager.py b/bitbake-dev/lib/bb/ui/crumbs/buildmanager.py
new file mode 100644
index 0000000000..572cc4c7c8
--- /dev/null
+++ b/bitbake-dev/lib/bb/ui/crumbs/buildmanager.py
@@ -0,0 +1,459 @@
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
27import datetime
28import time
29
30class BuildConfiguration:
31 """ Represents a potential *or* historic *or* concrete build. It
32 encompasses all the things that we need to tell bitbake to do to make it
33 build what we want it to build.
34
35 It also stored the metadata URL and the set of possible machines (and the
36 distros / images / uris for these. Apart from the metdata URL these are
37 not serialised to file (since they may be transient). In some ways this
38 functionality might be shifted to the loader class."""
39
40 def __init__ (self):
41 self.metadata_url = None
42
43 # Tuple of (distros, image, urls)
44 self.machine_options = {}
45
46 self.machine = None
47 self.distro = None
48 self.image = None
49 self.urls = []
50 self.extra_urls = []
51 self.extra_pkgs = []
52
53 def get_machines_model (self):
54 model = gtk.ListStore (gobject.TYPE_STRING)
55 for machine in self.machine_options.keys():
56 model.append ([machine])
57
58 return model
59
60 def get_distro_and_images_models (self, machine):
61 distro_model = gtk.ListStore (gobject.TYPE_STRING)
62
63 for distro in self.machine_options[machine][0]:
64 distro_model.append ([distro])
65
66 image_model = gtk.ListStore (gobject.TYPE_STRING)
67
68 for image in self.machine_options[machine][1]:
69 image_model.append ([image])
70
71 return (distro_model, image_model)
72
73 def get_repos (self):
74 self.urls = self.machine_options[self.machine][2]
75 return self.urls
76
77 # It might be a lot lot better if we stored these in like, bitbake conf
78 # file format.
79 @staticmethod
80 def load_from_file (filename):
81 f = open (filename, "r")
82
83 conf = BuildConfiguration()
84 for line in f.readlines():
85 data = line.split (";")[1]
86 if (line.startswith ("metadata-url;")):
87 conf.metadata_url = data.strip()
88 continue
89 if (line.startswith ("url;")):
90 conf.urls += [data.strip()]
91 continue
92 if (line.startswith ("extra-url;")):
93 conf.extra_urls += [data.strip()]
94 continue
95 if (line.startswith ("machine;")):
96 conf.machine = data.strip()
97 continue
98 if (line.startswith ("distribution;")):
99 conf.distro = data.strip()
100 continue
101 if (line.startswith ("image;")):
102 conf.image = data.strip()
103 continue
104
105 f.close ()
106 return conf
107
108 # Serialise to a file. This is part of the build process and we use this
109 # to be able to repeat a given build (using the same set of parameters)
110 # but also so that we can include the details of the image / machine /
111 # distro in the build manager tree view.
112 def write_to_file (self, filename):
113 f = open (filename, "w")
114
115 lines = []
116
117 if (self.metadata_url):
118 lines += ["metadata-url;%s\n" % (self.metadata_url)]
119
120 for url in self.urls:
121 lines += ["url;%s\n" % (url)]
122
123 for url in self.extra_urls:
124 lines += ["extra-url;%s\n" % (url)]
125
126 if (self.machine):
127 lines += ["machine;%s\n" % (self.machine)]
128
129 if (self.distro):
130 lines += ["distribution;%s\n" % (self.distro)]
131
132 if (self.image):
133 lines += ["image;%s\n" % (self.image)]
134
135 f.writelines (lines)
136 f.close ()
137
138class BuildResult(gobject.GObject):
139 """ Represents an historic build. Perhaps not successful. But it includes
140 things such as the files that are in the directory (the output from the
141 build) as well as a deserialised BuildConfiguration file that is stored in
142 ".conf" in the directory for the build.
143
144 This is GObject so that it can be included in the TreeStore."""
145
146 (STATE_COMPLETE, STATE_FAILED, STATE_ONGOING) = \
147 (0, 1, 2)
148
149 def __init__ (self, parent, identifier):
150 gobject.GObject.__init__ (self)
151 self.date = None
152
153 self.files = []
154 self.status = None
155 self.identifier = identifier
156 self.path = os.path.join (parent, identifier)
157
158 # Extract the date, since the directory name is of the
159 # format build-<year><month><day>-<ordinal> we can easily
160 # pull it out.
161 # TODO: Better to stat a file?
162 (_ , date, revision) = identifier.split ("-")
163 print date
164
165 year = int (date[0:4])
166 month = int (date[4:6])
167 day = int (date[6:8])
168
169 self.date = datetime.date (year, month, day)
170
171 self.conf = None
172
173 # By default builds are STATE_FAILED unless we find a "complete" file
174 # in which case they are STATE_COMPLETE
175 self.state = BuildResult.STATE_FAILED
176 for file in os.listdir (self.path):
177 if (file.startswith (".conf")):
178 conffile = os.path.join (self.path, file)
179 self.conf = BuildConfiguration.load_from_file (conffile)
180 elif (file.startswith ("complete")):
181 self.state = BuildResult.STATE_COMPLETE
182 else:
183 self.add_file (file)
184
185 def add_file (self, file):
186 # Just add the file for now. Don't care about the type.
187 self.files += [(file, None)]
188
189class BuildManagerModel (gtk.TreeStore):
190 """ Model for the BuildManagerTreeView. This derives from gtk.TreeStore
191 but it abstracts nicely what the columns mean and the setup of the columns
192 in the model. """
193
194 (COL_IDENT, COL_DESC, COL_MACHINE, COL_DISTRO, COL_BUILD_RESULT, COL_DATE, COL_STATE) = \
195 (0, 1, 2, 3, 4, 5, 6)
196
197 def __init__ (self):
198 gtk.TreeStore.__init__ (self,
199 gobject.TYPE_STRING,
200 gobject.TYPE_STRING,
201 gobject.TYPE_STRING,
202 gobject.TYPE_STRING,
203 gobject.TYPE_OBJECT,
204 gobject.TYPE_INT64,
205 gobject.TYPE_INT)
206
207class BuildManager (gobject.GObject):
208 """ This class manages the historic builds that have been found in the
209 "results" directory but is also used for starting a new build."""
210
211 __gsignals__ = {
212 'population-finished' : (gobject.SIGNAL_RUN_LAST,
213 gobject.TYPE_NONE,
214 ()),
215 'populate-error' : (gobject.SIGNAL_RUN_LAST,
216 gobject.TYPE_NONE,
217 ())
218 }
219
220 def update_build_result (self, result, iter):
221 # Convert the date into something we can sort by.
222 date = long (time.mktime (result.date.timetuple()))
223
224 # Add a top level entry for the build
225
226 self.model.set (iter,
227 BuildManagerModel.COL_IDENT, result.identifier,
228 BuildManagerModel.COL_DESC, result.conf.image,
229 BuildManagerModel.COL_MACHINE, result.conf.machine,
230 BuildManagerModel.COL_DISTRO, result.conf.distro,
231 BuildManagerModel.COL_BUILD_RESULT, result,
232 BuildManagerModel.COL_DATE, date,
233 BuildManagerModel.COL_STATE, result.state)
234
235 # And then we use the files in the directory as the children for the
236 # top level iter.
237 for file in result.files:
238 self.model.append (iter, (None, file[0], None, None, None, date, -1))
239
240 # This function is called as an idle by the BuildManagerPopulaterThread
241 def add_build_result (self, result):
242 gtk.gdk.threads_enter()
243 self.known_builds += [result]
244
245 self.update_build_result (result, self.model.append (None))
246
247 gtk.gdk.threads_leave()
248
249 def notify_build_finished (self):
250 # This is a bit of a hack. If we have a running build running then we
251 # will have a row in the model in STATE_ONGOING. Find it and make it
252 # as if it was a proper historic build (well, it is completed now....)
253
254 # We need to use the iters here rather than the Python iterator
255 # interface to the model since we need to pass it into
256 # update_build_result
257
258 iter = self.model.get_iter_first()
259
260 while (iter):
261 (ident, state) = self.model.get(iter,
262 BuildManagerModel.COL_IDENT,
263 BuildManagerModel.COL_STATE)
264
265 if state == BuildResult.STATE_ONGOING:
266 result = BuildResult (self.results_directory, ident)
267 self.update_build_result (result, iter)
268 iter = self.model.iter_next(iter)
269
270 def notify_build_succeeded (self):
271 # Write the "complete" file so that when we create the BuildResult
272 # object we put into the model
273
274 complete_file_path = os.path.join (self.cur_build_directory, "complete")
275 f = file (complete_file_path, "w")
276 f.close()
277 self.notify_build_finished()
278
279 def notify_build_failed (self):
280 # Without a "complete" file then this will mark the build as failed:
281 self.notify_build_finished()
282
283 # This function is called as an idle
284 def emit_population_finished_signal (self):
285 gtk.gdk.threads_enter()
286 self.emit ("population-finished")
287 gtk.gdk.threads_leave()
288
289 class BuildManagerPopulaterThread (threading.Thread):
290 def __init__ (self, manager, directory):
291 threading.Thread.__init__ (self)
292 self.manager = manager
293 self.directory = directory
294
295 def run (self):
296 # For each of the "build-<...>" directories ..
297
298 if os.path.exists (self.directory):
299 for directory in os.listdir (self.directory):
300
301 if not directory.startswith ("build-"):
302 continue
303
304 build_result = BuildResult (self.directory, directory)
305 self.manager.add_build_result (build_result)
306
307 gobject.idle_add (BuildManager.emit_population_finished_signal,
308 self.manager)
309
310 def __init__ (self, server, results_directory):
311 gobject.GObject.__init__ (self)
312
313 # The builds that we've found from walking the result directory
314 self.known_builds = []
315
316 # Save out the bitbake server, we need this for issuing commands to
317 # the cooker:
318 self.server = server
319
320 # The TreeStore that we use
321 self.model = BuildManagerModel ()
322
323 # The results directory is where we create (and look for) the
324 # build-<xyz>-<n> directories. We need to populate ourselves from
325 # directory
326 self.results_directory = results_directory
327 self.populate_from_directory (self.results_directory)
328
329 def populate_from_directory (self, directory):
330 thread = BuildManager.BuildManagerPopulaterThread (self, directory)
331 thread.start()
332
333 # Come up with the name for the next build ident by combining "build-"
334 # with the date formatted as yyyymmdd and then an ordinal. We do this by
335 # an optimistic algorithm incrementing the ordinal if we find that it
336 # already exists.
337 def get_next_build_ident (self):
338 today = datetime.date.today ()
339 datestr = str (today.year) + str (today.month) + str (today.day)
340
341 revision = 0
342 test_name = "build-%s-%d" % (datestr, revision)
343 test_path = os.path.join (self.results_directory, test_name)
344
345 while (os.path.exists (test_path)):
346 revision += 1
347 test_name = "build-%s-%d" % (datestr, revision)
348 test_path = os.path.join (self.results_directory, test_name)
349
350 return test_name
351
352 # Take a BuildConfiguration and then try and build it based on the
353 # parameters of that configuration. S
354 def do_build (self, conf):
355 server = self.server
356
357 # Work out the build directory. Note we actually create the
358 # directories here since we need to write the ".conf" file. Otherwise
359 # we could have relied on bitbake's builder thread to actually make
360 # the directories as it proceeds with the build.
361 ident = self.get_next_build_ident ()
362 build_directory = os.path.join (self.results_directory,
363 ident)
364 self.cur_build_directory = build_directory
365 os.makedirs (build_directory)
366
367 conffile = os.path.join (build_directory, ".conf")
368 conf.write_to_file (conffile)
369
370 # Add a row to the model representing this ongoing build. It's kinda a
371 # fake entry. If this build completes or fails then this gets updated
372 # with the real stuff like the historic builds
373 date = long (time.time())
374 self.model.append (None, (ident, conf.image, conf.machine, conf.distro,
375 None, date, BuildResult.STATE_ONGOING))
376 try:
377 server.runCommand(["setVariable", "BUILD_IMAGES_FROM_FEEDS", 1])
378 server.runCommand(["setVariable", "MACHINE", conf.machine])
379 server.runCommand(["setVariable", "DISTRO", conf.distro])
380 server.runCommand(["setVariable", "PACKAGE_CLASSES", "package_ipk"])
381 server.runCommand(["setVariable", "BBFILES", \
382 """${OEROOT}/meta/packages/*/*.bb ${OEROOT}/meta-moblin/packages/*/*.bb"""])
383 server.runCommand(["setVariable", "TMPDIR", "${OEROOT}/build/tmp"])
384 server.runCommand(["setVariable", "IPK_FEED_URIS", \
385 " ".join(conf.get_repos())])
386 server.runCommand(["setVariable", "DEPLOY_DIR_IMAGE",
387 build_directory])
388 server.runCommand(["buildTargets", [conf.image], "rootfs"])
389
390 except Exception, e:
391 print e
392
393class BuildManagerTreeView (gtk.TreeView):
394 """ The tree view for the build manager. This shows the historic builds
395 and so forth. """
396
397 # We use this function to control what goes in the cell since we store
398 # the date in the model as seconds since the epoch (for sorting) and so we
399 # need to make it human readable.
400 def date_format_custom_cell_data_func (self, col, cell, model, iter):
401 date = model.get (iter, BuildManagerModel.COL_DATE)[0]
402 datestr = time.strftime("%A %d %B %Y", time.localtime(date))
403 cell.set_property ("text", datestr)
404
405 # This format function controls what goes in the cell. We use this to map
406 # the integer state to a string and also to colourise the text
407 def state_format_custom_cell_data_fun (self, col, cell, model, iter):
408 state = model.get (iter, BuildManagerModel.COL_STATE)[0]
409
410 if (state == BuildResult.STATE_ONGOING):
411 cell.set_property ("text", "Active")
412 cell.set_property ("foreground", "#000000")
413 elif (state == BuildResult.STATE_FAILED):
414 cell.set_property ("text", "Failed")
415 cell.set_property ("foreground", "#ff0000")
416 elif (state == BuildResult.STATE_COMPLETE):
417 cell.set_property ("text", "Complete")
418 cell.set_property ("foreground", "#00ff00")
419 else:
420 cell.set_property ("text", "")
421
422 def __init__ (self):
423 gtk.TreeView.__init__(self)
424
425 # Misc descriptiony thing
426 renderer = gtk.CellRendererText ()
427 col = gtk.TreeViewColumn (None, renderer,
428 text=BuildManagerModel.COL_DESC)
429 self.append_column (col)
430
431 # Machine
432 renderer = gtk.CellRendererText ()
433 col = gtk.TreeViewColumn ("Machine", renderer,
434 text=BuildManagerModel.COL_MACHINE)
435 self.append_column (col)
436
437 # distro
438 renderer = gtk.CellRendererText ()
439 col = gtk.TreeViewColumn ("Distribution", renderer,
440 text=BuildManagerModel.COL_DISTRO)
441 self.append_column (col)
442
443 # date (using a custom function for formatting the cell contents it
444 # takes epoch -> human readable string)
445 renderer = gtk.CellRendererText ()
446 col = gtk.TreeViewColumn ("Date", renderer,
447 text=BuildManagerModel.COL_DATE)
448 self.append_column (col)
449 col.set_cell_data_func (renderer,
450 self.date_format_custom_cell_data_func)
451
452 # For status.
453 renderer = gtk.CellRendererText ()
454 col = gtk.TreeViewColumn ("Status", renderer,
455 text = BuildManagerModel.COL_STATE)
456 self.append_column (col)
457 col.set_cell_data_func (renderer,
458 self.state_format_custom_cell_data_fun)
459
diff --git a/bitbake-dev/lib/bb/ui/crumbs/puccho.glade b/bitbake-dev/lib/bb/ui/crumbs/puccho.glade
new file mode 100644
index 0000000000..d7553a6e14
--- /dev/null
+++ b/bitbake-dev/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-dev/lib/bb/ui/crumbs/runningbuild.py b/bitbake-dev/lib/bb/ui/crumbs/runningbuild.py
index b9aba5b8cc..54d56c2452 100644
--- a/bitbake-dev/lib/bb/ui/crumbs/runningbuild.py
+++ b/bitbake-dev/lib/bb/ui/crumbs/runningbuild.py
@@ -18,8 +18,9 @@
18# with this program; if not, write to the Free Software Foundation, Inc., 18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 20
21import gobject
22import gtk 21import gtk
22import gobject
23import gtk.glade
23 24
24class RunningBuildModel (gtk.TreeStore): 25class RunningBuildModel (gtk.TreeStore):
25 (COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5) 26 (COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5)
@@ -34,9 +35,12 @@ class RunningBuildModel (gtk.TreeStore):
34 35
35class RunningBuild (gobject.GObject): 36class RunningBuild (gobject.GObject):
36 __gsignals__ = { 37 __gsignals__ = {
37 'build-finished' : (gobject.SIGNAL_RUN_LAST, 38 'build-succeeded' : (gobject.SIGNAL_RUN_LAST,
38 gobject.TYPE_NONE, 39 gobject.TYPE_NONE,
39 ()) 40 ()),
41 'build-failed' : (gobject.SIGNAL_RUN_LAST,
42 gobject.TYPE_NONE,
43 ())
40 } 44 }
41 pids_to_task = {} 45 pids_to_task = {}
42 tasks_to_iter = {} 46 tasks_to_iter = {}
@@ -150,6 +154,15 @@ class RunningBuild (gobject.GObject):
150 del self.tasks_to_iter[(package, task)] 154 del self.tasks_to_iter[(package, task)]
151 del self.pids_to_task[pid] 155 del self.pids_to_task[pid]
152 156
157 elif event[0].startswith('bb.event.BuildCompleted'):
158 failures = int (event[1]['_failures'])
159
160 # Emit the appropriate signal depending on the number of failures
161 if (failures > 1):
162 self.emit ("build-failed")
163 else:
164 self.emit ("build-succeeded")
165
153class RunningBuildTreeView (gtk.TreeView): 166class RunningBuildTreeView (gtk.TreeView):
154 def __init__ (self): 167 def __init__ (self):
155 gtk.TreeView.__init__ (self) 168 gtk.TreeView.__init__ (self)
@@ -166,4 +179,3 @@ class RunningBuildTreeView (gtk.TreeView):
166 self.append_column (col) 179 self.append_column (col)
167 180
168 181
169
diff --git a/bitbake-dev/lib/bb/ui/puccho.py b/bitbake-dev/lib/bb/ui/puccho.py
new file mode 100644
index 0000000000..a6a613f1cf
--- /dev/null
+++ b/bitbake-dev/lib/bb/ui/puccho.py
@@ -0,0 +1,426 @@
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
27import datetime
28
29from bb.ui.crumbs.buildmanager import BuildManager, BuildConfiguration
30from bb.ui.crumbs.buildmanager import BuildManagerTreeView
31
32from bb.ui.crumbs.runningbuild import RunningBuild, RunningBuildTreeView
33
34# The metadata loader is used by the BuildSetupDialog to download the
35# available options to populate the dialog
36class MetaDataLoader(gobject.GObject):
37 """ This class provides the mechanism for loading the metadata (the
38 fetching and parsing) from a given URL. The metadata encompasses details
39 on what machines are available. The distribution and images available for
40 the machine and the the uris to use for building the given machine."""
41 __gsignals__ = {
42 'success' : (gobject.SIGNAL_RUN_LAST,
43 gobject.TYPE_NONE,
44 ()),
45 'error' : (gobject.SIGNAL_RUN_LAST,
46 gobject.TYPE_NONE,
47 (gobject.TYPE_STRING,))
48 }
49
50 # We use these little helper functions to ensure that we take the gdk lock
51 # when emitting the signal. These functions are called as idles (so that
52 # they happen in the gtk / main thread's main loop.
53 def emit_error_signal (self, remark):
54 gtk.gdk.threads_enter()
55 self.emit ("error", remark)
56 gtk.gdk.threads_leave()
57
58 def emit_success_signal (self):
59 gtk.gdk.threads_enter()
60 self.emit ("success")
61 gtk.gdk.threads_leave()
62
63 def __init__ (self):
64 gobject.GObject.__init__ (self)
65
66 class LoaderThread(threading.Thread):
67 """ This class provides an asynchronous loader for the metadata (by
68 using threads and signals). This is useful since the metadata may be
69 at a remote URL."""
70 class LoaderImportException (Exception):
71 pass
72
73 def __init__(self, loader, url):
74 threading.Thread.__init__ (self)
75 self.url = url
76 self.loader = loader
77
78 def run (self):
79 result = {}
80 try:
81 f = urllib2.urlopen (self.url)
82
83 # Parse the metadata format. The format is....
84 # <machine>;<default distro>|<distro>...;<default image>|<image>...;<type##url>|...
85 for line in f.readlines():
86 components = line.split(";")
87 if (len (components) < 4):
88 raise MetaDataLoader.LoaderThread.LoaderImportException
89 machine = components[0]
90 distros = components[1].split("|")
91 images = components[2].split("|")
92 urls = components[3].split("|")
93
94 result[machine] = (distros, images, urls)
95
96 # Create an object representing this *potential*
97 # configuration. It can become concrete if the machine, distro
98 # and image are all chosen in the UI
99 configuration = BuildConfiguration()
100 configuration.metadata_url = self.url
101 configuration.machine_options = result
102 self.loader.configuration = configuration
103
104 # Emit that we've actually got a configuration
105 gobject.idle_add (MetaDataLoader.emit_success_signal,
106 self.loader)
107
108 except MetaDataLoader.LoaderThread.LoaderImportException, e:
109 gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader,
110 "Repository metadata corrupt")
111 except Exception, e:
112 gobject.idle_add (MetaDataLoader.emit_error_signal, self.loader,
113 "Unable to download repository metadata")
114 print e
115
116 def try_fetch_from_url (self, url):
117 # Try and download the metadata. Firing a signal if successful
118 thread = MetaDataLoader.LoaderThread(self, url)
119 thread.start()
120
121class BuildSetupDialog (gtk.Dialog):
122 RESPONSE_BUILD = 1
123
124 # A little helper method that just sets the states on the widgets based on
125 # whether we've got good metadata or not.
126 def set_configurable (self, configurable):
127 if (self.configurable == configurable):
128 return
129
130 self.configurable = configurable
131 for widget in self.conf_widgets:
132 widget.set_sensitive (configurable)
133
134 if not configurable:
135 self.machine_combo.set_active (-1)
136 self.distribution_combo.set_active (-1)
137 self.image_combo.set_active (-1)
138
139 # GTK widget callbacks
140 def refresh_button_clicked (self, button):
141 # Refresh button clicked.
142
143 url = self.location_entry.get_chars (0, -1)
144 self.loader.try_fetch_from_url(url)
145
146 def repository_entry_editable_changed (self, entry):
147 if (len (entry.get_chars (0, -1)) > 0):
148 self.refresh_button.set_sensitive (True)
149 else:
150 self.refresh_button.set_sensitive (False)
151 self.clear_status_message()
152
153 # If we were previously configurable we are no longer since the
154 # location entry has been changed
155 self.set_configurable (False)
156
157 def machine_combo_changed (self, combobox):
158 active_iter = combobox.get_active_iter()
159
160 if not active_iter:
161 return
162
163 model = combobox.get_model()
164
165 if model:
166 chosen_machine = model.get (active_iter, 0)[0]
167
168 (distros_model, images_model) = \
169 self.loader.configuration.get_distro_and_images_models (chosen_machine)
170
171 self.distribution_combo.set_model (distros_model)
172 self.image_combo.set_model (images_model)
173
174 # Callbacks from the loader
175 def loader_success_cb (self, loader):
176 self.status_image.set_from_icon_name ("info",
177 gtk.ICON_SIZE_BUTTON)
178 self.status_image.show()
179 self.status_label.set_label ("Repository metadata successfully downloaded")
180
181 # Set the models on the combo boxes based on the models generated from
182 # the configuration that the loader has created
183
184 # We just need to set the machine here, that then determines the
185 # distro and image options. Cunning huh? :-)
186
187 self.configuration = self.loader.configuration
188 model = self.configuration.get_machines_model ()
189 self.machine_combo.set_model (model)
190
191 self.set_configurable (True)
192
193 def loader_error_cb (self, loader, message):
194 self.status_image.set_from_icon_name ("error",
195 gtk.ICON_SIZE_BUTTON)
196 self.status_image.show()
197 self.status_label.set_text ("Error downloading repository metadata")
198 for widget in self.conf_widgets:
199 widget.set_sensitive (False)
200
201 def clear_status_message (self):
202 self.status_image.hide()
203 self.status_label.set_label (
204 """<i>Enter the repository location and press _Refresh</i>""")
205
206 def __init__ (self):
207 gtk.Dialog.__init__ (self)
208
209 # Cancel
210 self.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
211
212 # Build
213 button = gtk.Button ("_Build", None, True)
214 image = gtk.Image ()
215 image.set_from_stock (gtk.STOCK_EXECUTE,gtk.ICON_SIZE_BUTTON)
216 button.set_image (image)
217 self.add_action_widget (button, BuildSetupDialog.RESPONSE_BUILD)
218 button.show_all ()
219
220 # Pull in *just* the table from the Glade XML data.
221 gxml = gtk.glade.XML ("bitbake-dev/lib/bb/ui/crumbs/puccho.glade",
222 root = "build_table")
223 table = gxml.get_widget ("build_table")
224 self.vbox.pack_start (table, True, False, 0)
225
226 # Grab all the widgets that we need to turn on/off when we refresh...
227 self.conf_widgets = []
228 self.conf_widgets += [gxml.get_widget ("machine_label")]
229 self.conf_widgets += [gxml.get_widget ("distribution_label")]
230 self.conf_widgets += [gxml.get_widget ("image_label")]
231 self.conf_widgets += [gxml.get_widget ("machine_combo")]
232 self.conf_widgets += [gxml.get_widget ("distribution_combo")]
233 self.conf_widgets += [gxml.get_widget ("image_combo")]
234
235 # Grab the status widgets
236 self.status_image = gxml.get_widget ("status_image")
237 self.status_label = gxml.get_widget ("status_label")
238
239 # Grab the refresh button and connect to the clicked signal
240 self.refresh_button = gxml.get_widget ("refresh_button")
241 self.refresh_button.connect ("clicked", self.refresh_button_clicked)
242
243 # Grab the location entry and connect to editable::changed
244 self.location_entry = gxml.get_widget ("location_entry")
245 self.location_entry.connect ("changed",
246 self.repository_entry_editable_changed)
247
248 # Grab the machine combo and hook onto the changed signal. This then
249 # allows us to populate the distro and image combos
250 self.machine_combo = gxml.get_widget ("machine_combo")
251 self.machine_combo.connect ("changed", self.machine_combo_changed)
252
253 # Setup the combo
254 cell = gtk.CellRendererText()
255 self.machine_combo.pack_start(cell, True)
256 self.machine_combo.add_attribute(cell, 'text', 0)
257
258 # Grab the distro and image combos. We need these to populate with
259 # models once the machine is chosen
260 self.distribution_combo = gxml.get_widget ("distribution_combo")
261 cell = gtk.CellRendererText()
262 self.distribution_combo.pack_start(cell, True)
263 self.distribution_combo.add_attribute(cell, 'text', 0)
264
265 self.image_combo = gxml.get_widget ("image_combo")
266 cell = gtk.CellRendererText()
267 self.image_combo.pack_start(cell, True)
268 self.image_combo.add_attribute(cell, 'text', 0)
269
270 # Put the default descriptive text in the status box
271 self.clear_status_message()
272
273 # Mark as non-configurable, this is just greys out the widgets the
274 # user can't yet use
275 self.configurable = False
276 self.set_configurable(False)
277
278 # Show the table
279 table.show_all ()
280
281 # The loader and some signals connected to it to update the status
282 # area
283 self.loader = MetaDataLoader()
284 self.loader.connect ("success", self.loader_success_cb)
285 self.loader.connect ("error", self.loader_error_cb)
286
287 def update_configuration (self):
288 """ A poorly named function but it updates the internal configuration
289 from the widgets. This can make that configuration concrete and can
290 thus be used for building """
291 # Extract the chosen machine from the combo
292 model = self.machine_combo.get_model()
293 active_iter = self.machine_combo.get_active_iter()
294 if (active_iter):
295 self.configuration.machine = model.get(active_iter, 0)[0]
296
297 # Extract the chosen distro from the combo
298 model = self.distribution_combo.get_model()
299 active_iter = self.distribution_combo.get_active_iter()
300 if (active_iter):
301 self.configuration.distro = model.get(active_iter, 0)[0]
302
303 # Extract the chosen image from the combo
304 model = self.image_combo.get_model()
305 active_iter = self.image_combo.get_active_iter()
306 if (active_iter):
307 self.configuration.image = model.get(active_iter, 0)[0]
308
309# This function operates to pull events out from the event queue and then push
310# them into the RunningBuild (which then drives the RunningBuild which then
311# pushes through and updates the progress tree view.)
312#
313# TODO: Should be a method on the RunningBuild class
314def event_handle_timeout (eventHandler, build):
315 # Consume as many messages as we can ...
316 event = eventHandler.getEvent()
317 while event:
318 build.handle_event (event)
319 event = eventHandler.getEvent()
320 return True
321
322class MainWindow (gtk.Window):
323
324 # Callback that gets fired when the user hits a button in the
325 # BuildSetupDialog.
326 def build_dialog_box_response_cb (self, dialog, response_id):
327 conf = None
328 if (response_id == BuildSetupDialog.RESPONSE_BUILD):
329 dialog.update_configuration()
330 print dialog.configuration.machine, dialog.configuration.distro, \
331 dialog.configuration.image
332 conf = dialog.configuration
333
334 dialog.destroy()
335
336 if conf:
337 self.manager.do_build (conf)
338
339 def build_button_clicked_cb (self, button):
340 dialog = BuildSetupDialog ()
341
342 # For some unknown reason Dialog.run causes nice little deadlocks ... :-(
343 dialog.connect ("response", self.build_dialog_box_response_cb)
344 dialog.show()
345
346 def __init__ (self):
347 gtk.Window.__init__ (self)
348
349 # Pull in *just* the main vbox from the Glade XML data and then pack
350 # that inside the window
351 gxml = gtk.glade.XML ("bitbake-dev/lib/bb/ui/crumbs/puccho.glade",
352 root = "main_window_vbox")
353 vbox = gxml.get_widget ("main_window_vbox")
354 self.add (vbox)
355
356 # Create the tree views for the build manager view and the progress view
357 self.build_manager_view = BuildManagerTreeView()
358 self.running_build_view = RunningBuildTreeView()
359
360 # Grab the scrolled windows that we put the tree views into
361 self.results_scrolledwindow = gxml.get_widget ("results_scrolledwindow")
362 self.progress_scrolledwindow = gxml.get_widget ("progress_scrolledwindow")
363
364 # Put the tree views inside ...
365 self.results_scrolledwindow.add (self.build_manager_view)
366 self.progress_scrolledwindow.add (self.running_build_view)
367
368 # Hook up the build button...
369 self.build_button = gxml.get_widget ("main_toolbutton_build")
370 self.build_button.connect ("clicked", self.build_button_clicked_cb)
371
372# I'm not very happy about the current ownership of the RunningBuild. I have
373# my suspicions that this object should be held by the BuildManager since we
374# care about the signals in the manager
375
376def running_build_succeeded_cb (running_build, manager):
377 # Notify the manager that a build has succeeded. This is necessary as part
378 # of the 'hack' that we use for making the row in the model / view
379 # representing the ongoing build change into a row representing the
380 # completed build. Since we know only one build can be running a time then
381 # we can handle this.
382
383 # FIXME: Refactor all this so that the RunningBuild is owned by the
384 # BuildManager. It can then hook onto the signals directly and drive
385 # interesting things it cares about.
386 manager.notify_build_succeeded ()
387 print "build succeeded"
388
389def running_build_failed_cb (running_build, manager):
390 # As above
391 print "build failed"
392 manager.notify_build_failed ()
393
394def init (server, eventHandler):
395 # Initialise threading...
396 gobject.threads_init()
397 gtk.gdk.threads_init()
398
399 main_window = MainWindow ()
400 main_window.show_all ()
401
402 # Set up the build manager stuff in general
403 builds_dir = os.path.join (os.getcwd(), "results")
404 manager = BuildManager (server, builds_dir)
405 main_window.build_manager_view.set_model (manager.model)
406
407 # Do the running build setup
408 running_build = RunningBuild ()
409 main_window.running_build_view.set_model (running_build.model)
410 running_build.connect ("build-succeeded", running_build_succeeded_cb,
411 manager)
412 running_build.connect ("build-failed", running_build_failed_cb, manager)
413
414 # We need to save the manager into the MainWindow so that the toolbar
415 # button can use it.
416 # FIXME: Refactor ?
417 main_window.manager = manager
418
419 # Use a timeout function for probing the event queue to find out if we
420 # have a message waiting for us.
421 gobject.timeout_add (200,
422 event_handle_timeout,
423 eventHandler,
424 running_build)
425
426 gtk.main()