2 # BitBake Graphical GTK User Interface
4 # Copyright (C) 2011 Intel Corporation
6 # Authored by Joshua Lock <josh@linux.intel.com>
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.
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.
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.
26 class Configurator(gobject
.GObject
):
29 A GObject to handle writing modified configuration values back
33 "layers-loaded" : (gobject
.SIGNAL_RUN_LAST
,
36 "layers-changed" : (gobject
.SIGNAL_RUN_LAST
,
42 gobject
.GObject
.__init
__(self
)
45 self
.enabled_layers
= {}
46 self
.loaded_layers
= {}
50 # NOTE: cribbed from the cooker...
51 def _parse(self
, f
, data
, include
=False):
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
))
58 def _loadLocalConf(self
, path
):
60 return bb
.data
.getVar(var
, data
, True) or ""
69 data
= self
._parse
(self
.local
, data
)
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
94 self
.orig_config
= copy
.deepcopy(self
.config
)
96 def setLocalConfVar(self
, var
, val
):
97 if var
in self
.config
:
98 self
.config
[var
] = val
100 def _loadLayerConf(self
, 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()
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
113 self
.enabled_layers
= copy
.deepcopy(self
.loaded_layers
)
114 self
.emit("layers-loaded")
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
)
123 def _splitLayer(self
, path
):
124 # we only care about the path up to /conf/layer.conf
125 layerpath
, conf
, end
= path
.rpartition("/conf/")
128 def _getLayerName(self
, path
):
129 # Should this be the collection name?
130 layerpath
, sep
, name
= path
.rpartition("/")
133 def disableLayer(self
, layer
):
134 if layer
in self
.enabled_layers
:
135 del self
.enabled_layers
[layer
]
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
144 def addLayer(self
, name
, path
):
145 self
.enabled_layers
[name
] = path
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
):
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
:
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
:
162 # if this layers path has changed, definitely dirty
163 if self
.enabled_layers
[layer
] != self
.loaded_layers
[layer
]:
168 def _constructLayerEntry(self
):
170 Returns a string representing the new layer selection
172 layers
= self
.enabled_layers
.copy()
173 # Construct BBLAYERS entry
174 layer_entry
= "BBLAYERS = \" \\\n"
176 layer_entry
= layer_entry
+ " %s \\\n" % layers
['meta']
179 layer_entry
= layer_entry
+ " %s \\\n" % layers
[layer
]
180 layer_entry
= layer_entry
+ " \""
182 return "".join(layer_entry
)
184 def writeLocalConf(self
):
185 # Dictionary containing only new or modified variables
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
192 if not len(changed_values
):
195 # Create a backup of the local.conf
196 bkup
= "%s~" % self
.local
197 os
.rename(self
.local
, bkup
)
199 # read the original conf into a list
200 with
open(bkup
, 'r') as config
:
201 config_lines
= config
.readlines()
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
)
214 pattern
= '^\s*(%s)\s*([+=?.]+)(.*)' % re
.escape(var
)
215 p
= re
.compile(pattern
)
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
)
225 config_lines
[cnt
] = "#%s" % line
226 new_config_lines
.append(new_line
)
231 new_config_lines
.append("%s = \"%s\"" % (var
, changed_values
[var
]))
233 # Add the modified variables
234 config_lines
.extend(new_config_lines
)
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
))
241 self
.orig_config
= copy
.deepcopy(self
.config
)
243 def writeLayerConf(self
):
244 # If we've not added/removed new layers don't write
245 if not self
._isLayerConfDirty
():
248 # This pattern should find the existing BBLAYERS
249 pattern
= 'BBLAYERS\s=\s\".*\"'
251 # Backup the users bblayers.conf
252 bkup
= "%s~" % self
.bblayers
253 os
.rename(self
.bblayers
, bkup
)
255 replacement
= self
._constructLayerEntry
()
257 with
open(bkup
, "r") as f
:
259 p
= re
.compile(pattern
, re
.DOTALL
)
260 new
= p
.sub(replacement
, contents
)
262 with
open(self
.bblayers
, "w") as n
:
265 # At some stage we should remove the backup we've created
266 # though we should probably verify it first
269 # set loaded_layers for dirtiness tracking
270 self
.loaded_layers
= copy
.deepcopy(self
.enabled_layers
)
272 self
.emit("layers-changed")
274 def configFound(self
, handler
, path
):
275 self
._addConfigFile
(path
)
277 def loadConfig(self
, path
):
278 self
._addConfigFile
(path
)