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/lib/bb/ui/crumbs/configurator.py | |
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/lib/bb/ui/crumbs/configurator.py')
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/configurator.py | 278 |
1 files changed, 278 insertions, 0 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) | ||