diff options
Diffstat (limited to 'bitbake/lib/bb/ui/crumbs')
-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 |
7 files changed, 1199 insertions, 125 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) | ||