summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
authorJoshua Lock <josh@linux.intel.com>2011-07-01 15:58:50 -0700
committerRichard Purdie <richard.purdie@linuxfoundation.org>2011-07-05 14:40:30 +0100
commit4cc291c007103c19779f995e852b37dbad122293 (patch)
tree3447d62ef1ba2eca08137b8e13df58f8a337a453 /bitbake
parent7fc9c3488f7865111ec052d1cab213d216d2b414 (diff)
downloadpoky-4cc291c007103c19779f995e852b37dbad122293.tar.gz
hob: re-designed interaction and implementation
Highlights include: * Atempted GNOME HIG compliance * Simplified UI and interaction model * Sorting and type to find in tree views * Preferences dialog to modify local settings * Dialog to add and remove layers * Search in packages list * Save/Load image recipes The build model has been changed, hob will attempt to build all dependent packages of an image and then use the buildFile server method to build the created image. (Bitbake rev: 48e64acaae4a741b9f5630f426fb4e6142755c2c) Signed-off-by: Joshua Lock <josh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-rw-r--r--bitbake/lib/bb/ui/crumbs/configurator.py278
-rw-r--r--bitbake/lib/bb/ui/crumbs/hig.py61
-rw-r--r--bitbake/lib/bb/ui/crumbs/hobeventhandler.py218
-rw-r--r--bitbake/lib/bb/ui/crumbs/hobprefs.py293
-rw-r--r--bitbake/lib/bb/ui/crumbs/layereditor.py136
-rw-r--r--bitbake/lib/bb/ui/crumbs/runningbuild.py12
-rw-r--r--bitbake/lib/bb/ui/crumbs/tasklistmodel.py326
-rw-r--r--bitbake/lib/bb/ui/hob.py924
8 files changed, 1824 insertions, 424 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/configurator.py b/bitbake/lib/bb/ui/crumbs/configurator.py
new file mode 100644
index 0000000000..b694143d4c
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/configurator.py
@@ -0,0 +1,278 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011 Intel Corporation
5#
6# Authored by Joshua Lock <josh@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 copy
23import re, os
24from bb import data
25
26class Configurator(gobject.GObject):
27
28 """
29 A GObject to handle writing modified configuration values back
30 to conf files.
31 """
32 __gsignals__ = {
33 "layers-loaded" : (gobject.SIGNAL_RUN_LAST,
34 gobject.TYPE_NONE,
35 ()),
36 "layers-changed" : (gobject.SIGNAL_RUN_LAST,
37 gobject.TYPE_NONE,
38 ())
39 }
40
41 def __init__(self):
42 gobject.GObject.__init__(self)
43 self.local = None
44 self.bblayers = None
45 self.enabled_layers = {}
46 self.loaded_layers = {}
47 self.config = {}
48 self.orig_config = {}
49
50 # NOTE: cribbed from the cooker...
51 def _parse(self, f, data, include=False):
52 try:
53 return bb.parse.handle(f, data, include)
54 except (IOError, bb.parse.ParseError) as exc:
55 parselog.critical("Unable to parse %s: %s" % (f, exc))
56 sys.exit(1)
57
58 def _loadLocalConf(self, path):
59 def getString(var):
60 return bb.data.getVar(var, data, True) or ""
61
62 self.local = path
63
64 if self.orig_config:
65 del self.orig_config
66 self.orig_config = {}
67
68 data = bb.data.init()
69 data = self._parse(self.local, data)
70
71 # We only need to care about certain variables
72 mach = getString('MACHINE')
73 if mach and mach != self.config.get('MACHINE', ''):
74 self.config['MACHINE'] = mach
75 sdkmach = getString('SDKMACHINE')
76 if sdkmach and sdkmach != self.config.get('SDKMACHINE', ''):
77 self.config['SDKMACHINE'] = sdkmach
78 distro = getString('DISTRO')
79 if distro and distro != self.config.get('DISTRO', ''):
80 self.config['DISTRO'] = distro
81 bbnum = getString('BB_NUMBER_THREADS')
82 if bbnum and bbnum != self.config.get('BB_NUMBER_THREADS', ''):
83 self.config['BB_NUMBER_THREADS'] = bbnum
84 pmake = getString('PARALLEL_MAKE')
85 if pmake and pmake != self.config.get('PARALLEL_MAKE', ''):
86 self.config['PARALLEL_MAKE'] = pmake
87 incompat = getString('INCOMPATIBLE_LICENSE')
88 if incompat and incompat != self.config.get('INCOMPATIBLE_LICENSE', ''):
89 self.config['INCOMPATIBLE_LICENSE'] = incompat
90 pclass = getString('PACKAGE_CLASSES')
91 if pclass and pclass != self.config.get('PACKAGE_CLASSES', ''):
92 self.config['PACKAGE_CLASSES'] = pclass
93
94 self.orig_config = copy.deepcopy(self.config)
95
96 def setLocalConfVar(self, var, val):
97 if var in self.config:
98 self.config[var] = val
99
100 def _loadLayerConf(self, path):
101 self.bblayers = path
102 self.enabled_layers = {}
103 self.loaded_layers = {}
104 data = bb.data.init()
105 data = self._parse(self.bblayers, data)
106 layers = (bb.data.getVar('BBLAYERS', data, True) or "").split()
107 for layer in layers:
108 # TODO: we may be better off calling the layer by its
109 # BBFILE_COLLECTIONS value?
110 name = self._getLayerName(layer)
111 self.loaded_layers[name] = layer
112
113 self.enabled_layers = copy.deepcopy(self.loaded_layers)
114 self.emit("layers-loaded")
115
116 def _addConfigFile(self, path):
117 pref, sep, filename = path.rpartition("/")
118 if filename == "local.conf" or filename == "hob.local.conf":
119 self._loadLocalConf(path)
120 elif filename == "bblayers.conf":
121 self._loadLayerConf(path)
122
123 def _splitLayer(self, path):
124 # we only care about the path up to /conf/layer.conf
125 layerpath, conf, end = path.rpartition("/conf/")
126 return layerpath
127
128 def _getLayerName(self, path):
129 # Should this be the collection name?
130 layerpath, sep, name = path.rpartition("/")
131 return name
132
133 def disableLayer(self, layer):
134 if layer in self.enabled_layers:
135 del self.enabled_layers[layer]
136
137 def addLayerConf(self, confpath):
138 layerpath = self._splitLayer(confpath)
139 name = self._getLayerName(layerpath)
140 if name not in self.enabled_layers:
141 self.addLayer(name, layerpath)
142 return name, layerpath
143
144 def addLayer(self, name, path):
145 self.enabled_layers[name] = path
146
147 def _isLayerConfDirty(self):
148 # if a different number of layers enabled to what was
149 # loaded, definitely different
150 if len(self.enabled_layers) != len(self.loaded_layers):
151 return True
152
153 for layer in self.loaded_layers:
154 # if layer loaded but no longer present, definitely dirty
155 if layer not in self.enabled_layers:
156 return True
157
158 for layer in self.enabled_layers:
159 # if this layer wasn't present at load, definitely dirty
160 if layer not in self.loaded_layers:
161 return True
162 # if this layers path has changed, definitely dirty
163 if self.enabled_layers[layer] != self.loaded_layers[layer]:
164 return True
165
166 return False
167
168 def _constructLayerEntry(self):
169 """
170 Returns a string representing the new layer selection
171 """
172 layers = self.enabled_layers.copy()
173 # Construct BBLAYERS entry
174 layer_entry = "BBLAYERS = \" \\\n"
175 if 'meta' in layers:
176 layer_entry = layer_entry + " %s \\\n" % layers['meta']
177 del layers['meta']
178 for layer in layers:
179 layer_entry = layer_entry + " %s \\\n" % layers[layer]
180 layer_entry = layer_entry + " \""
181
182 return "".join(layer_entry)
183
184 def writeLocalConf(self):
185 # Dictionary containing only new or modified variables
186 changed_values = {}
187 for var in self.config:
188 val = self.config[var]
189 if self.orig_config.get(var, None) != val:
190 changed_values[var] = val
191
192 if not len(changed_values):
193 return
194
195 # Create a backup of the local.conf
196 bkup = "%s~" % self.local
197 os.rename(self.local, bkup)
198
199 # read the original conf into a list
200 with open(bkup, 'r') as config:
201 config_lines = config.readlines()
202
203 new_config_lines = ["\n"]
204 for var in changed_values:
205 # Convenience function for re.subn(). If the pattern matches
206 # return a string which contains an assignment using the same
207 # assignment operator as the old assignment.
208 def replace_val(matchobj):
209 var = matchobj.group(1) # config variable
210 op = matchobj.group(2) # assignment operator
211 val = changed_values[var] # new config value
212 return "%s %s \"%s\"" % (var, op, val)
213
214 pattern = '^\s*(%s)\s*([+=?.]+)(.*)' % re.escape(var)
215 p = re.compile(pattern)
216 cnt = 0
217 replaced = False
218
219 # Iterate over the local.conf lines and if they are a match
220 # for the pattern comment out the line and append a new line
221 # with the new VAR op "value" entry
222 for line in config_lines:
223 new_line, replacements = p.subn(replace_val, line)
224 if replacements:
225 config_lines[cnt] = "#%s" % line
226 new_config_lines.append(new_line)
227 replaced = True
228 cnt = cnt + 1
229
230 if not replaced:
231 new_config_lines.append("%s = \"%s\"" % (var, changed_values[var]))
232
233 # Add the modified variables
234 config_lines.extend(new_config_lines)
235
236 # Write the updated lines list object to the local.conf
237 with open(self.local, "w") as n:
238 n.write("".join(config_lines))
239
240 del self.orig_config
241 self.orig_config = copy.deepcopy(self.config)
242
243 def writeLayerConf(self):
244 # If we've not added/removed new layers don't write
245 if not self._isLayerConfDirty():
246 return
247
248 # This pattern should find the existing BBLAYERS
249 pattern = 'BBLAYERS\s=\s\".*\"'
250
251 # Backup the users bblayers.conf
252 bkup = "%s~" % self.bblayers
253 os.rename(self.bblayers, bkup)
254
255 replacement = self._constructLayerEntry()
256
257 with open(bkup, "r") as f:
258 contents = f.read()
259 p = re.compile(pattern, re.DOTALL)
260 new = p.sub(replacement, contents)
261
262 with open(self.bblayers, "w") as n:
263 n.write(new)
264
265 # At some stage we should remove the backup we've created
266 # though we should probably verify it first
267 #os.remove(bkup)
268
269 # set loaded_layers for dirtiness tracking
270 self.loaded_layers = copy.deepcopy(self.enabled_layers)
271
272 self.emit("layers-changed")
273
274 def configFound(self, handler, path):
275 self._addConfigFile(path)
276
277 def loadConfig(self, path):
278 self._addConfigFile(path)
diff --git a/bitbake/lib/bb/ui/crumbs/hig.py b/bitbake/lib/bb/ui/crumbs/hig.py
new file mode 100644
index 0000000000..b3b3c7a72e
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/hig.py
@@ -0,0 +1,61 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011 Intel Corporation
5#
6# Authored by Joshua Lock <josh@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
23"""
24The following are convenience classes for implementing GNOME HIG compliant
25BitBake GUI's
26In summary: spacing = 12px, border-width = 6px
27"""
28
29class CrumbsDialog(gtk.Dialog):
30 """
31 A GNOME HIG compliant dialog widget.
32 Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons
33 """
34 def __init__(self, parent=None, label="", icon=gtk.STOCK_INFO):
35 gtk.Dialog.__init__(self, "", parent, gtk.DIALOG_DESTROY_WITH_PARENT)
36
37 #self.set_property("has-separator", False) # note: deprecated in 2.22
38
39 self.set_border_width(6)
40 self.vbox.set_property("spacing", 12)
41 self.action_area.set_property("spacing", 12)
42 self.action_area.set_property("border-width", 6)
43
44 first_row = gtk.HBox(spacing=12)
45 first_row.set_property("border-width", 6)
46 first_row.show()
47 self.vbox.add(first_row)
48
49 self.icon = gtk.Image()
50 self.icon.set_from_stock(icon, gtk.ICON_SIZE_DIALOG)
51 self.icon.set_property("yalign", 0.00)
52 self.icon.show()
53 first_row.add(self.icon)
54
55 self.label = gtk.Label()
56 self.label.set_use_markup(True)
57 self.label.set_line_wrap(True)
58 self.label.set_markup(label)
59 self.label.set_property("yalign", 0.00)
60 self.label.show()
61 first_row.add(self.label)
diff --git a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py
index c474491d6a..fa79e0c7a2 100644
--- a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py
+++ b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py
@@ -19,7 +19,6 @@
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 21import gobject
22from bb.ui.crumbs.progress import ProgressBar
23 22
24progress_total = 0 23progress_total = 0
25 24
@@ -29,46 +28,78 @@ class HobHandler(gobject.GObject):
29 This object does BitBake event handling for the hob gui. 28 This object does BitBake event handling for the hob gui.
30 """ 29 """
31 __gsignals__ = { 30 __gsignals__ = {
32 "machines-updated" : (gobject.SIGNAL_RUN_LAST, 31 "machines-updated" : (gobject.SIGNAL_RUN_LAST,
33 gobject.TYPE_NONE, 32 gobject.TYPE_NONE,
34 (gobject.TYPE_PYOBJECT,)), 33 (gobject.TYPE_PYOBJECT,)),
35 "distros-updated" : (gobject.SIGNAL_RUN_LAST, 34 "sdk-machines-updated": (gobject.SIGNAL_RUN_LAST,
36 gobject.TYPE_NONE, 35 gobject.TYPE_NONE,
37 (gobject.TYPE_PYOBJECT,)), 36 (gobject.TYPE_PYOBJECT,)),
38 "generating-data" : (gobject.SIGNAL_RUN_LAST, 37 "distros-updated" : (gobject.SIGNAL_RUN_LAST,
39 gobject.TYPE_NONE, 38 gobject.TYPE_NONE,
40 ()), 39 (gobject.TYPE_PYOBJECT,)),
41 "data-generated" : (gobject.SIGNAL_RUN_LAST, 40 "package-formats-found" : (gobject.SIGNAL_RUN_LAST,
42 gobject.TYPE_NONE, 41 gobject.TYPE_NONE,
43 ()) 42 (gobject.TYPE_PYOBJECT,)),
43 "config-found" : (gobject.SIGNAL_RUN_LAST,
44 gobject.TYPE_NONE,
45 (gobject.TYPE_STRING,)),
46 "generating-data" : (gobject.SIGNAL_RUN_LAST,
47 gobject.TYPE_NONE,
48 ()),
49 "data-generated" : (gobject.SIGNAL_RUN_LAST,
50 gobject.TYPE_NONE,
51 ()),
52 "error" : (gobject.SIGNAL_RUN_LAST,
53 gobject.TYPE_NONE,
54 (gobject.TYPE_STRING,)),
55 "build-complete" : (gobject.SIGNAL_RUN_LAST,
56 gobject.TYPE_NONE,
57 ()),
58 "reload-triggered" : (gobject.SIGNAL_RUN_LAST,
59 gobject.TYPE_NONE,
60 (gobject.TYPE_STRING,
61 gobject.TYPE_STRING)),
44 } 62 }
45 63
46 def __init__(self, taskmodel, server): 64 def __init__(self, taskmodel, server):
47 gobject.GObject.__init__(self) 65 gobject.GObject.__init__(self)
66 self.current_command = None
67 self.building = None
68 self.gplv3_excluded = False
69 self.build_toolchain = False
70 self.build_toolchain_headers = False
71 self.generating = False
72 self.build_queue = []
48 73
49 self.model = taskmodel 74 self.model = taskmodel
50 self.server = server 75 self.server = server
51 self.current_command = None
52 self.building = False
53 76
54 self.command_map = { 77 self.command_map = {
55 "findConfigFilesDistro" : ("findConfigFiles", "MACHINE", "findConfigFilesMachine"), 78 "findConfigFilePathLocal" : ("findConfigFilePath", ["hob.local.conf"], "findConfigFilePathHobLocal"),
56 "findConfigFilesMachine" : ("generateTargetsTree", "classes/image.bbclass", None), 79 "findConfigFilePathHobLocal" : ("findConfigFilePath", ["bblayers.conf"], "findConfigFilePathLayers"),
57 "generateTargetsTree" : (None, None, None), 80 "findConfigFilePathLayers" : ("findConfigFiles", ["DISTRO"], "findConfigFilesDistro"),
81 "findConfigFilesDistro" : ("findConfigFiles", ["MACHINE"], "findConfigFilesMachine"),
82 "findConfigFilesMachine" : ("findConfigFiles", ["MACHINE-SDK"], "findConfigFilesSdkMachine"),
83 "findConfigFilesSdkMachine" : ("findFilesMatchingInDir", ["rootfs_", "classes"], "findFilesMatchingPackage"),
84 "findFilesMatchingPackage" : ("generateTargetsTree", ["classes/image.bbclass"], None),
85 "generateTargetsTree" : (None, [], None),
58 } 86 }
59 87
60 def run_next_command(self): 88 def run_next_command(self):
61 # FIXME: this is ugly and I *will* replace it 89 # FIXME: this is ugly and I *will* replace it
62 if self.current_command: 90 if self.current_command:
91 if not self.generating:
92 self.emit("generating-data")
93 self.generating = True
63 next_cmd = self.command_map[self.current_command] 94 next_cmd = self.command_map[self.current_command]
64 command = next_cmd[0] 95 command = next_cmd[0]
65 argument = next_cmd[1] 96 argument = next_cmd[1]
66 self.current_command = next_cmd[2] 97 self.current_command = next_cmd[2]
67 if command == "generateTargetsTree": 98 args = [command]
68 self.emit("generating-data") 99 args.extend(argument)
69 self.server.runCommand([command, argument]) 100 self.server.runCommand(args)
70 101
71 def handle_event(self, event, running_build, pbar=None): 102 def handle_event(self, event, running_build, pbar):
72 if not event: 103 if not event:
73 return 104 return
74 105
@@ -77,9 +108,9 @@ class HobHandler(gobject.GObject):
77 running_build.handle_event(event) 108 running_build.handle_event(event)
78 elif isinstance(event, bb.event.TargetsTreeGenerated): 109 elif isinstance(event, bb.event.TargetsTreeGenerated):
79 self.emit("data-generated") 110 self.emit("data-generated")
111 self.generating = False
80 if event._model: 112 if event._model:
81 self.model.populate(event._model) 113 self.model.populate(event._model)
82
83 elif isinstance(event, bb.event.ConfigFilesFound): 114 elif isinstance(event, bb.event.ConfigFilesFound):
84 var = event._variable 115 var = event._variable
85 if var == "distro": 116 if var == "distro":
@@ -90,28 +121,44 @@ class HobHandler(gobject.GObject):
90 machines = event._values 121 machines = event._values
91 machines.sort() 122 machines.sort()
92 self.emit("machines-updated", machines) 123 self.emit("machines-updated", machines)
93 124 elif var == "machine-sdk":
125 sdk_machines = event._values
126 sdk_machines.sort()
127 self.emit("sdk-machines-updated", sdk_machines)
128 elif isinstance(event, bb.event.ConfigFilePathFound):
129 path = event._path
130 self.emit("config-found", path)
131 elif isinstance(event, bb.event.FilesMatchingFound):
132 # FIXME: hard coding, should at least be a variable shared between
133 # here and the caller
134 if event._pattern == "rootfs_":
135 formats = []
136 for match in event._matches:
137 classname, sep, cls = match.rpartition(".")
138 fs, sep, format = classname.rpartition("_")
139 formats.append(format)
140 formats.sort()
141 self.emit("package-formats-found", formats)
94 elif isinstance(event, bb.command.CommandCompleted): 142 elif isinstance(event, bb.command.CommandCompleted):
95 self.run_next_command() 143 self.run_next_command()
96 elif isinstance(event, bb.event.CacheLoadStarted) and pbar: 144 elif isinstance(event, bb.command.CommandFailed):
97 pbar.set_title("Loading cache") 145 self.emit("error", event.error)
146 elif isinstance(event, bb.event.CacheLoadStarted):
98 bb.ui.crumbs.hobeventhandler.progress_total = event.total 147 bb.ui.crumbs.hobeventhandler.progress_total = event.total
99 pbar.update(0, bb.ui.crumbs.hobeventhandler.progress_total) 148 pbar.set_text("Loading cache: %s/%s" % (0, bb.ui.crumbs.hobeventhandler.progress_total))
100 elif isinstance(event, bb.event.CacheLoadProgress) and pbar: 149 elif isinstance(event, bb.event.CacheLoadProgress):
101 pbar.update(event.current, bb.ui.crumbs.hobeventhandler.progress_total) 150 pbar.set_text("Loading cache: %s/%s" % (event.current, bb.ui.crumbs.hobeventhandler.progress_total))
102 elif isinstance(event, bb.event.CacheLoadCompleted) and pbar: 151 elif isinstance(event, bb.event.CacheLoadCompleted):
103 pbar.update(bb.ui.crumbs.hobeventhandler.progress_total, bb.ui.crumbs.hobeventhandler.progress_total) 152 pbar.set_text("Loading cache: %s/%s" % (bb.ui.crumbs.hobeventhandler.progress_total, bb.ui.crumbs.hobeventhandler.progress_total))
104 elif isinstance(event, bb.event.ParseStarted) and pbar: 153 elif isinstance(event, bb.event.ParseStarted):
105 if event.total == 0: 154 if event.total == 0:
106 return 155 return
107 pbar.set_title("Processing recipes")
108 bb.ui.crumbs.hobeventhandler.progress_total = event.total 156 bb.ui.crumbs.hobeventhandler.progress_total = event.total
109 pbar.update(0, bb.ui.crumbs.hobeventhandler.progress_total) 157 pbar.set_text("Processing recipes: %s/%s" % (0, bb.ui.crumbs.hobeventhandler.progress_total))
110 elif isinstance(event, bb.event.ParseProgress) and pbar: 158 elif isinstance(event, bb.event.ParseProgress):
111 pbar.update(event.current, bb.ui.crumbs.hobeventhandler.progress_total) 159 pbar.set_text("Processing recipes: %s/%s" % (event.current, bb.ui.crumbs.hobeventhandler.progress_total))
112 elif isinstance(event, bb.event.ParseCompleted) and pbar: 160 elif isinstance(event, bb.event.ParseCompleted):
113 pbar.hide() 161 pbar.set_fraction(1.0)
114
115 return 162 return
116 163
117 def event_handle_idle_func (self, eventHandler, running_build, pbar): 164 def event_handle_idle_func (self, eventHandler, running_build, pbar):
@@ -124,16 +171,95 @@ class HobHandler(gobject.GObject):
124 171
125 def set_machine(self, machine): 172 def set_machine(self, machine):
126 self.server.runCommand(["setVariable", "MACHINE", machine]) 173 self.server.runCommand(["setVariable", "MACHINE", machine])
127 self.current_command = "findConfigFilesMachine" 174
128 self.run_next_command() 175 def set_sdk_machine(self, sdk_machine):
176 self.server.runCommand(["setVariable", "SDKMACHINE", sdk_machine])
129 177
130 def set_distro(self, distro): 178 def set_distro(self, distro):
131 self.server.runCommand(["setVariable", "DISTRO", distro]) 179 self.server.runCommand(["setVariable", "DISTRO", distro])
132 180
133 def run_build(self, targets): 181 def set_package_format(self, format):
134 self.building = True 182 self.server.runCommand(["setVariable", "PACKAGE_CLASSES", "package_%s" % format])
183
184 def reload_data(self, config=None):
185 img = self.model.selected_image
186 selected_packages, _ = self.model.get_selected_packages()
187 self.emit("reload-triggered", img, " ".join(selected_packages))
188 self.server.runCommand(["reparseFiles"])
189 self.current_command = "findConfigFilePathLayers"
190 self.run_next_command()
191
192 def set_bbthreads(self, threads):
193 self.server.runCommand(["setVariable", "BB_NUMBER_THREADS", threads])
194
195 def set_pmake(self, threads):
196 pmake = "-j %s" % threads
197 self.server.runCommand(["setVariable", "BB_NUMBER_THREADS", pmake])
198
199 def run_build(self, tgts):
200 self.building = "image"
201 targets = []
202 targets.append(tgts)
203 if self.build_toolchain and self.build_toolchain_headers:
204 targets = ["meta-toolchain-sdk"] + targets
205 elif self.build_toolchain:
206 targets = ["meta-toolchain"] + targets
135 self.server.runCommand(["buildTargets", targets, "build"]) 207 self.server.runCommand(["buildTargets", targets, "build"])
136 208
137 def cancel_build(self): 209 def build_packages(self, pkgs):
138 # Note: this may not be the right way to stop an in-progress build 210 self.building = "packages"
139 self.server.runCommand(["stateStop"]) 211 if 'meta-toolchain' in self.build_queue:
212 self.build_queue.remove('meta-toolchain')
213 pkgs.extend('meta-toolchain')
214 self.server.runCommand(["buildTargets", pkgs, "build"])
215
216 def build_file(self, image):
217 self.building = "image"
218 self.server.runCommand(["buildFile", image, "build"])
219
220 def cancel_build(self, force=False):
221 if force:
222 # Force the cooker to stop as quickly as possible
223 self.server.runCommand(["stateStop"])
224 else:
225 # Wait for tasks to complete before shutting down, this helps
226 # leave the workdir in a usable state
227 self.server.runCommand(["stateShutdown"])
228
229 def toggle_gplv3(self, excluded):
230 if self.gplv3_excluded != excluded:
231 self.gplv3_excluded = excluded
232 if excluded:
233 self.server.runCommand(["setVariable", "INCOMPATIBLE_LICENSE", "GPLv3"])
234 else:
235 self.server.runCommand(["setVariable", "INCOMPATIBLE_LICENSE", ""])
236
237 def toggle_toolchain(self, enabled):
238 if self.build_toolchain != enabled:
239 self.build_toolchain = enabled
240
241 def toggle_toolchain_headers(self, enabled):
242 if self.build_toolchain_headers != enabled:
243 self.build_toolchain_headers = enabled
244
245 def queue_image_recipe_path(self, path):
246 self.build_queue.append(path)
247
248 def build_complete_cb(self, running_build):
249 if len(self.build_queue) > 0:
250 next = self.build_queue.pop(0)
251 if next.endswith('.bb'):
252 self.build_file(next)
253 self.building = 'image'
254 self.build_file(next)
255 else:
256 self.build_packages(next.split(" "))
257 else:
258 self.building = None
259 self.emit("build-complete")
260
261 def set_image_output_type(self, output_type):
262 self.server.runCommand(["setVariable", "IMAGE_FSTYPES", output_type])
263
264 def get_image_deploy_dir(self):
265 return self.server.runCommand(["getVariable", "DEPLOY_DIR_IMAGE"])
diff --git a/bitbake/lib/bb/ui/crumbs/hobprefs.py b/bitbake/lib/bb/ui/crumbs/hobprefs.py
new file mode 100644
index 0000000000..f186410720
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/hobprefs.py
@@ -0,0 +1,293 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011 Intel Corporation
5#
6# Authored by Joshua Lock <josh@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
22from bb.ui.crumbs.configurator import Configurator
23
24class HobPrefs(gtk.Dialog):
25 """
26 """
27 def empty_combo_text(self, combo_text):
28 model = combo_text.get_model()
29 if model:
30 model.clear()
31
32 def output_type_changed_cb(self, combo, handler):
33 ot = combo.get_active_text()
34 if ot != self.curr_output_type:
35 self.curr_output_type = ot
36 handler.set_image_output_type(ot)
37
38 def sdk_machine_combo_changed_cb(self, combo, handler):
39 sdk_mach = combo.get_active_text()
40 if sdk_mach != self.curr_sdk_mach:
41 self.curr_sdk_mach = sdk_mach
42 self.configurator.setLocalConfVar('SDKMACHINE', sdk_mach)
43 handler.set_sdk_machine(sdk_mach)
44
45 def update_sdk_machines(self, handler, sdk_machines):
46 active = 0
47 # disconnect the signal handler before updating the combo model
48 if self.sdk_machine_handler_id:
49 self.sdk_machine_combo.disconnect(self.sdk_machine_handler_id)
50 self.sdk_machine_handler_id = None
51
52 self.empty_combo_text(self.sdk_machine_combo)
53 for sdk_machine in sdk_machines:
54 self.sdk_machine_combo.append_text(sdk_machine)
55 if sdk_machine == self.curr_sdk_mach:
56 self.sdk_machine_combo.set_active(active)
57 active = active + 1
58
59 self.sdk_machine_handler_id = self.sdk_machine_combo.connect("changed", self.sdk_machine_combo_changed_cb, handler)
60
61 def distro_combo_changed_cb(self, combo, handler):
62 distro = combo.get_active_text()
63 if distro != self.curr_distro:
64 self.curr_distro = distro
65 self.configurator.setLocalConfVar('DISTRO', distro)
66 handler.set_distro(distro)
67 self.reload_required = True
68
69 def update_distros(self, handler, distros):
70 active = 0
71 # disconnect the signal handler before updating combo model
72 if self.distro_handler_id:
73 self.distro_combo.disconnect(self.distro_handler_id)
74 self.distro_handler_id = None
75
76 self.empty_combo_text(self.distro_combo)
77 for distro in distros:
78 self.distro_combo.append_text(distro)
79 if distro == self.curr_distro:
80 self.distro_combo.set_active(active)
81 active = active + 1
82
83 self.distro_handler_id = self.distro_combo.connect("changed", self.distro_combo_changed_cb, handler)
84
85 def package_format_combo_changed_cb(self, combo, handler):
86 package_format = combo.get_active_text()
87 if package_format != self.curr_package_format:
88 self.curr_package_format = package_format
89 self.configurator.setLocalConfVar('PACKAGE_CLASSES', 'package_%s' % package_format)
90 handler.set_package_format(package_format)
91
92 def update_package_formats(self, handler, formats):
93 active = 0
94 # disconnect the signal handler before updating the model
95 if self.package_handler_id:
96 self.package_combo.disconnect(self.package_handler_id)
97 self.package_handler_id = None
98
99 self.empty_combo_text(self.package_combo)
100 for format in formats:
101 self.package_combo.append_text(format)
102 if format == self.curr_package_format:
103 self.package_combo.set_active(active)
104 active = active + 1
105
106 self.package_handler_id = self.package_combo.connect("changed", self.package_format_combo_changed_cb, handler)
107
108 def include_gplv3_cb(self, toggle):
109 excluded = toggle.get_active()
110 self.handler.toggle_gplv3(excluded)
111 if excluded:
112 self.configurator.setLocalConfVar('INCOMPATIBLE_LICENSE', 'GPLv3')
113 else:
114 self.configurator.setLocalConfVar('INCOMPATIBLE_LICENSE', '')
115 self.reload_required = True
116
117 def change_bb_threads_cb(self, spinner):
118 val = spinner.get_value_as_int()
119 self.handler.set_bbthreads(val)
120 self.configurator.setLocalConfVar('BB_NUMBER_THREADS', val)
121
122 def change_make_threads_cb(self, spinner):
123 val = spinner.get_value_as_int()
124 self.handler.set_pmake(val)
125 self.configurator.setLocalConfVar('PARALLEL_MAKE', "-j %s" % val)
126
127 def toggle_toolchain_cb(self, check):
128 enabled = check.get_active()
129 self.handler.toggle_toolchain(enabled)
130
131 def toggle_headers_cb(self, check):
132 enabled = check.get_active()
133 self.handler.toggle_toolchain_headers(enabled)
134
135 def set_parent_window(self, parent):
136 self.set_transient_for(parent)
137
138 def write_changes(self):
139 self.configurator.writeLocalConf()
140
141 def prefs_response_cb(self, dialog, response):
142 if self.reload_required:
143 glib.idle_add(self.handler.reload_data)
144
145 def __init__(self, configurator, handler, curr_sdk_mach, curr_distro, pclass,
146 cpu_cnt, pmake, bbthread, image_types):
147 """
148 """
149 gtk.Dialog.__init__(self, "Preferences", None,
150 gtk.DIALOG_DESTROY_WITH_PARENT,
151 (gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
152
153 self.set_border_width(6)
154 self.vbox.set_property("spacing", 12)
155 self.action_area.set_property("spacing", 12)
156 self.action_area.set_property("border-width", 6)
157
158 self.handler = handler
159 self.configurator = configurator
160
161 self.curr_sdk_mach = curr_sdk_mach
162 self.curr_distro = curr_distro
163 self.curr_package_format = pclass
164 self.curr_output_type = None
165 self.cpu_cnt = cpu_cnt
166 self.pmake = pmake
167 self.bbthread = bbthread
168 self.reload_required = False
169 self.distro_handler_id = None
170 self.sdk_machine_handler_id = None
171 self.package_handler_id = None
172
173 left = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
174 right = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
175
176 label = gtk.Label()
177 label.set_markup("<b>Policy</b>")
178 label.show()
179 frame = gtk.Frame()
180 frame.set_label_widget(label)
181 frame.set_shadow_type(gtk.SHADOW_NONE)
182 frame.show()
183 self.vbox.pack_start(frame)
184 pbox = gtk.VBox(False, 12)
185 pbox.show()
186 frame.add(pbox)
187 hbox = gtk.HBox(False, 12)
188 hbox.show()
189 pbox.pack_start(hbox, expand=False, fill=False, padding=6)
190 # Distro selector
191 label = gtk.Label("Distribution:")
192 label.show()
193 hbox.pack_start(label, expand=False, fill=False, padding=6)
194 self.distro_combo = gtk.combo_box_new_text()
195 self.distro_combo.set_tooltip_text("Select the Yocto distribution you would like to use")
196 self.distro_combo.show()
197 hbox.pack_start(self.distro_combo, expand=False, fill=False, padding=6)
198 # Exclude GPLv3
199 check = gtk.CheckButton("Exclude GPLv3 packages")
200 check.set_tooltip_text("Check this box to prevent GPLv3 packages from being included in your image")
201 check.show()
202 check.connect("toggled", self.include_gplv3_cb)
203 hbox.pack_start(check, expand=False, fill=False, padding=6)
204 hbox = gtk.HBox(False, 12)
205 hbox.show()
206 pbox.pack_start(hbox, expand=False, fill=False, padding=6)
207 # Package format selector
208 label = gtk.Label("Package format:")
209 label.show()
210 hbox.pack_start(label, expand=False, fill=False, padding=6)
211 self.package_combo = gtk.combo_box_new_text()
212 self.package_combo.set_tooltip_text("Select the package format you would like to use in your image")
213 self.package_combo.show()
214 hbox.pack_start(self.package_combo, expand=False, fill=False, padding=6)
215 # Image output type selector
216 label = gtk.Label("Image output type:")
217 label.show()
218 hbox.pack_start(label, expand=False, fill=False, padding=6)
219 output_combo = gtk.combo_box_new_text()
220 if image_types:
221 for it in image_types.split(" "):
222 output_combo.append_text(it)
223 output_combo.connect("changed", self.output_type_changed_cb, handler)
224 else:
225 output_combo.set_sensitive(False)
226 output_combo.show()
227 hbox.pack_start(output_combo)
228 # BitBake
229 label = gtk.Label()
230 label.set_markup("<b>BitBake</b>")
231 label.show()
232 frame = gtk.Frame()
233 frame.set_label_widget(label)
234 frame.set_shadow_type(gtk.SHADOW_NONE)
235 frame.show()
236 self.vbox.pack_start(frame)
237 pbox = gtk.VBox(False, 12)
238 pbox.show()
239 frame.add(pbox)
240 hbox = gtk.HBox(False, 12)
241 hbox.show()
242 pbox.pack_start(hbox, expand=False, fill=False, padding=6)
243 label = gtk.Label("BitBake threads:")
244 label.show()
245 spin_max = 9 #self.cpu_cnt * 3
246 hbox.pack_start(label, expand=False, fill=False, padding=6)
247 bbadj = gtk.Adjustment(value=self.bbthread, lower=1, upper=spin_max, step_incr=1)
248 bbspinner = gtk.SpinButton(adjustment=bbadj, climb_rate=1, digits=0)
249 bbspinner.show()
250 bbspinner.connect("value-changed", self.change_bb_threads_cb)
251 hbox.pack_start(bbspinner, expand=False, fill=False, padding=6)
252 label = gtk.Label("Make threads:")
253 label.show()
254 hbox.pack_start(label, expand=False, fill=False, padding=6)
255 madj = gtk.Adjustment(value=self.pmake, lower=1, upper=spin_max, step_incr=1)
256 makespinner = gtk.SpinButton(adjustment=madj, climb_rate=1, digits=0)
257 makespinner.connect("value-changed", self.change_make_threads_cb)
258 makespinner.show()
259 hbox.pack_start(makespinner, expand=False, fill=False, padding=6)
260 # Toolchain
261 label = gtk.Label()
262 label.set_markup("<b>External Toolchain</b>")
263 label.show()
264 frame = gtk.Frame()
265 frame.set_label_widget(label)
266 frame.set_shadow_type(gtk.SHADOW_NONE)
267 frame.show()
268 self.vbox.pack_start(frame)
269 pbox = gtk.VBox(False, 12)
270 pbox.show()
271 frame.add(pbox)
272 hbox = gtk.HBox(False, 12)
273 hbox.show()
274 pbox.pack_start(hbox, expand=False, fill=False, padding=6)
275 toolcheck = gtk.CheckButton("Build external development toolchain with image")
276 toolcheck.show()
277 toolcheck.connect("toggled", self.toggle_toolchain_cb)
278 hbox.pack_start(toolcheck, expand=False, fill=False, padding=6)
279 hbox = gtk.HBox(False, 12)
280 hbox.show()
281 pbox.pack_start(hbox, expand=False, fill=False, padding=6)
282 label = gtk.Label("Toolchain host:")
283 label.show()
284 hbox.pack_start(label, expand=False, fill=False, padding=6)
285 self.sdk_machine_combo = gtk.combo_box_new_text()
286 self.sdk_machine_combo.set_tooltip_text("Select the host architecture of the external machine")
287 self.sdk_machine_combo.show()
288 hbox.pack_start(self.sdk_machine_combo, expand=False, fill=False, padding=6)
289 headerscheck = gtk.CheckButton("Include development headers with toolchain")
290 headerscheck.show()
291 headerscheck.connect("toggled", self.toggle_headers_cb)
292 hbox.pack_start(headerscheck, expand=False, fill=False, padding=6)
293 self.connect("response", self.prefs_response_cb)
diff --git a/bitbake/lib/bb/ui/crumbs/layereditor.py b/bitbake/lib/bb/ui/crumbs/layereditor.py
new file mode 100644
index 0000000000..76a2eb536f
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/layereditor.py
@@ -0,0 +1,136 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011 Intel Corporation
5#
6# Authored by Joshua Lock <josh@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
23from bb.ui.crumbs.configurator import Configurator
24
25class LayerEditor(gtk.Dialog):
26 """
27 Gtk+ Widget for enabling and disabling layers.
28 Layers are added through using an open dialog to find the layer.conf
29 Disabled layers are deleted from conf/bblayers.conf
30 """
31 def __init__(self, configurator, parent=None):
32 gtk.Dialog.__init__(self, "Layers", None,
33 gtk.DIALOG_DESTROY_WITH_PARENT,
34 (gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
35
36 # We want to show a little more of the treeview in the default,
37 # emptier, case
38 self.set_size_request(-1, 300)
39 self.set_border_width(6)
40 self.vbox.set_property("spacing", 0)
41 self.action_area.set_property("border-width", 6)
42
43 self.configurator = configurator
44 self.newly_added = {}
45
46 # Label to inform users that meta is enabled but that you can't
47 # disable it as it'd be a *bad* idea
48 msg = "As the core of the build system the <i>meta</i> layer must always be included and therefore can't be viewed or edited here."
49 lbl = gtk.Label()
50 lbl.show()
51 lbl.set_use_markup(True)
52 lbl.set_markup(msg)
53 lbl.set_line_wrap(True)
54 lbl.set_justify(gtk.JUSTIFY_FILL)
55 self.vbox.pack_start(lbl, expand=False, fill=False, padding=6)
56
57 # Create a treeview in which to list layers
58 # ListStore of Name, Path, Enabled
59 self.layer_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
60 self.tv = gtk.TreeView(self.layer_store)
61 self.tv.set_headers_visible(True)
62
63 col0 = gtk.TreeViewColumn('Name')
64 self.tv.append_column(col0)
65 col1 = gtk.TreeViewColumn('Path')
66 self.tv.append_column(col1)
67 col2 = gtk.TreeViewColumn('Enabled')
68 self.tv.append_column(col2)
69
70 cell0 = gtk.CellRendererText()
71 col0.pack_start(cell0, True)
72 col0.set_attributes(cell0, text=0)
73 cell1 = gtk.CellRendererText()
74 col1.pack_start(cell1, True)
75 col1.set_attributes(cell1, text=1)
76 cell2 = gtk.CellRendererToggle()
77 cell2.connect("toggled", self._toggle_layer_cb)
78 col2.pack_start(cell2, True)
79 col2.set_attributes(cell2, active=2)
80
81 self.tv.show()
82 self.vbox.pack_start(self.tv, expand=True, fill=True, padding=0)
83
84 tb = gtk.Toolbar()
85 tb.set_icon_size(gtk.ICON_SIZE_SMALL_TOOLBAR)
86 tb.set_style(gtk.TOOLBAR_BOTH)
87 tb.set_tooltips(True)
88 tb.show()
89 icon = gtk.Image()
90 icon.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_SMALL_TOOLBAR)
91 icon.show()
92 tb.insert_item("Add Layer", "Add new layer", None, icon,
93 self._find_layer_cb, None, -1)
94 self.vbox.pack_start(tb, expand=False, fill=False, padding=0)
95
96 def set_parent_window(self, parent):
97 self.set_transient_for(parent)
98
99 def load_current_layers(self, data):
100 for layer, path in self.configurator.enabled_layers.items():
101 if layer != 'meta':
102 self.layer_store.append([layer, path, True])
103
104 def save_current_layers(self):
105 self.configurator.writeLayerConf()
106
107 def _toggle_layer_cb(self, cell, path):
108 name = self.layer_store[path][0]
109 toggle = not self.layer_store[path][2]
110 if toggle:
111 self.configurator.addLayer(name, path)
112 else:
113 self.configurator.disableLayer(name)
114 self.layer_store[path][2] = toggle
115
116 def _find_layer_cb(self, button):
117 self.find_layer(self)
118
119 def find_layer(self, parent):
120 dialog = gtk.FileChooserDialog("Add new layer", parent,
121 gtk.FILE_CHOOSER_ACTION_OPEN,
122 (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
123 gtk.STOCK_OPEN, gtk.RESPONSE_YES))
124 label = gtk.Label("Select the layer.conf of the layer you wish to add")
125 label.show()
126 dialog.set_extra_widget(label)
127 response = dialog.run()
128 path = dialog.get_filename()
129 dialog.destroy()
130
131 if response == gtk.RESPONSE_YES:
132 # FIXME: verify we've actually got a layer conf?
133 if path.endswith(".conf"):
134 name, layerpath = self.configurator.addLayerConf(path)
135 self.newly_added[name] = layerpath
136 self.layer_store.append([name, layerpath, True])
diff --git a/bitbake/lib/bb/ui/crumbs/runningbuild.py b/bitbake/lib/bb/ui/crumbs/runningbuild.py
index 70fd57effc..bdab34040c 100644
--- a/bitbake/lib/bb/ui/crumbs/runningbuild.py
+++ b/bitbake/lib/bb/ui/crumbs/runningbuild.py
@@ -47,12 +47,18 @@ class RunningBuildModel (gtk.TreeStore):
47 47
48class RunningBuild (gobject.GObject): 48class RunningBuild (gobject.GObject):
49 __gsignals__ = { 49 __gsignals__ = {
50 'build-started' : (gobject.SIGNAL_RUN_LAST,
51 gobject.TYPE_NONE,
52 ()),
50 'build-succeeded' : (gobject.SIGNAL_RUN_LAST, 53 'build-succeeded' : (gobject.SIGNAL_RUN_LAST,
51 gobject.TYPE_NONE, 54 gobject.TYPE_NONE,
52 ()), 55 ()),
53 'build-failed' : (gobject.SIGNAL_RUN_LAST, 56 'build-failed' : (gobject.SIGNAL_RUN_LAST,
54 gobject.TYPE_NONE, 57 gobject.TYPE_NONE,
55 ()) 58 ()),
59 'build-complete' : (gobject.SIGNAL_RUN_LAST,
60 gobject.TYPE_NONE,
61 ())
56 } 62 }
57 pids_to_task = {} 63 pids_to_task = {}
58 tasks_to_iter = {} 64 tasks_to_iter = {}
@@ -201,6 +207,7 @@ class RunningBuild (gobject.GObject):
201 207
202 elif isinstance(event, bb.event.BuildStarted): 208 elif isinstance(event, bb.event.BuildStarted):
203 209
210 self.emit("build-started")
204 self.model.prepend(None, (None, 211 self.model.prepend(None, (None,
205 None, 212 None,
206 None, 213 None,
@@ -218,6 +225,9 @@ class RunningBuild (gobject.GObject):
218 Colors.OK, 225 Colors.OK,
219 0)) 226 0))
220 227
228 # Emit a generic "build-complete" signal for things wishing to
229 # handle when the build is finished
230 self.emit("build-complete")
221 # Emit the appropriate signal depending on the number of failures 231 # Emit the appropriate signal depending on the number of failures
222 if (failures >= 1): 232 if (failures >= 1):
223 self.emit ("build-failed") 233 self.emit ("build-failed")
diff --git a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py
index a83a176ddc..d9829861bb 100644
--- a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py
+++ b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py
@@ -20,6 +20,58 @@
20 20
21import gtk 21import gtk
22import gobject 22import gobject
23import re
24
25class BuildRep(gobject.GObject):
26
27 def __init__(self, userpkgs, allpkgs, base_image=None):
28 gobject.GObject.__init__(self)
29 self.base_image = base_image
30 self.allpkgs = allpkgs
31 self.userpkgs = userpkgs
32
33 def loadRecipe(self, pathname):
34 contents = []
35 packages = ""
36 base_image = ""
37
38 with open(pathname, 'r') as f:
39 contents = f.readlines()
40
41 pkg_pattern = "^\s*(IMAGE_INSTALL)\s*([+=.?]+)\s*(\"\S*\")"
42 img_pattern = "^\s*(require)\s+(\S+.bb)"
43
44 for line in contents:
45 matchpkg = re.search(pkg_pattern, line)
46 matchimg = re.search(img_pattern, line)
47 if matchpkg:
48 packages = packages + matchpkg.group(3).strip('"')
49 if matchimg:
50 base_image = os.path.basename(matchimg.group(2)).split(".")[0]
51
52 self.base_image = base_image
53 self.userpkgs = packages
54
55 def writeRecipe(self, writepath, model):
56 # FIXME: Need a better way to determine meta_path...
57 template = """
58# Recipe generated by the HOB
59
60require %s.bb
61
62IMAGE_INSTALL += "%s"
63"""
64 meta_path = model.find_image_path(self.base_image)
65
66 recipe = template % (meta_path, self.userpkgs)
67
68 if os.path.exists(writepath):
69 os.rename(writepath, "%s~" % writepath)
70
71 with open(writepath, 'w') as r:
72 r.write(recipe)
73
74 return writepath
23 75
24class TaskListModel(gtk.ListStore): 76class TaskListModel(gtk.ListStore):
25 """ 77 """
@@ -28,12 +80,18 @@ class TaskListModel(gtk.ListStore):
28 providing convenience functions to access gtk.TreeModel subclasses which 80 providing convenience functions to access gtk.TreeModel subclasses which
29 provide filtered views of the data. 81 provide filtered views of the data.
30 """ 82 """
31 (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC) = range(8) 83 (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC, COL_IMG, COL_PATH) = range(10)
32 84
33 __gsignals__ = { 85 __gsignals__ = {
34 "tasklist-populated" : (gobject.SIGNAL_RUN_LAST, 86 "tasklist-populated" : (gobject.SIGNAL_RUN_LAST,
35 gobject.TYPE_NONE, 87 gobject.TYPE_NONE,
36 ()) 88 ()),
89 "contents-changed" : (gobject.SIGNAL_RUN_LAST,
90 gobject.TYPE_NONE,
91 (gobject.TYPE_INT,)),
92 "image-changed" : (gobject.SIGNAL_RUN_LAST,
93 gobject.TYPE_NONE,
94 (gobject.TYPE_STRING,)),
37 } 95 }
38 96
39 """ 97 """
@@ -43,6 +101,7 @@ class TaskListModel(gtk.ListStore):
43 self.tasks = None 101 self.tasks = None
44 self.packages = None 102 self.packages = None
45 self.images = None 103 self.images = None
104 self.selected_image = None
46 105
47 gtk.ListStore.__init__ (self, 106 gtk.ListStore.__init__ (self,
48 gobject.TYPE_STRING, 107 gobject.TYPE_STRING,
@@ -52,7 +111,22 @@ class TaskListModel(gtk.ListStore):
52 gobject.TYPE_STRING, 111 gobject.TYPE_STRING,
53 gobject.TYPE_STRING, 112 gobject.TYPE_STRING,
54 gobject.TYPE_STRING, 113 gobject.TYPE_STRING,
55 gobject.TYPE_BOOLEAN) 114 gobject.TYPE_BOOLEAN,
115 gobject.TYPE_BOOLEAN,
116 gobject.TYPE_STRING)
117
118 def contents_changed_cb(self, tree_model, path, it=None):
119 pkg_cnt = self.contents.iter_n_children(None)
120 self.emit("contents-changed", pkg_cnt)
121
122 def contents_model_filter(self, model, it):
123 if not model.get_value(it, self.COL_INC) or model.get_value(it, self.COL_TYPE) == 'image':
124 return False
125 name = model.get_value(it, self.COL_NAME)
126 if name.endswith('-native') or name.endswith('-cross'):
127 return False
128 else:
129 return True
56 130
57 """ 131 """
58 Create, if required, and return a filtered gtk.TreeModel 132 Create, if required, and return a filtered gtk.TreeModel
@@ -62,7 +136,9 @@ class TaskListModel(gtk.ListStore):
62 def contents_model(self): 136 def contents_model(self):
63 if not self.contents: 137 if not self.contents:
64 self.contents = self.filter_new() 138 self.contents = self.filter_new()
65 self.contents.set_visible_column(self.COL_INC) 139 self.contents.set_visible_func(self.contents_model_filter)
140 self.contents.connect("row-inserted", self.contents_changed_cb)
141 self.contents.connect("row-deleted", self.contents_changed_cb)
66 return self.contents 142 return self.contents
67 143
68 """ 144 """
@@ -107,10 +183,10 @@ class TaskListModel(gtk.ListStore):
107 Helper function to determine whether an item is a package 183 Helper function to determine whether an item is a package
108 """ 184 """
109 def package_model_filter(self, model, it): 185 def package_model_filter(self, model, it):
110 if model.get_value(it, self.COL_TYPE) == 'package': 186 if model.get_value(it, self.COL_TYPE) != 'package':
111 return True
112 else:
113 return False 187 return False
188 else:
189 return True
114 190
115 """ 191 """
116 Create, if required, and return a filtered gtk.TreeModel 192 Create, if required, and return a filtered gtk.TreeModel
@@ -129,33 +205,78 @@ class TaskListModel(gtk.ListStore):
129 to notify any listeners that the model is ready 205 to notify any listeners that the model is ready
130 """ 206 """
131 def populate(self, event_model): 207 def populate(self, event_model):
208 # First clear the model, in case repopulating
209 self.clear()
132 for item in event_model["pn"]: 210 for item in event_model["pn"]:
133 atype = 'package' 211 atype = 'package'
134 name = item 212 name = item
135 summary = event_model["pn"][item]["summary"] 213 summary = event_model["pn"][item]["summary"]
136 license = event_model["pn"][item]["license"] 214 lic = event_model["pn"][item]["license"]
137 group = event_model["pn"][item]["section"] 215 group = event_model["pn"][item]["section"]
138 216 filename = event_model["pn"][item]["filename"]
139 depends = event_model["depends"].get(item, "") 217 depends = event_model["depends"].get(item, "")
140 rdepends = event_model["rdepends-pn"].get(item, "") 218 rdepends = event_model["rdepends-pn"].get(item, "")
141 depends = depends + rdepends 219 if rdepends:
220 for rdep in rdepends:
221 if event_model["packages"].get(rdep, ""):
222 pn = event_model["packages"][rdep].get("pn", "")
223 if pn:
224 depends.append(pn)
225
142 self.squish(depends) 226 self.squish(depends)
143 deps = " ".join(depends) 227 deps = " ".join(depends)
144 228
145 if name.count('task-') > 0: 229 if name.count('task-') > 0:
146 atype = 'task' 230 atype = 'task'
147 elif name.count('-image-') > 0: 231 elif name.count('-image-') > 0:
148 atype = 'image' 232 atype = 'image'
149 233
150 self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary, 234 self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary,
151 self.COL_LIC, license, self.COL_GROUP, group, 235 self.COL_LIC, lic, self.COL_GROUP, group,
152 self.COL_DEPS, deps, self.COL_BINB, "", 236 self.COL_DEPS, deps, self.COL_BINB, "",
153 self.COL_TYPE, atype, self.COL_INC, False) 237 self.COL_TYPE, atype, self.COL_INC, False,
154 238 self.COL_IMG, False, self.COL_PATH, filename)
239
155 self.emit("tasklist-populated") 240 self.emit("tasklist-populated")
156 241
157 """ 242 """
158 squish lst so that it doesn't contain any duplicates 243 Load a BuildRep into the model
244 """
245 def load_image_rep(self, rep):
246 # Unset everything
247 it = self.get_iter_first()
248 while it:
249 path = self.get_path(it)
250 self[path][self.COL_INC] = False
251 self[path][self.COL_IMG] = False
252 it = self.iter_next(it)
253
254 # Iterate the images and disable them all
255 it = self.images.get_iter_first()
256 while it:
257 path = self.images.convert_path_to_child_path(self.images.get_path(it))
258 name = self[path][self.COL_NAME]
259 if name == rep.base_image:
260 self.include_item(path, image_contents=True)
261 else:
262 self[path][self.COL_INC] = False
263 it = self.images.iter_next(it)
264
265 # Mark all of the additional packages for inclusion
266 packages = rep.packages.split(" ")
267 it = self.get_iter_first()
268 while it:
269 path = self.get_path(it)
270 name = self[path][self.COL_NAME]
271 if name in packages:
272 self.include_item(path)
273 packages.remove(name)
274 it = self.iter_next(it)
275
276 self.emit("image-changed", rep.base_image)
277
278 """
279 squish lst so that it doesn't contain any duplicate entries
159 """ 280 """
160 def squish(self, lst): 281 def squish(self, lst):
161 seen = {} 282 seen = {}
@@ -173,56 +294,59 @@ class TaskListModel(gtk.ListStore):
173 self[path][self.COL_INC] = False 294 self[path][self.COL_INC] = False
174 295
175 """ 296 """
297 recursively called to mark the item at opath and any package which
298 depends on it for removal
176 """ 299 """
177 def mark(self, path): 300 def mark(self, opath):
178 name = self[path][self.COL_NAME]
179 it = self.get_iter_first()
180 removals = [] 301 removals = []
181 #print("Removing %s" % name) 302 it = self.get_iter_first()
303 name = self[opath][self.COL_NAME]
182 304
183 self.remove_item_path(path) 305 self.remove_item_path(opath)
184 306
185 # Remove all dependent packages, update binb 307 # Remove all dependent packages, update binb
186 while it: 308 while it:
187 path = self.get_path(it) 309 path = self.get_path(it)
188 # FIXME: need to ensure partial name matching doesn't happen, regexp? 310 inc = self[path][self.COL_INC]
189 if self[path][self.COL_INC] and self[path][self.COL_DEPS].count(name): 311 deps = self[path][self.COL_DEPS]
190 #print("%s depended on %s, marking for removal" % (self[path][self.COL_NAME], name)) 312 binb = self[path][self.COL_BINB]
313
314 # FIXME: need to ensure partial name matching doesn't happen
315 if inc and deps.count(name):
191 # found a dependency, remove it 316 # found a dependency, remove it
192 self.mark(path) 317 self.mark(path)
193 if self[path][self.COL_INC] and self[path][self.COL_BINB].count(name): 318 if inc and binb.count(name):
194 binb = self.find_alt_dependency(self[path][self.COL_NAME]) 319 bib = self.find_alt_dependency(name)
195 #print("%s was brought in by %s, binb set to %s" % (self[path][self.COL_NAME], name, binb)) 320 self[path][self.COL_BINB] = bib
196 self[path][self.COL_BINB] = binb 321
197 it = self.iter_next(it) 322 it = self.iter_next(it)
198 323
199 """ 324 """
325 Remove items from contents if the have an empty COL_BINB (brought in by)
326 caused by all packages they are a dependency of being removed.
327 If the item isn't a package we leave it included.
200 """ 328 """
201 def sweep_up(self): 329 def sweep_up(self):
330 model = self.contents
202 removals = [] 331 removals = []
203 it = self.get_iter_first() 332 it = self.contents.get_iter_first()
204 333
205 while it: 334 while it:
206 path = self.get_path(it) 335 binb = model.get_value(it, self.COL_BINB)
207 binb = self[path][self.COL_BINB] 336 itype = model.get_value(it, self.COL_TYPE)
208 if binb == "" or binb is None: 337
209 #print("Sweeping up %s" % self[path][self.COL_NAME]) 338 if itype == 'package' and not binb:
210 if not path in removals: 339 opath = model.convert_path_to_child_path(model.get_path(it))
211 removals.extend(path) 340 if not opath in removals:
212 it = self.iter_next(it) 341 removals.extend(opath)
342
343 it = model.iter_next(it)
213 344
214 while removals: 345 while removals:
215 path = removals.pop() 346 path = removals.pop()
216 self.mark(path) 347 self.mark(path)
217 348
218 """ 349 """
219 Remove an item from the contents
220 """
221 def remove_item(self, path):
222 self.mark(path)
223 self.sweep_up()
224
225 """
226 Find the name of an item in the image contents which depends on the item 350 Find the name of an item in the image contents which depends on the item
227 at contents_path returns either an item name (str) or None 351 at contents_path returns either an item name (str) or None
228 NOTE: 352 NOTE:
@@ -238,18 +362,11 @@ class TaskListModel(gtk.ListStore):
238 inc = self[path][self.COL_INC] 362 inc = self[path][self.COL_INC]
239 if itname != name and inc and deps.count(name) > 0: 363 if itname != name and inc and deps.count(name) > 0:
240 # if this item depends on the item, return this items name 364 # if this item depends on the item, return this items name
241 #print("%s depends on %s" % (itname, name))
242 return itname 365 return itname
243 it = self.iter_next(it) 366 it = self.iter_next(it)
244 return "" 367 return ""
245 368
246 """ 369 """
247 Convert a path in self to a path in the filtered contents model
248 """
249 def contents_path_for_path(self, path):
250 return self.contents.convert_child_path_to_path(path)
251
252 """
253 Check the self.contents gtk.TreeModel for an item 370 Check the self.contents gtk.TreeModel for an item
254 where COL_NAME matches item_name 371 where COL_NAME matches item_name
255 Returns True if a match is found, False otherwise 372 Returns True if a match is found, False otherwise
@@ -266,25 +383,30 @@ class TaskListModel(gtk.ListStore):
266 """ 383 """
267 Add this item, and any of its dependencies, to the image contents 384 Add this item, and any of its dependencies, to the image contents
268 """ 385 """
269 def include_item(self, item_path, binb=""): 386 def include_item(self, item_path, binb="", image_contents=False):
270 name = self[item_path][self.COL_NAME] 387 name = self[item_path][self.COL_NAME]
271 deps = self[item_path][self.COL_DEPS] 388 deps = self[item_path][self.COL_DEPS]
272 cur_inc = self[item_path][self.COL_INC] 389 cur_inc = self[item_path][self.COL_INC]
273 #print("Adding %s for %s dependency" % (name, binb))
274 if not cur_inc: 390 if not cur_inc:
275 self[item_path][self.COL_INC] = True 391 self[item_path][self.COL_INC] = True
276 self[item_path][self.COL_BINB] = binb 392 self[item_path][self.COL_BINB] = binb
393 # We want to do some magic with things which are brought in by the base
394 # image so tag them as so
395 if image_contents:
396 self[item_path][self.COL_IMG] = True
397 if self[item_path][self.COL_TYPE] == 'image':
398 self.selected_image = name
399
277 if deps: 400 if deps:
278 #print("Dependencies of %s are %s" % (name, deps))
279 # add all of the deps and set their binb to this item 401 # add all of the deps and set their binb to this item
280 for dep in deps.split(" "): 402 for dep in deps.split(" "):
281 # FIXME: this skipping virtuals can't be right? Unless we choose only to show target
282 # packages? In which case we should handle this server side...
283 # If the contents model doesn't already contain dep, add it 403 # If the contents model doesn't already contain dep, add it
284 if not dep.startswith("virtual") and not self.contents_includes_name(dep): 404 # We only care to show things which will end up in the
405 # resultant image, so filter cross and native recipes
406 if not self.contents_includes_name(dep) and not dep.endswith("-native") and not dep.endswith("-cross"):
285 path = self.find_path_for_item(dep) 407 path = self.find_path_for_item(dep)
286 if path: 408 if path:
287 self.include_item(path, name) 409 self.include_item(path, name, image_contents)
288 else: 410 else:
289 pass 411 pass
290 412
@@ -317,30 +439,78 @@ class TaskListModel(gtk.ListStore):
317 it = self.contents.get_iter_first() 439 it = self.contents.get_iter_first()
318 440
319 """ 441 """
320 Returns True if one of the selected tasks is an image, False otherwise 442 Returns two lists. One of user selected packages and the other containing
443 all selected packages
321 """ 444 """
322 def targets_contains_image(self): 445 def get_selected_packages(self):
323 it = self.images.get_iter_first() 446 allpkgs = []
447 userpkgs = []
448
449 it = self.contents.get_iter_first()
324 while it: 450 while it:
325 path = self.images.get_path(it) 451 sel = self.contents.get_value(it, self.COL_BINB) == "User Selected"
326 inc = self.images[path][self.COL_INC] 452 name = self.contents.get_value(it, self.COL_NAME)
327 if inc: 453 allpkgs.append(name)
328 return True 454 if sel:
329 it = self.images.iter_next(it) 455 userpkgs.append(name)
330 return False 456 it = self.contents.iter_next(it)
457 return userpkgs, allpkgs
331 458
332 """ 459 def get_build_rep(self):
333 Return a list of all selected items which are not -native or -cross 460 userpkgs, allpkgs = self.get_selected_packages()
334 """ 461 image = self.selected_image
335 def get_targets(self): 462
336 tasks = [] 463 return BuildRep(" ".join(userpkgs), " ".join(allpkgs), image)
337 464
465 def find_reverse_depends(self, pn):
466 revdeps = []
338 it = self.contents.get_iter_first() 467 it = self.contents.get_iter_first()
468
339 while it: 469 while it:
340 path = self.contents.get_path(it) 470 if self.contents.get_value(it, self.COL_DEPS).count(pn) != 0:
341 name = self.contents[path][self.COL_NAME] 471 revdeps.append(self.contents.get_value(it, self.COL_NAME))
342 stype = self.contents[path][self.COL_TYPE]
343 if not name.count('-native') and not name.count('-cross'):
344 tasks.append(name)
345 it = self.contents.iter_next(it) 472 it = self.contents.iter_next(it)
346 return tasks 473
474 if pn in revdeps:
475 revdeps.remove(pn)
476 return revdeps
477
478 def set_selected_image(self, img):
479 self.selected_image = img
480 path = self.find_path_for_item(img)
481 self.include_item(item_path=path,
482 binb="User Selected",
483 image_contents=True)
484
485 self.emit("image-changed", self.selected_image)
486
487 def set_selected_packages(self, pkglist):
488 selected = pkglist
489 it = self.get_iter_first()
490
491 while it:
492 name = self.get_value(it, self.COL_NAME)
493 if name in pkglist:
494 pkglist.remove(name)
495 path = self.get_path(it)
496 self.include_item(item_path=path,
497 binb="User Selected")
498 if len(pkglist) == 0:
499 return
500 it = self.iter_next(it)
501
502 def find_image_path(self, image):
503 it = self.images.get_iter_first()
504
505 while it:
506 image_name = self.images.get_value(it, self.COL_NAME)
507 if image_name == image:
508 path = self.images.get_value(it, self.COL_PATH)
509 meta_pattern = "(\S*)/(meta*/)(\S*)"
510 meta_match = re.search(meta_pattern, path)
511 if meta_match:
512 _, lyr, bbrel = path.partition(meta_match.group(2))
513 if bbrel:
514 path = bbrel
515 return path
516 it = self.images.iter_next(it)
diff --git a/bitbake/lib/bb/ui/hob.py b/bitbake/lib/bb/ui/hob.py
index 175e5bdf6c..fca41e44dc 100644
--- a/bitbake/lib/bb/ui/hob.py
+++ b/bitbake/lib/bb/ui/hob.py
@@ -18,12 +18,16 @@
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 glib
21import gobject 22import gobject
22import gtk 23import gtk
23from bb.ui.crumbs.progress import ProgressBar 24from bb.ui.crumbs.tasklistmodel import TaskListModel, BuildRep
24from bb.ui.crumbs.tasklistmodel import TaskListModel
25from bb.ui.crumbs.hobeventhandler import HobHandler 25from bb.ui.crumbs.hobeventhandler import HobHandler
26from bb.ui.crumbs.configurator import Configurator
27from bb.ui.crumbs.hobprefs import HobPrefs
28from bb.ui.crumbs.layereditor import LayerEditor
26from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild 29from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
30from bb.ui.crumbs.hig import CrumbsDialog
27import xmlrpclib 31import xmlrpclib
28import logging 32import logging
29import Queue 33import Queue
@@ -32,226 +36,459 @@ extraCaches = ['bb.cache_extra:HobRecipeInfo']
32 36
33class MainWindow (gtk.Window): 37class MainWindow (gtk.Window):
34 38
35 def __init__(self, taskmodel, handler, curr_mach=None, curr_distro=None): 39 def __init__(self, taskmodel, handler, configurator, prefs, layers, mach):
36 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) 40 gtk.Window.__init__(self)
41 # global state
42 self.curr_mach = mach
43 self.machine_handler_id = None
44 self.image_combo_id = None
45 self.generating = False
46 self.files_to_clean = []
47 self.selected_image = None
48 self.selected_packages = None
49
37 self.model = taskmodel 50 self.model = taskmodel
38 self.model.connect("tasklist-populated", self.update_model) 51 self.model.connect("tasklist-populated", self.update_model)
39 self.curr_mach = curr_mach 52 self.model.connect("image-changed", self.image_changed_string_cb)
40 self.curr_distro = curr_distro 53 self.curr_image_path = None
41 self.handler = handler 54 self.handler = handler
42 self.set_border_width(10) 55 self.configurator = configurator
43 self.connect("delete-event", gtk.main_quit) 56 self.prefs = prefs
44 self.set_title("BitBake Image Creator") 57 self.layers = layers
45 self.set_default_size(700, 600) 58 self.save_path = None
59 self.dirty = False
60
61 self.connect("delete-event", self.destroy_window)
62 self.set_title("Image Creator")
63 self.set_icon_name("applications-development")
64 self.set_default_size(1000, 650)
46 65
47 self.build = RunningBuild() 66 self.build = RunningBuild()
48 self.build.connect("build-succeeded", self.running_build_succeeded_cb)
49 self.build.connect("build-failed", self.running_build_failed_cb) 67 self.build.connect("build-failed", self.running_build_failed_cb)
68 self.build.connect("build-complete", self.handler.build_complete_cb)
69 self.build.connect("build-started", self.build_started_cb)
50 70
51 createview = self.create_build_gui() 71 self.handler.connect("build-complete", self.build_complete_cb)
72
73 vbox = gtk.VBox(False, 0)
74 vbox.set_border_width(0)
75 vbox.show()
76 self.add(vbox)
77 self.menu = self.create_menu()
78 vbox.pack_start(self.menu, False)
79 createview = self.create_build_gui()
80 self.back = None
81 self.cancel = None
52 buildview = self.view_build_gui() 82 buildview = self.view_build_gui()
53 self.nb = gtk.Notebook() 83 self.nb = gtk.Notebook()
54 self.nb.append_page(createview) 84 self.nb.append_page(createview)
55 self.nb.append_page(buildview) 85 self.nb.append_page(buildview)
56 self.nb.set_current_page(0) 86 self.nb.set_current_page(0)
57 self.nb.set_show_tabs(False) 87 self.nb.set_show_tabs(False)
58 self.add(self.nb) 88 vbox.pack_start(self.nb, expand=True, fill=True)
59 self.generating = False 89
90 def destroy_window(self, widget, event):
91 self.quit()
92
93 def menu_quit(self, action):
94 self.quit()
95
96 def quit(self):
97 if self.dirty and len(self.model.contents):
98 question = "Would you like to save your customisations?"
99 dialog = CrumbsDialog(self, question, gtk.STOCK_DIALOG_WARNING)
100 dialog.add_buttons(gtk.STOCK_NO, gtk.RESPONSE_NO,
101 gtk.STOCK_YES, gtk.RESPONSE_YES)
102 resp = dialog.run()
103 if resp == gtk.RESPONSE_YES:
104 if not self.save_path:
105 self.get_save_path()
106 self.save_recipe_file()
107 rep = self.model.get_build_rep()
108 rep.writeRecipe(self.save_path, self.model)
109
110 gtk.main_quit()
60 111
61 def scroll_tv_cb(self, model, path, it, view): 112 def scroll_tv_cb(self, model, path, it, view):
62 view.scroll_to_cell(path) 113 view.scroll_to_cell(path)
63 114
64 def running_build_failed_cb(self, running_build): 115 def running_build_failed_cb(self, running_build):
65 # FIXME: handle this 116 # FIXME: handle this
66 return 117 print("Build failed")
118
119 def image_changed_string_cb(self, model, new_image):
120 cnt = 0
121 it = self.model.images.get_iter_first()
122 while it:
123 path = self.model.images.get_path(it)
124 if self.model.images[path][self.model.COL_NAME] == new_image:
125 self.image_combo.set_active(cnt)
126 break
127 it = self.model.images.iter_next(it)
128 cnt = cnt + 1
129
130 def image_changed_cb(self, combo):
131 model = self.image_combo.get_model()
132 it = self.image_combo.get_active_iter()
133 if it:
134 path = model.get_path(it)
135 # Firstly, deselect the previous image
136 if self.curr_image_path:
137 self.toggle_package(self.curr_image_path, model)
138 # Now select the new image and save its path in case we
139 # change the image later
140 self.curr_image_path = path
141 self.toggle_package(path, model, image=True)
142
143 def reload_triggered_cb(self, handler, image, packages):
144 if image:
145 self.selected_image = image
146 if len(packages):
147 self.selected_packages = packages.split()
67 148
68 def running_build_succeeded_cb(self, running_build): 149 def data_generated(self, handler):
69 label = gtk.Label("Build completed, start another build?") 150 self.generating = False
70 dialog = gtk.Dialog("Build complete", 151 self.image_combo.set_model(self.model.images_model())
71 self, 152 if not self.image_combo_id:
72 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 153 self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
73 (gtk.STOCK_NO, gtk.RESPONSE_NO, 154 self.enable_widgets()
74 gtk.STOCK_YES, gtk.RESPONSE_YES))
75 dialog.vbox.pack_start(label)
76 label.show()
77 response = dialog.run()
78 dialog.destroy()
79 if response == gtk.RESPONSE_YES:
80 self.model.reset() # NOTE: really?
81 self.nb.set_current_page(0)
82 return
83 155
84 def machine_combo_changed_cb(self, combo, handler): 156 def machine_combo_changed_cb(self, combo, handler):
85 mach = combo.get_active_text() 157 mach = combo.get_active_text()
86 if mach != self.curr_mach: 158 if mach != self.curr_mach:
87 self.curr_mach = mach 159 self.curr_mach = mach
160 # Flush this straight to the file as MACHINE is changed
161 # independently of other 'Preferences'
162 self.configurator.setLocalConfVar('MACHINE', mach)
163 self.configurator.writeLocalConf()
88 handler.set_machine(mach) 164 handler.set_machine(mach)
165 handler.reload_data()
89 166
90 def update_machines(self, handler, machines): 167 def update_machines(self, handler, machines):
91 active = 0 168 active = 0
92 for machine in machines: 169 # disconnect the signal handler before updating the combo model
93 self.machine_combo.append_text(machine) 170 if self.machine_handler_id:
94 if machine == self.curr_mach: 171 self.machine_combo.disconnect(self.machine_handler_id)
172 self.machine_handler_id = None
173
174 model = self.machine_combo.get_model()
175 if model:
176 model.clear()
177
178 for machine in machines:
179 self.machine_combo.append_text(machine)
180 if machine == self.curr_mach:
95 self.machine_combo.set_active(active) 181 self.machine_combo.set_active(active)
96 active = active + 1 182 active = active + 1
97 self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler) 183
98 184 self.machine_handler_id = self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler)
99 def update_distros(self, handler, distros): 185
100 # FIXME: when we add UI for changing distro this will be used 186 def set_busy_cursor(self, busy=True):
101 return 187 """
102 188 Convenience method to set the cursor to a spinner when executing
103 def data_generated(self, handler): 189 a potentially lengthy process.
104 self.generating = False 190 A busy value of False will set the cursor back to the default
105 191 left pointer.
106 def spin_idle_func(self, pbar): 192 """
193 if busy:
194 cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
195 else:
196 # TODO: presumably the default cursor is different on RTL
197 # systems. Can we determine the default cursor? Or at least
198 # the cursor which is set before we change it?
199 cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
200 window = self.get_root_window()
201 window.set_cursor(cursor)
202
203 def busy_idle_func(self):
107 if self.generating: 204 if self.generating:
108 pbar.pulse() 205 self.progress.set_text("Loading...")
206 self.progress.pulse()
109 return True 207 return True
110 else: 208 else:
111 pbar.hide() 209 if not self.image_combo_id:
210 self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
211 self.progress.set_text("Loaded")
212 self.progress.set_fraction(0.0)
213 self.set_busy_cursor(False)
112 return False 214 return False
113 215
114 def busy(self, handler): 216 def busy(self, handler):
115 self.generating = True 217 self.generating = True
116 pbar = ProgressBar(self) 218 self.set_busy_cursor()
117 pbar.connect("delete-event", gtk.main_quit) # NOTE: questionable... 219 if self.image_combo_id:
118 pbar.pulse() 220 self.image_combo.disconnect(self.image_combo_id)
119 gobject.timeout_add (200, 221 self.image_combo_id = None
120 self.spin_idle_func, 222 self.progress.pulse()
121 pbar) 223 gobject.timeout_add (200, self.busy_idle_func)
224 self.disable_widgets()
225
226 def enable_widgets(self):
227 self.menu.set_sensitive(True)
228 self.machine_combo.set_sensitive(True)
229 self.image_combo.set_sensitive(True)
230 self.nb.set_sensitive(True)
231 self.contents_tree.set_sensitive(True)
232
233 def disable_widgets(self):
234 self.menu.set_sensitive(False)
235 self.machine_combo.set_sensitive(False)
236 self.image_combo.set_sensitive(False)
237 self.nb.set_sensitive(False)
238 self.contents_tree.set_sensitive(False)
122 239
123 def update_model(self, model): 240 def update_model(self, model):
124 pkgsaz_model = gtk.TreeModelSort(self.model.packages_model()) 241 # We want the packages model to be alphabetised and sortable so create
242 # a TreeModelSort to use in the view
243 pkgsaz_model = gtk.TreeModelSort(self.model.packages_model())
125 pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING) 244 pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
245 # Unset default sort func so that we only toggle between A-Z and
246 # Z-A sorting
247 pkgsaz_model.set_default_sort_func(None)
126 self.pkgsaz_tree.set_model(pkgsaz_model) 248 self.pkgsaz_tree.set_model(pkgsaz_model)
127 249
128 # FIXME: need to implement a custom sort function, as otherwise the column 250 # We want the contents to be alphabetised so create a TreeModelSort to
129 # is re-ordered when toggling the inclusion state (COL_INC) 251 # use in the view
130 pkgsgrp_model = gtk.TreeModelSort(self.model.packages_model()) 252 contents_model = gtk.TreeModelSort(self.model.contents_model())
131 pkgsgrp_model.set_sort_column_id(self.model.COL_GROUP, gtk.SORT_ASCENDING) 253 contents_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
132 self.pkgsgrp_tree.set_model(pkgsgrp_model) 254 # Unset default sort func so that we only toggle between A-Z and
133 255 # Z-A sorting
134 self.contents_tree.set_model(self.model.contents_model()) 256 contents_model.set_default_sort_func(None)
135 self.images_tree.set_model(self.model.images_model()) 257 self.contents_tree.set_model(contents_model)
136 self.tasks_tree.set_model(self.model.tasks_model()) 258 self.tasks_tree.set_model(self.model.tasks_model())
259
260 if self.selected_image:
261 if self.image_combo_id:
262 self.image_combo.disconnect(self.image_combo_id)
263 self.image_combo_id = None
264 self.model.set_selected_image(self.selected_image)
265 self.selected_image = None
266 if not self.image_combo_id:
267 self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
268
269 if self.selected_packages:
270 self.model.set_selected_packages(self.selected_packages)
271 self.selected_packages = None
137 272
138 def reset_clicked_cb(self, button): 273 def reset_clicked_cb(self, button):
139 label = gtk.Label("Are you sure you want to reset the image contents?") 274 lbl = "<b>Reset your selections?</b>\n\nAny new changes you have made will be lost"
140 dialog = gtk.Dialog("Confirm reset", self, 275 dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
141 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 276 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
142 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, 277 dialog.add_button("Reset", gtk.RESPONSE_OK)
143 gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
144 dialog.vbox.pack_start(label)
145 label.show()
146 response = dialog.run() 278 response = dialog.run()
147 dialog.destroy() 279 dialog.destroy()
148 if (response == gtk.RESPONSE_ACCEPT): 280 if response == gtk.RESPONSE_OK:
149 self.model.reset() 281 self.reset_build()
150 return 282 return
151 283
284 def reset_build(self):
285 self.image_combo.disconnect(self.image_combo_id)
286 self.image_combo_id = None
287 self.image_combo.set_active(-1)
288 self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
289 self.model.reset()
290
291 def layers_cb(self, action):
292 resp = self.layers.run()
293 self.layers.save_current_layers()
294 self.layers.hide()
295
296 def add_layer_cb(self, action):
297 self.layers.find_layer(self)
298
299 def preferences_cb(self, action):
300 resp = self.prefs.run()
301 self.prefs.write_changes()
302 self.prefs.hide()
303
304 def about_cb(self, action):
305 about = gtk.AboutDialog()
306 about.set_name("Image Creator")
307 about.set_copyright("Copyright (C) 2011 Intel Corporation")
308 about.set_authors(["Joshua Lock <josh@linux.intel.com>"])
309 about.set_logo_icon_name("applications-development")
310 about.run()
311 about.destroy()
312
313 def save_recipe_file(self):
314 rep = self.model.get_build_rep()
315 rep.writeRecipe(self.save_path, self.model)
316 self.dirty = False
317
318 def get_save_path(self):
319 chooser = gtk.FileChooserDialog(title=None, parent=self,
320 action=gtk.FILE_CHOOSER_ACTION_SAVE,
321 buttons=(gtk.STOCK_CANCEL,
322 gtk.RESPONSE_CANCEL,
323 gtk.STOCK_SAVE,
324 gtk.RESPONSE_OK,))
325 chooser.set_current_name("myimage.bb")
326 response = chooser.run()
327 if response == gtk.RESPONSE_OK:
328 self.save_path = chooser.get_filename()
329 chooser.destroy()
330
331 def save_cb(self, action):
332 if not self.save_path:
333 self.get_save_path()
334 self.save_recipe_file()
335
336 def save_as_cb(self, action):
337 self.get_save_path()
338 self.save_recipe_file()
339
340 def open_cb(self, action):
341 chooser = gtk.FileChooserDialog(title=None, parent=self,
342 action=gtk.FILE_CHOOSER_ACTION_OPEN,
343 buttons=(gtk.STOCK_CANCEL,
344 gtk.RESPONSE_CANCEL,
345 gtk.STOCK_OPEN,
346 gtk.RESPONSE_OK))
347 response = chooser.run()
348 rep = BuildRep(None, None, None)
349 if response == gtk.RESPONSE_OK:
350 rep.loadRecipe(chooser.get_filename())
351 chooser.destroy()
352 self.model.load_image_rep(rep)
353 self.dirty = False
354
152 def bake_clicked_cb(self, button): 355 def bake_clicked_cb(self, button):
153 if not self.model.targets_contains_image(): 356 rep = self.model.get_build_rep()
154 label = gtk.Label("No image was selected. Just build the selected packages?") 357 if not rep.base_image:
155 dialog = gtk.Dialog("Warning, no image selected", 358 lbl = "<b>Build only packages?</b>\n\nAn image has not been selected, so only the selected packages will be built."
156 self, 359 dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
157 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 360 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
158 (gtk.STOCK_NO, gtk.RESPONSE_NO, 361 dialog.add_button("Build", gtk.RESPONSE_YES)
159 gtk.STOCK_YES, gtk.RESPONSE_YES))
160 dialog.vbox.pack_start(label)
161 label.show()
162 response = dialog.run() 362 response = dialog.run()
163 dialog.destroy() 363 dialog.destroy()
164 if not response == gtk.RESPONSE_YES: 364 if response == gtk.RESPONSE_CANCEL:
165 return 365 return
166
167 # Note: We could "squash" the targets list to only include things not brought in by an image
168 task_list = self.model.get_targets()
169 if len(task_list):
170 tasks = " ".join(task_list)
171 # TODO: show a confirmation dialog
172 print("Including these extra tasks in IMAGE_INSTALL: %s" % tasks)
173 else: 366 else:
174 return 367 # TODO: show a confirmation dialog ?
175 368 if not self.save_path:
369 import tempfile, datetime
370 image_name = "hob-%s-variant-%s.bb" % (rep.base_image, datetime.date.today().isoformat())
371 image_dir = os.path.join(tempfile.gettempdir(), 'hob-images')
372 bb.utils.mkdirhier(image_dir)
373 recipepath = os.path.join(image_dir, image_name)
374 else:
375 recipepath = self.save_path
376
377 rep.writeRecipe(recipepath, self.model)
378 # In the case where we saved the file for the purpose of building
379 # it we should then delete it so that the users workspace doesn't
380 # contain files they haven't explicitly saved there.
381 if not self.save_path:
382 self.files_to_clean.append(recipepath)
383
384 self.handler.queue_image_recipe_path(recipepath)
385
386 self.handler.build_packages(rep.allpkgs.split(" "))
176 self.nb.set_current_page(1) 387 self.nb.set_current_page(1)
177 self.handler.run_build(task_list)
178 388
179 return 389 def back_button_clicked_cb(self, button):
390 self.toggle_createview()
180 391
181 def advanced_expander_cb(self, expander, param): 392 def toggle_createview(self):
182 return 393 self.build.model.clear()
183 394 self.nb.set_current_page(0)
184 def images(self):
185 self.images_tree = gtk.TreeView()
186 self.images_tree.set_headers_visible(True)
187 self.images_tree.set_headers_clickable(False)
188 self.images_tree.set_enable_search(True)
189 self.images_tree.set_search_column(0)
190 self.images_tree.get_selection().set_mode(gtk.SELECTION_NONE)
191 395
192 col = gtk.TreeViewColumn('Package') 396 def build_complete_cb(self, running_build):
193 col1 = gtk.TreeViewColumn('Description') 397 self.back.connect("clicked", self.back_button_clicked_cb)
194 col2 = gtk.TreeViewColumn('License') 398 self.back.set_sensitive(True)
195 col3 = gtk.TreeViewColumn('Include') 399 self.cancel.set_sensitive(False)
196 col3.set_resizable(False) 400 for f in self.files_to_clean:
401 os.remove(f)
197 402
198 self.images_tree.append_column(col) 403 lbl = "<b>Build completed</b>\n\nClick 'Edit Image' to start another build or 'View Log' to view the build log."
199 self.images_tree.append_column(col1) 404 if self.handler.building == "image":
200 self.images_tree.append_column(col2) 405 deploy = self.handler.get_image_deploy_dir()
201 self.images_tree.append_column(col3) 406 lbl = lbl + "\n<a href=\"file://%s\" title=\"%s\">Browse folder of built images</a>." % (deploy, deploy)
202 407
203 cell = gtk.CellRendererText() 408 dialog = CrumbsDialog(self, lbl)
204 cell1 = gtk.CellRendererText() 409 dialog.add_button("View Log", gtk.RESPONSE_CANCEL)
205 cell2 = gtk.CellRendererText() 410 dialog.add_button("Edit Image", gtk.RESPONSE_OK)
206 cell3 = gtk.CellRendererToggle() 411 response = dialog.run()
207 cell3.set_property('activatable', True) 412 dialog.destroy()
208 cell3.connect("toggled", self.toggle_include_cb, self.images_tree) 413 if response == gtk.RESPONSE_OK:
209 414 self.toggle_createview()
210 col.pack_start(cell, True) 415
211 col1.pack_start(cell1, True) 416 def build_started_cb(self, running_build):
212 col2.pack_start(cell2, True) 417 self.back.set_sensitive(False)
213 col3.pack_start(cell3, True) 418 self.cancel.set_sensitive(True)
214 419
215 col.set_attributes(cell, text=self.model.COL_NAME) 420 def include_gplv3_cb(self, toggle):
216 col1.set_attributes(cell1, text=self.model.COL_DESC) 421 excluded = toggle.get_active()
217 col2.set_attributes(cell2, text=self.model.COL_LIC) 422 self.handler.toggle_gplv3(excluded)
218 col3.set_attributes(cell3, active=self.model.COL_INC) 423
219 424 def change_bb_threads(self, spinner):
220 self.images_tree.show() 425 val = spinner.get_value_as_int()
221 426 self.handler.set_bbthreads(val)
222 scroll = gtk.ScrolledWindow() 427
223 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) 428 def change_make_threads(self, spinner):
224 scroll.set_shadow_type(gtk.SHADOW_IN) 429 val = spinner.get_value_as_int()
225 scroll.add(self.images_tree) 430 self.handler.set_pmake(val)
226 431
227 return scroll 432 def toggle_toolchain(self, check):
433 enabled = check.get_active()
434 self.handler.toggle_toolchain(enabled)
435
436 def toggle_headers(self, check):
437 enabled = check.get_active()
438 self.handler.toggle_toolchain_headers(enabled)
439
440 def toggle_package_idle_cb(self, opath, image):
441 """
442 As the operations which we're calling on the model can take
443 a significant amount of time (in the order of seconds) during which
444 the GUI is unresponsive as the main loop is blocked perform them in
445 an idle function which at least enables us to set the busy cursor
446 before the UI is blocked giving the appearance of being responsive.
447 """
448 # Whether the item is currently included
449 inc = self.model[opath][self.model.COL_INC]
450 # If the item is already included, mark it for removal then
451 # the sweep_up() method finds affected items and marks them
452 # appropriately
453 if inc:
454 self.model.mark(opath)
455 self.model.sweep_up()
456 # If the item isn't included, mark it for inclusion
457 else:
458 self.model.include_item(item_path=opath,
459 binb="User Selected",
460 image_contents=image)
461
462 self.set_busy_cursor(False)
463 return False
464
465 def toggle_package(self, path, model, image=False):
466 # Warn user before removing packages
467 inc = model[path][self.model.COL_INC]
468 if inc:
469 pn = model[path][self.model.COL_NAME]
470 revdeps = self.model.find_reverse_depends(pn)
471 if len(revdeps):
472 lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone and all packages which depend on this will be removed\nPackages which depend on %s include %s." % (pn, pn, ", ".join(revdeps).rstrip(","))
473 else:
474 lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone." % pn
475 dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
476 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
477 dialog.add_button("Remove", gtk.RESPONSE_OK)
478 response = dialog.run()
479 dialog.destroy()
480 if response == gtk.RESPONSE_CANCEL:
481 return
228 482
229 def toggle_package(self, path, model): 483 self.set_busy_cursor()
230 # Convert path to path in original model 484 # Convert path to path in original model
231 opath = model.convert_path_to_child_path(path) 485 opath = model.convert_path_to_child_path(path)
232 # current include status 486 # This is a potentially length call which can block the
233 inc = self.model[opath][self.model.COL_INC] 487 # main loop, therefore do the work in an idle func to keep
234 if inc: 488 # the UI responsive
235 self.model.mark(opath) 489 glib.idle_add(self.toggle_package_idle_cb, opath, image)
236 self.model.sweep_up()
237 #self.model.remove_package_full(cpath)
238 else:
239 self.model.include_item(opath)
240 return
241 490
242 def remove_package_cb(self, cell, path): 491 self.dirty = True
243 model = self.model.contents_model()
244 label = gtk.Label("Are you sure you want to remove this item?")
245 dialog = gtk.Dialog("Confirm removal", self,
246 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
247 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
248 gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
249 dialog.vbox.pack_start(label)
250 label.show()
251 response = dialog.run()
252 dialog.destroy()
253 if (response == gtk.RESPONSE_ACCEPT):
254 self.toggle_package(path, model)
255 492
256 def toggle_include_cb(self, cell, path, tv): 493 def toggle_include_cb(self, cell, path, tv):
257 model = tv.get_model() 494 model = tv.get_model()
@@ -262,23 +499,36 @@ class MainWindow (gtk.Window):
262 sort_model = tv.get_model() 499 sort_model = tv.get_model()
263 cpath = sort_model.convert_path_to_child_path(path) 500 cpath = sort_model.convert_path_to_child_path(path)
264 self.toggle_package(cpath, sort_model.get_model()) 501 self.toggle_package(cpath, sort_model.get_model())
265 502
266 def pkgsaz(self): 503 def pkgsaz(self):
504 vbox = gtk.VBox(False, 6)
505 vbox.show()
267 self.pkgsaz_tree = gtk.TreeView() 506 self.pkgsaz_tree = gtk.TreeView()
268 self.pkgsaz_tree.set_headers_visible(True) 507 self.pkgsaz_tree.set_headers_visible(True)
269 self.pkgsaz_tree.set_headers_clickable(True) 508 self.pkgsaz_tree.set_headers_clickable(True)
270 self.pkgsaz_tree.set_enable_search(True) 509 self.pkgsaz_tree.set_enable_search(True)
271 self.pkgsaz_tree.set_search_column(0) 510 self.pkgsaz_tree.set_search_column(0)
272 self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_NONE) 511 self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
273 512
274 col = gtk.TreeViewColumn('Package') 513 col = gtk.TreeViewColumn('Package')
514 col.set_clickable(True)
515 col.set_sort_column_id(self.model.COL_NAME)
516 col.set_min_width(220)
275 col1 = gtk.TreeViewColumn('Description') 517 col1 = gtk.TreeViewColumn('Description')
276 col1.set_resizable(True) 518 col1.set_resizable(True)
519 col1.set_min_width(360)
277 col2 = gtk.TreeViewColumn('License') 520 col2 = gtk.TreeViewColumn('License')
278 col2.set_resizable(True) 521 col2.set_resizable(True)
522 col2.set_clickable(True)
523 col2.set_sort_column_id(self.model.COL_LIC)
524 col2.set_min_width(170)
279 col3 = gtk.TreeViewColumn('Group') 525 col3 = gtk.TreeViewColumn('Group')
280 col4 = gtk.TreeViewColumn('Include') 526 col3.set_clickable(True)
281 col4.set_resizable(False) 527 col3.set_sort_column_id(self.model.COL_GROUP)
528 col4 = gtk.TreeViewColumn('Included')
529 col4.set_min_width(80)
530 col4.set_max_width(90)
531 col4.set_sort_column_id(self.model.COL_INC)
282 532
283 self.pkgsaz_tree.append_column(col) 533 self.pkgsaz_tree.append_column(col)
284 self.pkgsaz_tree.append_column(col1) 534 self.pkgsaz_tree.append_column(col1)
@@ -288,9 +538,9 @@ class MainWindow (gtk.Window):
288 538
289 cell = gtk.CellRendererText() 539 cell = gtk.CellRendererText()
290 cell1 = gtk.CellRendererText() 540 cell1 = gtk.CellRendererText()
291 cell1.set_property('width-chars', 20) 541 cell1.set_property('width-chars', 20)
292 cell2 = gtk.CellRendererText() 542 cell2 = gtk.CellRendererText()
293 cell2.set_property('width-chars', 20) 543 cell2.set_property('width-chars', 20)
294 cell3 = gtk.CellRendererText() 544 cell3 = gtk.CellRendererText()
295 cell4 = gtk.CellRendererToggle() 545 cell4 = gtk.CellRendererToggle()
296 cell4.set_property('activatable', True) 546 cell4.set_property('activatable', True)
@@ -300,7 +550,7 @@ class MainWindow (gtk.Window):
300 col1.pack_start(cell1, True) 550 col1.pack_start(cell1, True)
301 col2.pack_start(cell2, True) 551 col2.pack_start(cell2, True)
302 col3.pack_start(cell3, True) 552 col3.pack_start(cell3, True)
303 col4.pack_start(cell4, True) 553 col4.pack_end(cell4, True)
304 554
305 col.set_attributes(cell, text=self.model.COL_NAME) 555 col.set_attributes(cell, text=self.model.COL_NAME)
306 col1.set_attributes(cell1, text=self.model.COL_DESC) 556 col1.set_attributes(cell1, text=self.model.COL_DESC)
@@ -314,75 +564,43 @@ class MainWindow (gtk.Window):
314 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) 564 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
315 scroll.set_shadow_type(gtk.SHADOW_IN) 565 scroll.set_shadow_type(gtk.SHADOW_IN)
316 scroll.add(self.pkgsaz_tree) 566 scroll.add(self.pkgsaz_tree)
567 vbox.pack_start(scroll, True, True, 0)
568
569 hb = gtk.HBox(False, 0)
570 hb.show()
571 search = gtk.Entry()
572 search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
573 search.connect("icon-release", self.search_entry_clear_cb)
574 search.show()
575 self.pkgsaz_tree.set_search_entry(search)
576 hb.pack_end(search, False, False, 0)
577 label = gtk.Label("Search packages:")
578 label.show()
579 hb.pack_end(label, False, False, 6)
580 vbox.pack_start(hb, False, False, 0)
317 581
318 return scroll 582 return vbox
319
320 def pkgsgrp(self):
321 self.pkgsgrp_tree = gtk.TreeView()
322 self.pkgsgrp_tree.set_headers_visible(True)
323 self.pkgsgrp_tree.set_headers_clickable(False)
324 self.pkgsgrp_tree.set_enable_search(True)
325 self.pkgsgrp_tree.set_search_column(0)
326 self.pkgsgrp_tree.get_selection().set_mode(gtk.SELECTION_NONE)
327
328 col = gtk.TreeViewColumn('Package')
329 col1 = gtk.TreeViewColumn('Description')
330 col1.set_resizable(True)
331 col2 = gtk.TreeViewColumn('License')
332 col2.set_resizable(True)
333 col3 = gtk.TreeViewColumn('Group')
334 col4 = gtk.TreeViewColumn('Include')
335 col4.set_resizable(False)
336
337 self.pkgsgrp_tree.append_column(col)
338 self.pkgsgrp_tree.append_column(col1)
339 self.pkgsgrp_tree.append_column(col2)
340 self.pkgsgrp_tree.append_column(col3)
341 self.pkgsgrp_tree.append_column(col4)
342
343 cell = gtk.CellRendererText()
344 cell1 = gtk.CellRendererText()
345 cell1.set_property('width-chars', 20)
346 cell2 = gtk.CellRendererText()
347 cell2.set_property('width-chars', 20)
348 cell3 = gtk.CellRendererText()
349 cell4 = gtk.CellRendererToggle()
350 cell4.set_property("activatable", True)
351 cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsgrp_tree)
352
353 col.pack_start(cell, True)
354 col1.pack_start(cell1, True)
355 col2.pack_start(cell2, True)
356 col3.pack_start(cell3, True)
357 col4.pack_start(cell4, True)
358
359 col.set_attributes(cell, text=self.model.COL_NAME)
360 col1.set_attributes(cell1, text=self.model.COL_DESC)
361 col2.set_attributes(cell2, text=self.model.COL_LIC)
362 col3.set_attributes(cell3, text=self.model.COL_GROUP)
363 col4.set_attributes(cell4, active=self.model.COL_INC)
364
365 self.pkgsgrp_tree.show()
366
367 scroll = gtk.ScrolledWindow()
368 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
369 scroll.set_shadow_type(gtk.SHADOW_IN)
370 scroll.add(self.pkgsgrp_tree)
371 583
372 return scroll 584 def search_entry_clear_cb(self, entry, icon_pos, event):
585 entry.set_text("")
373 586
374 def tasks(self): 587 def tasks(self):
588 vbox = gtk.VBox(False, 6)
589 vbox.show()
375 self.tasks_tree = gtk.TreeView() 590 self.tasks_tree = gtk.TreeView()
376 self.tasks_tree.set_headers_visible(True) 591 self.tasks_tree.set_headers_visible(True)
377 self.tasks_tree.set_headers_clickable(False) 592 self.tasks_tree.set_headers_clickable(False)
378 self.tasks_tree.set_enable_search(True) 593 self.tasks_tree.set_enable_search(True)
379 self.tasks_tree.set_search_column(0) 594 self.tasks_tree.set_search_column(0)
380 self.tasks_tree.get_selection().set_mode(gtk.SELECTION_NONE) 595 self.tasks_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
381 596
382 col = gtk.TreeViewColumn('Package') 597 col = gtk.TreeViewColumn('Package')
598 col.set_min_width(430)
383 col1 = gtk.TreeViewColumn('Description') 599 col1 = gtk.TreeViewColumn('Description')
600 col1.set_min_width(430)
384 col2 = gtk.TreeViewColumn('Include') 601 col2 = gtk.TreeViewColumn('Include')
385 col2.set_resizable(False) 602 col2.set_min_width(70)
603 col2.set_max_width(80)
386 604
387 self.tasks_tree.append_column(col) 605 self.tasks_tree.append_column(col)
388 self.tasks_tree.append_column(col1) 606 self.tasks_tree.append_column(col1)
@@ -396,7 +614,7 @@ class MainWindow (gtk.Window):
396 614
397 col.pack_start(cell, True) 615 col.pack_start(cell, True)
398 col1.pack_start(cell1, True) 616 col1.pack_start(cell1, True)
399 col2.pack_start(cell2, True) 617 col2.pack_end(cell2, True)
400 618
401 col.set_attributes(cell, text=self.model.COL_NAME) 619 col.set_attributes(cell, text=self.model.COL_NAME)
402 col1.set_attributes(cell1, text=self.model.COL_DESC) 620 col1.set_attributes(cell1, text=self.model.COL_DESC)
@@ -408,26 +626,37 @@ class MainWindow (gtk.Window):
408 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) 626 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
409 scroll.set_shadow_type(gtk.SHADOW_IN) 627 scroll.set_shadow_type(gtk.SHADOW_IN)
410 scroll.add(self.tasks_tree) 628 scroll.add(self.tasks_tree)
629 vbox.pack_start(scroll, True, True, 0)
630
631 hb = gtk.HBox(False, 0)
632 hb.show()
633 search = gtk.Entry()
634 search.show()
635 self.tasks_tree.set_search_entry(search)
636 hb.pack_end(search, False, False, 0)
637 label = gtk.Label("Search collections:")
638 label.show()
639 hb.pack_end(label, False, False, 6)
640 vbox.pack_start(hb, False, False, 0)
411 641
412 return scroll 642 return vbox
413 643
414 def cancel_build(self, button): 644 def cancel_build(self, button):
415 label = gtk.Label("Do you really want to stop this build?") 645 lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this build?"
416 dialog = gtk.Dialog("Cancel build", 646 dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
417 self, 647 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
418 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 648 dialog.add_button("Stop", gtk.RESPONSE_OK)
419 (gtk.STOCK_NO, gtk.RESPONSE_NO, 649 dialog.add_button("Force Stop", gtk.RESPONSE_YES)
420 gtk.STOCK_YES, gtk.RESPONSE_YES))
421 dialog.vbox.pack_start(label)
422 label.show()
423 response = dialog.run() 650 response = dialog.run()
424 dialog.destroy() 651 dialog.destroy()
425 if response == gtk.RESPONSE_YES: 652 if response == gtk.RESPONSE_OK:
426 self.handler.cancel_build() 653 self.handler.cancel_build()
427 return 654 elif response == gtk.RESPONSE_YES:
655 self.handler.cancel_build(True)
428 656
429 def view_build_gui(self): 657 def view_build_gui(self):
430 vbox = gtk.VBox(False, 6) 658 vbox = gtk.VBox(False, 12)
659 vbox.set_border_width(6)
431 vbox.show() 660 vbox.show()
432 build_tv = RunningBuildTreeView() 661 build_tv = RunningBuildTreeView()
433 build_tv.show() 662 build_tv.show()
@@ -438,20 +667,74 @@ class MainWindow (gtk.Window):
438 scrolled_view.add(build_tv) 667 scrolled_view.add(build_tv)
439 scrolled_view.show() 668 scrolled_view.show()
440 vbox.pack_start(scrolled_view, expand=True, fill=True) 669 vbox.pack_start(scrolled_view, expand=True, fill=True)
441 hbox = gtk.HBox(False, 6) 670 hbox = gtk.HBox(False, 12)
442 hbox.show() 671 hbox.show()
443 vbox.pack_start(hbox, expand=False, fill=False) 672 vbox.pack_start(hbox, expand=False, fill=False)
444 cancel = gtk.Button(stock=gtk.STOCK_CANCEL) 673 self.back = gtk.Button("Back")
445 cancel.connect("clicked", self.cancel_build) 674 self.back.show()
446 cancel.show() 675 self.back.set_sensitive(False)
447 hbox.pack_end(cancel, expand=False, fill=False) 676 hbox.pack_start(self.back, expand=False, fill=False)
677 self.cancel = gtk.Button("Stop Build")
678 self.cancel.connect("clicked", self.cancel_build)
679 self.cancel.show()
680 hbox.pack_end(self.cancel, expand=False, fill=False)
448 681
449 return vbox 682 return vbox
683
684 def create_menu(self):
685 menu_items = '''<ui>
686 <menubar name="MenuBar">
687 <menu action="File">
688 <menuitem action="Save"/>
689 <menuitem action="Save As"/>
690 <menuitem action="Open"/>
691 <separator/>
692 <menuitem action="AddLayer" label="Add Layer"/>
693 <separator/>
694 <menuitem action="Quit"/>
695 </menu>
696 <menu action="Edit">
697 <menuitem action="Layers" label="Layers"/>
698 <menuitem action="Preferences"/>
699 </menu>
700 <menu action="Help">
701 <menuitem action="About"/>
702 </menu>
703 </menubar>
704 </ui>'''
705
706 uimanager = gtk.UIManager()
707 accel = uimanager.get_accel_group()
708 self.add_accel_group(accel)
709
710 actions = gtk.ActionGroup('ImageCreator')
711 self.actions = actions
712 actions.add_actions([('Quit', gtk.STOCK_QUIT, None, None,
713 None, self.menu_quit,),
714 ('File', None, '_File'),
715 ('Save', gtk.STOCK_SAVE, None, None, None, self.save_cb),
716 ('Save As', gtk.STOCK_SAVE_AS, None, None, None, self.save_as_cb),
717 ('Open', gtk.STOCK_OPEN, None, None, None, self.open_cb),
718 ('AddLayer', None, 'Add Layer', None, None, self.add_layer_cb),
719 ('Edit', None, '_Edit'),
720 ('Help', None, '_Help'),
721 ('Layers', None, 'Layers', None, None, self.layers_cb),
722 ('Preferences', gtk.STOCK_PREFERENCES, None, None, None, self.preferences_cb),
723 ('About', gtk.STOCK_ABOUT, None, None, None, self.about_cb)])
724 uimanager.insert_action_group(actions, 0)
725 uimanager.add_ui_from_string(menu_items)
726
727 menubar = uimanager.get_widget('/MenuBar')
728 menubar.show_all()
729
730 return menubar
450 731
451 def create_build_gui(self): 732 def create_build_gui(self):
452 vbox = gtk.VBox(False, 6) 733 vbox = gtk.VBox(False, 12)
734 vbox.set_border_width(6)
453 vbox.show() 735 vbox.show()
454 hbox = gtk.HBox(False, 6) 736
737 hbox = gtk.HBox(False, 12)
455 hbox.show() 738 hbox.show()
456 vbox.pack_start(hbox, expand=False, fill=False) 739 vbox.pack_start(hbox, expand=False, fill=False)
457 740
@@ -459,90 +742,92 @@ class MainWindow (gtk.Window):
459 label.show() 742 label.show()
460 hbox.pack_start(label, expand=False, fill=False, padding=6) 743 hbox.pack_start(label, expand=False, fill=False, padding=6)
461 self.machine_combo = gtk.combo_box_new_text() 744 self.machine_combo = gtk.combo_box_new_text()
462 self.machine_combo.set_active(0)
463 self.machine_combo.show() 745 self.machine_combo.show()
464 self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.") 746 self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.")
465 hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6) 747 hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6)
748 label = gtk.Label("Base image:")
749 label.show()
750 hbox.pack_start(label, expand=False, fill=False, padding=6)
751 self.image_combo = gtk.ComboBox()
752 self.image_combo.show()
753 self.image_combo.set_tooltip_text("Selects the image on which to base the created image")
754 image_combo_cell = gtk.CellRendererText()
755 self.image_combo.pack_start(image_combo_cell, True)
756 self.image_combo.add_attribute(image_combo_cell, 'text', self.model.COL_NAME)
757 hbox.pack_start(self.image_combo, expand=False, fill=False, padding=6)
758 self.progress = gtk.ProgressBar()
759 self.progress.set_size_request(250, -1)
760 hbox.pack_end(self.progress, expand=False, fill=False, padding=6)
466 761
467 ins = gtk.Notebook() 762 ins = gtk.Notebook()
468 vbox.pack_start(ins, expand=True, fill=True) 763 vbox.pack_start(ins, expand=True, fill=True)
469 ins.set_show_tabs(True) 764 ins.set_show_tabs(True)
470 label = gtk.Label("Images") 765 label = gtk.Label("Packages")
471 label.show() 766 label.show()
472 ins.append_page(self.images(), tab_label=label) 767 ins.append_page(self.pkgsaz(), tab_label=label)
473 label = gtk.Label("Tasks") 768 label = gtk.Label("Package Collections")
474 label.show() 769 label.show()
475 ins.append_page(self.tasks(), tab_label=label) 770 ins.append_page(self.tasks(), tab_label=label)
476 label = gtk.Label("Packages (by Group)")
477 label.show()
478 ins.append_page(self.pkgsgrp(), tab_label=label)
479 label = gtk.Label("Packages (by Name)")
480 label.show()
481 ins.append_page(self.pkgsaz(), tab_label=label)
482 ins.set_current_page(0) 771 ins.set_current_page(0)
483 ins.show_all() 772 ins.show_all()
484 773
485 hbox = gtk.HBox()
486 hbox.show()
487 vbox.pack_start(hbox, expand=False, fill=False)
488 label = gtk.Label("Image contents:") 774 label = gtk.Label("Image contents:")
775 self.model.connect("contents-changed", self.update_package_count_cb, label)
776 label.set_property("xalign", 0.00)
489 label.show() 777 label.show()
490 hbox.pack_start(label, expand=False, fill=False, padding=6) 778 vbox.pack_start(label, expand=False, fill=False, padding=6)
491 con = self.contents() 779 con = self.contents()
492 con.show() 780 con.show()
493 vbox.pack_start(con, expand=True, fill=True) 781 vbox.pack_start(con, expand=True, fill=True)
494 782
495 #advanced = gtk.Expander(label="Advanced") 783 bbox = gtk.HButtonBox()
496 #advanced.connect("notify::expanded", self.advanced_expander_cb) 784 bbox.set_spacing(12)
497 #advanced.show() 785 bbox.set_layout(gtk.BUTTONBOX_END)
498 #vbox.pack_start(advanced, expand=False, fill=False) 786 bbox.show()
499 787 vbox.pack_start(bbox, expand=False, fill=False)
500 hbox = gtk.HBox()
501 hbox.show()
502 vbox.pack_start(hbox, expand=False, fill=False)
503 bake = gtk.Button("Bake")
504 bake.connect("clicked", self.bake_clicked_cb)
505 bake.show()
506 hbox.pack_end(bake, expand=False, fill=False, padding=6)
507 reset = gtk.Button("Reset") 788 reset = gtk.Button("Reset")
508 reset.connect("clicked", self.reset_clicked_cb) 789 reset.connect("clicked", self.reset_clicked_cb)
509 reset.show() 790 reset.show()
510 hbox.pack_end(reset, expand=False, fill=False, padding=6) 791 bbox.add(reset)
792 bake = gtk.Button("Bake")
793 bake.connect("clicked", self.bake_clicked_cb)
794 bake.show()
795 bbox.add(bake)
511 796
512 return vbox 797 return vbox
513 798
799 def update_package_count_cb(self, model, count, label):
800 lbl = "Image contents (%s packages):" % count
801 label.set_text(lbl)
802
514 def contents(self): 803 def contents(self):
515 self.contents_tree = gtk.TreeView() 804 self.contents_tree = gtk.TreeView()
516 self.contents_tree.set_headers_visible(True) 805 self.contents_tree.set_headers_visible(True)
517 self.contents_tree.get_selection().set_mode(gtk.SELECTION_NONE) 806 self.contents_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
518 807
519 # allow searching in the package column 808 # allow searching in the package column
520 self.contents_tree.set_search_column(0) 809 self.contents_tree.set_search_column(0)
810 self.contents_tree.set_enable_search(True)
521 811
522 col = gtk.TreeViewColumn('Package') 812 col = gtk.TreeViewColumn('Package')
523 col.set_sort_column_id(0) 813 col.set_sort_column_id(0)
814 col.set_min_width(430)
524 col1 = gtk.TreeViewColumn('Brought in by') 815 col1 = gtk.TreeViewColumn('Brought in by')
525 col1.set_resizable(True) 816 col1.set_resizable(True)
526 col2 = gtk.TreeViewColumn('Remove') 817 col1.set_min_width(430)
527 col2.set_expand(False)
528 818
529 self.contents_tree.append_column(col) 819 self.contents_tree.append_column(col)
530 self.contents_tree.append_column(col1) 820 self.contents_tree.append_column(col1)
531 self.contents_tree.append_column(col2)
532 821
533 cell = gtk.CellRendererText() 822 cell = gtk.CellRendererText()
534 cell1 = gtk.CellRendererText() 823 cell1 = gtk.CellRendererText()
535 cell1.set_property('width-chars', 20) 824 cell1.set_property('width-chars', 20)
536 cell2 = gtk.CellRendererToggle()
537 cell2.connect("toggled", self.remove_package_cb)
538 825
539 col.pack_start(cell, True) 826 col.pack_start(cell, True)
540 col1.pack_start(cell1, True) 827 col1.pack_start(cell1, True)
541 col2.pack_start(cell2, True)
542 828
543 col.set_attributes(cell, text=self.model.COL_NAME) 829 col.set_attributes(cell, text=self.model.COL_NAME)
544 col1.set_attributes(cell1, text=self.model.COL_BINB) 830 col1.set_attributes(cell1, text=self.model.COL_BINB)
545 col2.set_attributes(cell2, active=self.model.COL_INC)
546 831
547 self.contents_tree.show() 832 self.contents_tree.show()
548 833
@@ -554,26 +839,67 @@ class MainWindow (gtk.Window):
554 return scroll 839 return scroll
555 840
556def main (server, eventHandler): 841def main (server, eventHandler):
842 import multiprocessing
843 cpu_cnt = multiprocessing.cpu_count()
844
557 gobject.threads_init() 845 gobject.threads_init()
558 846
559 taskmodel = TaskListModel() 847 taskmodel = TaskListModel()
848 configurator = Configurator()
560 handler = HobHandler(taskmodel, server) 849 handler = HobHandler(taskmodel, server)
561 mach = server.runCommand(["getVariable", "MACHINE"]) 850 mach = server.runCommand(["getVariable", "MACHINE"])
851 sdk_mach = server.runCommand(["getVariable", "SDKMACHINE"])
852 # If SDKMACHINE not set the default SDK_ARCH is used so we
853 # should represent that in the GUI
854 if not sdk_mach:
855 sdk_mach = server.runCommand(["getVariable", "SDK_ARCH"])
562 distro = server.runCommand(["getVariable", "DISTRO"]) 856 distro = server.runCommand(["getVariable", "DISTRO"])
563 857 bbthread = server.runCommand(["getVariable", "BB_NUMBER_THREADS"])
564 window = MainWindow(taskmodel, handler, mach, distro) 858 if not bbthread:
859 bbthread = cpu_cnt
860 handler.set_bbthreads(cpu_cnt)
861 else:
862 bbthread = int(bbthread)
863 pmake = server.runCommand(["getVariable", "PARALLEL_MAKE"])
864 if not pmake:
865 pmake = cpu_cnt
866 handler.set_pmake(cpu_cnt)
867 else:
868 # The PARALLEL_MAKE variable will be of the format: "-j 3" and we only
869 # want a number for the spinner, so strip everything from the variable
870 # up to and including the space
871 pmake = int(pmake[pmake.find(" ")+1:])
872
873 image_types = server.runCommand(["getVariable", "IMAGE_TYPES"])
874
875 pclasses = server.runCommand(["getVariable", "PACKAGE_CLASSES"]).split(" ")
876 # NOTE: we're only supporting one value for PACKAGE_CLASSES being set
877 # this seems OK because we're using the first package format set in
878 # PACKAGE_CLASSES and that's the package manager used for the rootfs
879 pkg, sep, pclass = pclasses[0].rpartition("_")
880
881 prefs = HobPrefs(configurator, handler, sdk_mach, distro, pclass, cpu_cnt,
882 pmake, bbthread, image_types)
883 layers = LayerEditor(configurator, None)
884 window = MainWindow(taskmodel, handler, configurator, prefs, layers, mach)
885 prefs.set_parent_window(window)
886 layers.set_parent_window(window)
565 window.show_all () 887 window.show_all ()
566 handler.connect("machines-updated", window.update_machines) 888 handler.connect("machines-updated", window.update_machines)
567 handler.connect("distros-updated", window.update_distros) 889 handler.connect("sdk-machines-updated", prefs.update_sdk_machines)
890 handler.connect("distros-updated", prefs.update_distros)
891 handler.connect("package-formats-found", prefs.update_package_formats)
568 handler.connect("generating-data", window.busy) 892 handler.connect("generating-data", window.busy)
569 handler.connect("data-generated", window.data_generated) 893 handler.connect("data-generated", window.data_generated)
570 pbar = ProgressBar(window) 894 handler.connect("reload-triggered", window.reload_triggered_cb)
571 pbar.connect("delete-event", gtk.main_quit) 895 configurator.connect("layers-loaded", layers.load_current_layers)
896 configurator.connect("layers-changed", handler.reload_data)
897 handler.connect("config-found", configurator.configFound)
572 898
573 try: 899 try:
574 # kick the while thing off 900 # kick the while thing off
575 handler.current_command = "findConfigFilesDistro" 901 handler.current_command = "findConfigFilePathLocal"
576 server.runCommand(["findConfigFiles", "DISTRO"]) 902 server.runCommand(["findConfigFilePath", "local.conf"])
577 except xmlrpclib.Fault: 903 except xmlrpclib.Fault:
578 print("XMLRPC Fault getting commandline:\n %s" % x) 904 print("XMLRPC Fault getting commandline:\n %s" % x)
579 return 1 905 return 1
@@ -584,7 +910,7 @@ def main (server, eventHandler):
584 handler.event_handle_idle_func, 910 handler.event_handle_idle_func,
585 eventHandler, 911 eventHandler,
586 window.build, 912 window.build,
587 pbar) 913 window.progress)
588 914
589 try: 915 try:
590 gtk.main() 916 gtk.main()