diff options
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) | ||