diff options
author | Joshua Lock <josh@linux.intel.com> | 2011-07-01 15:58:50 -0700 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2011-07-05 14:40:30 +0100 |
commit | 4cc291c007103c19779f995e852b37dbad122293 (patch) | |
tree | 3447d62ef1ba2eca08137b8e13df58f8a337a453 /bitbake | |
parent | 7fc9c3488f7865111ec052d1cab213d216d2b414 (diff) | |
download | poky-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.py | 278 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/hig.py | 61 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/hobeventhandler.py | 218 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/hobprefs.py | 293 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/layereditor.py | 136 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/runningbuild.py | 12 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/tasklistmodel.py | 326 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/hob.py | 924 |
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 | |||
21 | import gobject | ||
22 | import copy | ||
23 | import re, os | ||
24 | from bb import data | ||
25 | |||
26 | class 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 | |||
21 | import gobject | ||
22 | import gtk | ||
23 | """ | ||
24 | The following are convenience classes for implementing GNOME HIG compliant | ||
25 | BitBake GUI's | ||
26 | In summary: spacing = 12px, border-width = 6px | ||
27 | """ | ||
28 | |||
29 | class 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 | ||
21 | import gobject | 21 | import gobject |
22 | from bb.ui.crumbs.progress import ProgressBar | ||
23 | 22 | ||
24 | progress_total = 0 | 23 | progress_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 | |||
21 | import gtk | ||
22 | from bb.ui.crumbs.configurator import Configurator | ||
23 | |||
24 | class 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 | |||
21 | import gobject | ||
22 | import gtk | ||
23 | from bb.ui.crumbs.configurator import Configurator | ||
24 | |||
25 | class 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 | ||
48 | class RunningBuild (gobject.GObject): | 48 | class 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 | ||
21 | import gtk | 21 | import gtk |
22 | import gobject | 22 | import gobject |
23 | import re | ||
24 | |||
25 | class 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 | |||
60 | require %s.bb | ||
61 | |||
62 | IMAGE_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 | ||
24 | class TaskListModel(gtk.ListStore): | 76 | class 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 | ||
21 | import glib | ||
21 | import gobject | 22 | import gobject |
22 | import gtk | 23 | import gtk |
23 | from bb.ui.crumbs.progress import ProgressBar | 24 | from bb.ui.crumbs.tasklistmodel import TaskListModel, BuildRep |
24 | from bb.ui.crumbs.tasklistmodel import TaskListModel | ||
25 | from bb.ui.crumbs.hobeventhandler import HobHandler | 25 | from bb.ui.crumbs.hobeventhandler import HobHandler |
26 | from bb.ui.crumbs.configurator import Configurator | ||
27 | from bb.ui.crumbs.hobprefs import HobPrefs | ||
28 | from bb.ui.crumbs.layereditor import LayerEditor | ||
26 | from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild | 29 | from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild |
30 | from bb.ui.crumbs.hig import CrumbsDialog | ||
27 | import xmlrpclib | 31 | import xmlrpclib |
28 | import logging | 32 | import logging |
29 | import Queue | 33 | import Queue |
@@ -32,226 +36,459 @@ extraCaches = ['bb.cache_extra:HobRecipeInfo'] | |||
32 | 36 | ||
33 | class MainWindow (gtk.Window): | 37 | class 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 | ||
556 | def main (server, eventHandler): | 841 | def 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() |