Changeset View
Changeset View
Standalone View
Standalone View
bin/options.py
1 | ## @package property handling | 1 | ## @package property handling | ||
---|---|---|---|---|---|
2 | # | 2 | # | ||
3 | # (c) copyright 2009-2011 Ralf Habacker <ralf.habacker@freenet.de> | 3 | # (c) copyright 2009-2011 Ralf Habacker <ralf.habacker@freenet.de> | ||
4 | # | 4 | # | ||
5 | # | 5 | # | ||
6 | # | 6 | # | ||
7 | | ||||
7 | import utils | 8 | import utils | ||
8 | from CraftConfig import * | 9 | from CraftConfig import * | ||
9 | from CraftCore import CraftCore | 10 | from CraftCore import CraftCore | ||
10 | from Blueprints.CraftPackageObject import * | 11 | from Blueprints.CraftPackageObject import * | ||
11 | from CraftDebug import deprecated | 12 | from CraftDebug import deprecated | ||
12 | 13 | | |||
13 | import configparser | 14 | import configparser | ||
14 | import atexit | 15 | import atexit | ||
15 | import copy | 16 | import zlib | ||
17 | from typing import Dict | ||||
18 | | ||||
19 | class RegisteredOption(object): | ||||
20 | def __init__(self, value, compatible): | ||||
21 | self.value = value | ||||
22 | # whether or not this change breaks binary cache compatibility | ||||
23 | self.compatible = compatible | ||||
16 | 24 | | |||
17 | class UserOptions(object): | 25 | class UserOptions(object): | ||
18 | class UserOptionsSingleton(object): | 26 | class UserOptionsSingleton(object): | ||
19 | _instance = None | 27 | _instance = None | ||
20 | 28 | | |||
21 | @property | 29 | @property | ||
22 | def __header(self): | 30 | def __header(self): | ||
23 | return """\ | 31 | return """\ | ||
Show All 28 Lines | |||||
52 | # Settings are inherited, so you can set them for a whole sub branch or a single blueprint. | 60 | # Settings are inherited, so you can set them for a whole sub branch or a single blueprint. | ||
53 | # While blueprint from [libs] are all ignored blueprint from [libs/qt5] are not. | 61 | # While blueprint from [libs] are all ignored blueprint from [libs/qt5] are not. | ||
54 | # | 62 | # | ||
55 | """ | 63 | """ | ||
56 | 64 | | |||
57 | def __init__(self): | 65 | def __init__(self): | ||
58 | self.cachedOptions = {} | 66 | self.cachedOptions = {} | ||
59 | self.packageOptions = {} | 67 | self.packageOptions = {} | ||
60 | self.registeredOptions = {} | 68 | self.registeredOptions = {} # type: Dict[str : RegisteredOption] | ||
61 | 69 | | |||
62 | self.path = CraftCore.settings.get("Blueprints", "Settings", | 70 | self.path = CraftCore.settings.get("Blueprints", "Settings", | ||
63 | os.path.join(CraftCore.standardDirs.etcDir(), "BlueprintSettings.ini")) | 71 | os.path.join(CraftCore.standardDirs.etcDir(), "BlueprintSettings.ini")) | ||
64 | self.settings = configparser.ConfigParser(allow_no_value=True) | 72 | self.settings = configparser.ConfigParser(allow_no_value=True) | ||
65 | self.settings.optionxform = str | 73 | self.settings.optionxform = str | ||
66 | 74 | | |||
67 | if os.path.isfile(self.path): | 75 | if os.path.isfile(self.path): | ||
68 | self.settings.read(self.path, encoding="utf-8") | 76 | self.settings.read(self.path, encoding="utf-8") | ||
Show All 37 Lines | 110 | def __init__(self, package): | |||
106 | _register = self.registerOption | 114 | _register = self.registerOption | ||
107 | _convert = self._convert | 115 | _convert = self._convert | ||
108 | 116 | | |||
109 | _register("version", str, permanent=False) | 117 | _register("version", str, permanent=False) | ||
110 | _register("branch", str, permanent=False) | 118 | _register("branch", str, permanent=False) | ||
111 | _register("revision", str, permanent=False) | 119 | _register("revision", str, permanent=False) | ||
112 | _register("patchLevel", int, permanent=False) | 120 | _register("patchLevel", int, permanent=False) | ||
113 | _register("ignored", bool, permanent=False) | 121 | _register("ignored", bool, permanent=False) | ||
114 | _register("buildTests", bool, permanent=False) | 122 | _register("buildTests", bool, permanent=False, compatible=True) | ||
115 | _register("buildStatic",bool, permanent=False) | 123 | _register("buildStatic",bool, permanent=False) | ||
116 | 124 | | |||
117 | _register("buildType", CraftCore.settings.get("Compile", "BuildType"), permanent=False) | 125 | _register("buildType", CraftCore.settings.get("Compile", "BuildType"), permanent=False) | ||
118 | _register("args", "", permanent=False) | 126 | _register("args", "", permanent=False) | ||
119 | 127 | | |||
120 | settings = UserOptions.instance().settings | 128 | settings = UserOptions.instance().settings | ||
121 | if settings.has_section(package.path): | 129 | if settings.has_section(package.path): | ||
122 | _registered = UserOptions.instance().registeredOptions[package.path] | 130 | _registered = UserOptions.instance().registeredOptions[package.path] | ||
123 | for k, v in settings[package.path].items(): | 131 | for key, value in settings[package.path].items(): | ||
124 | if k in _registered: | 132 | if key in _registered: | ||
125 | v = _convert(_registered[k], v) | 133 | value = _convert(_registered[key].value, value) | ||
126 | setattr(self, k, v) | 134 | setattr(self, key, value) | ||
127 | 135 | | |||
128 | def __str__(self): | 136 | def __str__(self): | ||
129 | out = [] | 137 | out = [] | ||
130 | for k, v in UserOptions.instance().registeredOptions[self._package.path].items(): | 138 | for key, option in UserOptions.instance().registeredOptions[self._package.path].items(): | ||
131 | atr = getattr(self, k) | 139 | value = option.value | ||
140 | atr = getattr(self, key) | ||||
132 | if atr is None: | 141 | if atr is None: | ||
133 | if callable(v): | 142 | if callable(value): | ||
134 | atr = f"({v.__name__})" | 143 | atr = f"({value.__name__})" | ||
135 | else: | 144 | else: | ||
136 | atr = v | 145 | atr = value | ||
137 | out.append((k, atr)) | 146 | out.append((key, atr)) | ||
138 | return ", ".join([f"{x}={y}" for x, y in sorted(out)]) | 147 | return ", ".join([f"{x}={y}" for x, y in sorted(out)]) | ||
139 | 148 | | |||
149 | def configHash(self): | ||||
150 | tmp = [] | ||||
151 | for key, option in sorted(UserOptions.instance().registeredOptions[self._package.path].items()): | ||||
152 | # ignore flags that have no influence on the archive | ||||
153 | if not option.compatible: | ||||
154 | value = option.value | ||||
155 | atr = getattr(self, key) | ||||
156 | if atr is not None: | ||||
157 | if key == "buildType": | ||||
158 | # Releaseand and RelWithDebInfo are compatible | ||||
159 | atr = 1 if atr in {"Release", "RelWithDebInfo"} else 0 | ||||
160 | tmp.append(key.encode()) | ||||
161 | tmp.append(bytes(atr, "UTF-8") if isinstance(atr, str) else bytes([atr])) | ||||
162 | print(tmp) | ||||
163 | return zlib.adler32(b"".join(tmp)) | ||||
164 | | ||||
165 | | ||||
140 | @staticmethod | 166 | @staticmethod | ||
141 | def get(package): | 167 | def get(package): | ||
142 | _instance = UserOptions.instance() | 168 | _instance = UserOptions.instance() | ||
143 | packagePath = package.path | 169 | packagePath = package.path | ||
144 | if packagePath in _instance.cachedOptions: | 170 | if packagePath in _instance.cachedOptions: | ||
145 | option = _instance.cachedOptions[packagePath] | 171 | option = _instance.cachedOptions[packagePath] | ||
146 | else: | 172 | else: | ||
147 | option = UserOptions(package) | 173 | option = UserOptions(package) | ||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Line(s) | |||||
191 | @staticmethod | 217 | @staticmethod | ||
192 | def addPackageOption(package : CraftPackageObject, key : str, value : str) -> None: | 218 | def addPackageOption(package : CraftPackageObject, key : str, value : str) -> None: | ||
193 | if package.path not in UserOptions.instance().packageOptions: | 219 | if package.path not in UserOptions.instance().packageOptions: | ||
194 | UserOptions.instance().packageOptions[package.path] = {} | 220 | UserOptions.instance().packageOptions[package.path] = {} | ||
195 | UserOptions.instance().packageOptions[package.path][key] = value | 221 | UserOptions.instance().packageOptions[package.path][key] = value | ||
196 | 222 | | |||
197 | 223 | | |||
198 | def setOption(self, key, value) -> bool: | 224 | def setOption(self, key, value) -> bool: | ||
199 | _instance = UserOptions.instance() | 225 | _instance = UserOptions.instance() # type: UserOptions.UserOptionsSingleton | ||
200 | package = self._package | 226 | package = self._package | ||
201 | if package.path not in _instance.registeredOptions:# actually that can only happen if package is invalid | 227 | if package.path not in _instance.registeredOptions:# actually that can only happen if package is invalid | ||
202 | CraftCore.log.error(f"{package} has no options") | 228 | CraftCore.log.error(f"{package} has no options") | ||
203 | return False | 229 | return False | ||
204 | if key not in _instance.registeredOptions[package.path]: | 230 | if key not in _instance.registeredOptions[package.path]: | ||
205 | CraftCore.log.error(f"{package} unknown option {key}") | 231 | CraftCore.log.error(f"{package} unknown option {key}") | ||
206 | CraftCore.log.error(f"Valid options are") | 232 | CraftCore.log.error(f"Valid options are") | ||
207 | for opt, default in _instance.registeredOptions[package.path].items(): | 233 | for optionKey, defaultOption in _instance.registeredOptions[package.path].items(): | ||
234 | default = defaultOption.value | ||||
208 | default = default if callable(default) else type(default) | 235 | default = default if callable(default) else type(default) | ||
209 | CraftCore.log.error(f"\t{default.__name__} : {opt}") | 236 | CraftCore.log.error(f"\t{default.__name__} : {optionKey}") | ||
210 | return False | 237 | return False | ||
211 | settings = _instance.initPackage(self) | 238 | settings = _instance.initPackage(self) | ||
212 | if value == "" and key in settings: | 239 | if value == "" and key in settings: | ||
213 | del settings[key] | 240 | del settings[key] | ||
214 | delattr(self, key) | 241 | delattr(self, key) | ||
215 | else: | 242 | else: | ||
216 | value = self._convert(_instance.registeredOptions[package.path][key], value) | 243 | value = self._convert(_instance.registeredOptions[package.path][key].value, value) | ||
217 | settings[key] = str(value) | 244 | settings[key] = str(value) | ||
218 | setattr(self, key, value) | 245 | setattr(self, key, value) | ||
219 | return True | 246 | return True | ||
220 | 247 | | |||
221 | def registerOption(self, key : str, default, permanent=True) -> bool: | 248 | def registerOption(self, key : str, default, permanent=True, compatible=False) -> bool: | ||
222 | _instance = UserOptions.instance() | 249 | _instance = UserOptions.instance() | ||
223 | package = self._package | 250 | package = self._package | ||
224 | if package.path not in _instance.registeredOptions: | 251 | if package.path not in _instance.registeredOptions: | ||
225 | _instance.registeredOptions[package.path] = {} | 252 | _instance.registeredOptions[package.path] = {} | ||
226 | if key in _instance.registeredOptions[package.path]: | 253 | if key in _instance.registeredOptions[package.path]: | ||
227 | raise BlueprintException(f"Failed to register option:\n[{package}]\n{key}={default}\nThe setting {key} is already registered.", package) | 254 | raise BlueprintException(f"Failed to register option:\n[{package}]\n{key}={default}\nThe setting {key} is already registered.", package) | ||
228 | return False | 255 | return False | ||
229 | _instance.registeredOptions[package.path][key] = default | 256 | _instance.registeredOptions[package.path][key] = RegisteredOption(default, compatible) | ||
230 | if permanent: | 257 | if permanent: | ||
231 | settings = _instance.initPackage(self) | 258 | settings = _instance.initPackage(self) | ||
232 | if key and key not in settings: | 259 | if key and key not in settings: | ||
233 | settings[key] = str(default) | 260 | settings[key] = str(default) | ||
234 | 261 | | |||
235 | # don't try to save types | 262 | # don't try to save types | ||
236 | if not callable(default): | 263 | if not callable(default): | ||
237 | if not hasattr(self, key): | 264 | if not hasattr(self, key): | ||
238 | setattr(self, key, default) | 265 | setattr(self, key, default) | ||
239 | else: | 266 | else: | ||
240 | # convert type | 267 | # convert type | ||
241 | old = getattr(self, key) | 268 | old = getattr(self, key) | ||
242 | try: | 269 | try: | ||
243 | new = self._convert(default, old) | 270 | new = self._convert(default, old) | ||
244 | except: | 271 | except: | ||
245 | raise BlueprintException(f"Found an invalid option in BlueprintSettings.ini,\n[{self._package}]\n{key}={old}", self._package) | 272 | raise BlueprintException(f"Found an invalid option in BlueprintSettings.ini,\n[{self._package}]\n{key}={old}", self._package) | ||
246 | #print(key, type(old), old, type(new), new) | 273 | #print(key, type(old), old, type(new), new) | ||
247 | setattr(self, key, new) | 274 | setattr(self, key, new) | ||
248 | return True | 275 | return True | ||
249 | 276 | | |||
250 | def setDefault(self, key : str, default) -> bool: | 277 | def setDefault(self, key : str, default) -> bool: | ||
251 | _instance = UserOptions.instance() | 278 | _instance = UserOptions.instance() | ||
252 | package = self._package | 279 | package = self._package | ||
253 | if key not in _instance.registeredOptions[package.path]: | 280 | if key not in _instance.registeredOptions[package.path]: | ||
254 | raise BlueprintException(f"Failed to set default for unregistered option: [{package}]{key}.", package) | 281 | raise BlueprintException(f"Failed to set default for unregistered option: [{package}]{key}.", package) | ||
255 | return False | 282 | return False | ||
256 | 283 | | |||
257 | settings = _instance.initPackage(self) | 284 | settings = _instance.initPackage(self) | ||
258 | _instance.registeredOptions[package.path][key] = default | 285 | _instance.registeredOptions[package.path][key].value = default | ||
259 | if key not in settings: | 286 | if key not in settings: | ||
260 | settings[key] = str(default) | 287 | settings[key] = str(default) | ||
261 | setattr(self, key, default) | 288 | setattr(self, key, default) | ||
262 | return True | 289 | return True | ||
263 | 290 | | |||
264 | 291 | | |||
265 | def __getattribute__(self, name): | 292 | def __getattribute__(self, name): | ||
266 | if name.startswith("_"): | 293 | if name.startswith("_"): | ||
Show All 12 Lines | |||||
279 | 306 | | |||
280 | out = None | 307 | out = None | ||
281 | _instance = UserOptions.instance() | 308 | _instance = UserOptions.instance() | ||
282 | _package = self._package | 309 | _package = self._package | ||
283 | _packagePath = _package.path | 310 | _packagePath = _package.path | ||
284 | if _packagePath in _instance.packageOptions and name in _instance.packageOptions[_packagePath]: | 311 | if _packagePath in _instance.packageOptions and name in _instance.packageOptions[_packagePath]: | ||
285 | if _packagePath not in _instance.registeredOptions or name not in _instance.registeredOptions[_packagePath]: | 312 | if _packagePath not in _instance.registeredOptions or name not in _instance.registeredOptions[_packagePath]: | ||
286 | raise BlueprintException(f"Package {_package} has no registered option {name}", _package) | 313 | raise BlueprintException(f"Package {_package} has no registered option {name}", _package) | ||
287 | out = self._convert(_instance.registeredOptions[_packagePath][name], _instance.packageOptions[_packagePath][name]) | 314 | out = self._convert(_instance.registeredOptions[_packagePath][name].value, _instance.packageOptions[_packagePath][name]) | ||
288 | elif member is not None: | 315 | elif member is not None: | ||
289 | # value is not overwritten by comand line options | 316 | # value is not overwritten by comand line options | ||
290 | return member | 317 | return member | ||
291 | else: | 318 | else: | ||
292 | parent = _package.parent | 319 | parent = _package.parent | ||
293 | if parent: | 320 | if parent: | ||
294 | out = getattr(UserOptions.get(parent), name) | 321 | out = getattr(UserOptions.get(parent), name) | ||
295 | 322 | | |||
296 | if not out: | 323 | if not out: | ||
297 | # name is a registered option and not a type but a default value | 324 | # name is a registered option and not a type but a default value | ||
298 | if _packagePath in _instance.registeredOptions and name in _instance.registeredOptions[_packagePath]: | 325 | if _packagePath in _instance.registeredOptions and name in _instance.registeredOptions[_packagePath]: | ||
299 | default = _instance.registeredOptions[_packagePath][name] | 326 | default = _instance.registeredOptions[_packagePath][name].value | ||
300 | if not callable(default): | 327 | if not callable(default): | ||
301 | out = default | 328 | out = default | ||
302 | 329 | | |||
303 | 330 | | |||
304 | # skip lookup in command line options and parent objects the enxt time | 331 | # skip lookup in command line options and parent objects the enxt time | ||
305 | _cache[name] = out | 332 | _cache[name] = out | ||
306 | #print(_packagePath, name, type(out), out) | 333 | #print(_packagePath, name, type(out), out) | ||
307 | return out | 334 | return out | ||
▲ Show 20 Lines • Show All 160 Lines • Show Last 20 Lines |