diff options
Diffstat (limited to 'bitbake/lib/bb/data_smart.py')
-rw-r--r-- | bitbake/lib/bb/data_smart.py | 371 |
1 files changed, 237 insertions, 134 deletions
diff --git a/bitbake/lib/bb/data_smart.py b/bitbake/lib/bb/data_smart.py index 2328c334ac..8e7dd98384 100644 --- a/bitbake/lib/bb/data_smart.py +++ b/bitbake/lib/bb/data_smart.py | |||
@@ -16,8 +16,11 @@ BitBake build tools. | |||
16 | # | 16 | # |
17 | # Based on functions from the base bb module, Copyright 2003 Holger Schurig | 17 | # Based on functions from the base bb module, Copyright 2003 Holger Schurig |
18 | 18 | ||
19 | import copy, re, sys, traceback | 19 | import builtins |
20 | from collections import MutableMapping | 20 | import copy |
21 | import re | ||
22 | import sys | ||
23 | from collections.abc import MutableMapping | ||
21 | import logging | 24 | import logging |
22 | import hashlib | 25 | import hashlib |
23 | import bb, bb.codeparser | 26 | import bb, bb.codeparser |
@@ -26,13 +29,25 @@ from bb.COW import COWDictBase | |||
26 | 29 | ||
27 | logger = logging.getLogger("BitBake.Data") | 30 | logger = logging.getLogger("BitBake.Data") |
28 | 31 | ||
29 | __setvar_keyword__ = ["_append", "_prepend", "_remove"] | 32 | __setvar_keyword__ = [":append", ":prepend", ":remove"] |
30 | __setvar_regexp__ = re.compile(r'(?P<base>.*?)(?P<keyword>_append|_prepend|_remove)(_(?P<add>[^A-Z]*))?$') | 33 | __setvar_regexp__ = re.compile(r'(?P<base>.*?)(?P<keyword>:append|:prepend|:remove)(:(?P<add>[^A-Z]*))?$') |
31 | __expand_var_regexp__ = re.compile(r"\${[a-zA-Z0-9\-_+./~]+?}") | 34 | __expand_var_regexp__ = re.compile(r"\${[a-zA-Z0-9\-_+./~:]+}") |
32 | __expand_python_regexp__ = re.compile(r"\${@.+?}") | 35 | __expand_python_regexp__ = re.compile(r"\${@(?:{.*?}|.)+?}") |
33 | __whitespace_split__ = re.compile(r'(\s)') | 36 | __whitespace_split__ = re.compile(r'(\s)') |
34 | __override_regexp__ = re.compile(r'[a-z0-9]+') | 37 | __override_regexp__ = re.compile(r'[a-z0-9]+') |
35 | 38 | ||
39 | bitbake_renamed_vars = { | ||
40 | "BB_ENV_WHITELIST": "BB_ENV_PASSTHROUGH", | ||
41 | "BB_ENV_EXTRAWHITE": "BB_ENV_PASSTHROUGH_ADDITIONS", | ||
42 | "BB_HASHBASE_WHITELIST": "BB_BASEHASH_IGNORE_VARS", | ||
43 | "BB_HASHCONFIG_WHITELIST": "BB_HASHCONFIG_IGNORE_VARS", | ||
44 | "BB_HASHTASK_WHITELIST": "BB_TASKHASH_IGNORE_TASKS", | ||
45 | "BB_SETSCENE_ENFORCE_WHITELIST": "BB_SETSCENE_ENFORCE_IGNORE_TASKS", | ||
46 | "MULTI_PROVIDER_WHITELIST": "BB_MULTI_PROVIDER_ALLOWED", | ||
47 | "BB_STAMP_WHITELIST": "is a deprecated variable and support has been removed", | ||
48 | "BB_STAMP_POLICY": "is a deprecated variable and support has been removed", | ||
49 | } | ||
50 | |||
36 | def infer_caller_details(loginfo, parent = False, varval = True): | 51 | def infer_caller_details(loginfo, parent = False, varval = True): |
37 | """Save the caller the trouble of specifying everything.""" | 52 | """Save the caller the trouble of specifying everything.""" |
38 | # Save effort. | 53 | # Save effort. |
@@ -80,68 +95,79 @@ def infer_caller_details(loginfo, parent = False, varval = True): | |||
80 | loginfo['func'] = func | 95 | loginfo['func'] = func |
81 | 96 | ||
82 | class VariableParse: | 97 | class VariableParse: |
83 | def __init__(self, varname, d, val = None): | 98 | def __init__(self, varname, d, unexpanded_value = None, val = None): |
84 | self.varname = varname | 99 | self.varname = varname |
85 | self.d = d | 100 | self.d = d |
86 | self.value = val | 101 | self.value = val |
102 | self.unexpanded_value = unexpanded_value | ||
87 | 103 | ||
88 | self.references = set() | 104 | self.references = set() |
89 | self.execs = set() | 105 | self.execs = set() |
90 | self.contains = {} | 106 | self.contains = {} |
91 | 107 | ||
92 | def var_sub(self, match): | 108 | def var_sub(self, match): |
93 | key = match.group()[2:-1] | 109 | key = match.group()[2:-1] |
94 | if self.varname and key: | 110 | if self.varname and key: |
95 | if self.varname == key: | 111 | if self.varname == key: |
96 | raise Exception("variable %s references itself!" % self.varname) | 112 | raise Exception("variable %s references itself!" % self.varname) |
97 | var = self.d.getVarFlag(key, "_content") | 113 | var = self.d.getVarFlag(key, "_content") |
98 | self.references.add(key) | 114 | self.references.add(key) |
99 | if var is not None: | 115 | if var is not None: |
100 | return var | 116 | return var |
101 | else: | 117 | else: |
102 | return match.group() | 118 | return match.group() |
103 | 119 | ||
104 | def python_sub(self, match): | 120 | def python_sub(self, match): |
105 | if isinstance(match, str): | 121 | if isinstance(match, str): |
106 | code = match | 122 | code = match |
107 | else: | 123 | else: |
108 | code = match.group()[3:-1] | 124 | code = match.group()[3:-1] |
109 | 125 | ||
110 | if self.varname: | 126 | # Do not run code that contains one or more unexpanded variables |
111 | varname = 'Var <%s>' % self.varname | 127 | # instead return the code with the characters we removed put back |
112 | else: | 128 | if __expand_var_regexp__.findall(code): |
113 | varname = '<expansion>' | 129 | return "${@" + code + "}" |
114 | codeobj = compile(code.strip(), varname, "eval") | ||
115 | |||
116 | parser = bb.codeparser.PythonParser(self.varname, logger) | ||
117 | parser.parse_python(code) | ||
118 | if self.varname: | ||
119 | vardeps = self.d.getVarFlag(self.varname, "vardeps") | ||
120 | if vardeps is None: | ||
121 | parser.log.flush() | ||
122 | else: | ||
123 | parser.log.flush() | ||
124 | self.references |= parser.references | ||
125 | self.execs |= parser.execs | ||
126 | 130 | ||
127 | for k in parser.contains: | 131 | if self.varname: |
128 | if k not in self.contains: | 132 | varname = 'Var <%s>' % self.varname |
129 | self.contains[k] = parser.contains[k].copy() | 133 | else: |
130 | else: | 134 | varname = '<expansion>' |
131 | self.contains[k].update(parser.contains[k]) | 135 | codeobj = compile(code.strip(), varname, "eval") |
132 | value = utils.better_eval(codeobj, DataContext(self.d), {'d' : self.d}) | 136 | |
133 | return str(value) | 137 | parser = bb.codeparser.PythonParser(self.varname, logger) |
138 | parser.parse_python(code) | ||
139 | if self.varname: | ||
140 | vardeps = self.d.getVarFlag(self.varname, "vardeps") | ||
141 | if vardeps is None: | ||
142 | parser.log.flush() | ||
143 | else: | ||
144 | parser.log.flush() | ||
145 | self.references |= parser.references | ||
146 | self.execs |= parser.execs | ||
134 | 147 | ||
148 | for k in parser.contains: | ||
149 | if k not in self.contains: | ||
150 | self.contains[k] = parser.contains[k].copy() | ||
151 | else: | ||
152 | self.contains[k].update(parser.contains[k]) | ||
153 | value = utils.better_eval(codeobj, DataContext(self.d), {'d' : self.d}) | ||
154 | return str(value) | ||
135 | 155 | ||
136 | class DataContext(dict): | 156 | class DataContext(dict): |
157 | excluded = set([i for i in dir(builtins) if not i.startswith('_')] + ['oe']) | ||
158 | |||
137 | def __init__(self, metadata, **kwargs): | 159 | def __init__(self, metadata, **kwargs): |
138 | self.metadata = metadata | 160 | self.metadata = metadata |
139 | dict.__init__(self, **kwargs) | 161 | dict.__init__(self, **kwargs) |
140 | self['d'] = metadata | 162 | self['d'] = metadata |
163 | self.context = set(bb.utils.get_context()) | ||
141 | 164 | ||
142 | def __missing__(self, key): | 165 | def __missing__(self, key): |
166 | if key in self.excluded or key in self.context: | ||
167 | raise KeyError(key) | ||
168 | |||
143 | value = self.metadata.getVar(key) | 169 | value = self.metadata.getVar(key) |
144 | if value is None or self.metadata.getVarFlag(key, 'func', False): | 170 | if value is None: |
145 | raise KeyError(key) | 171 | raise KeyError(key) |
146 | else: | 172 | else: |
147 | return value | 173 | return value |
@@ -151,6 +177,7 @@ class ExpansionError(Exception): | |||
151 | self.expression = expression | 177 | self.expression = expression |
152 | self.variablename = varname | 178 | self.variablename = varname |
153 | self.exception = exception | 179 | self.exception = exception |
180 | self.varlist = [varname or expression or ""] | ||
154 | if varname: | 181 | if varname: |
155 | if expression: | 182 | if expression: |
156 | self.msg = "Failure expanding variable %s, expression was %s which triggered exception %s: %s" % (varname, expression, type(exception).__name__, exception) | 183 | self.msg = "Failure expanding variable %s, expression was %s which triggered exception %s: %s" % (varname, expression, type(exception).__name__, exception) |
@@ -160,8 +187,14 @@ class ExpansionError(Exception): | |||
160 | self.msg = "Failure expanding expression %s which triggered exception %s: %s" % (expression, type(exception).__name__, exception) | 187 | self.msg = "Failure expanding expression %s which triggered exception %s: %s" % (expression, type(exception).__name__, exception) |
161 | Exception.__init__(self, self.msg) | 188 | Exception.__init__(self, self.msg) |
162 | self.args = (varname, expression, exception) | 189 | self.args = (varname, expression, exception) |
190 | |||
191 | def addVar(self, varname): | ||
192 | if varname: | ||
193 | self.varlist.append(varname) | ||
194 | |||
163 | def __str__(self): | 195 | def __str__(self): |
164 | return self.msg | 196 | chain = "\nThe variable dependency chain for the failure is: " + " -> ".join(self.varlist) |
197 | return self.msg + chain | ||
165 | 198 | ||
166 | class IncludeHistory(object): | 199 | class IncludeHistory(object): |
167 | def __init__(self, parent = None, filename = '[TOP LEVEL]'): | 200 | def __init__(self, parent = None, filename = '[TOP LEVEL]'): |
@@ -239,12 +272,9 @@ class VariableHistory(object): | |||
239 | return | 272 | return |
240 | if 'op' not in loginfo or not loginfo['op']: | 273 | if 'op' not in loginfo or not loginfo['op']: |
241 | loginfo['op'] = 'set' | 274 | loginfo['op'] = 'set' |
242 | if 'detail' in loginfo: | ||
243 | loginfo['detail'] = str(loginfo['detail']) | ||
244 | if 'variable' not in loginfo or 'file' not in loginfo: | 275 | if 'variable' not in loginfo or 'file' not in loginfo: |
245 | raise ValueError("record() missing variable or file.") | 276 | raise ValueError("record() missing variable or file.") |
246 | var = loginfo['variable'] | 277 | var = loginfo['variable'] |
247 | |||
248 | if var not in self.variables: | 278 | if var not in self.variables: |
249 | self.variables[var] = [] | 279 | self.variables[var] = [] |
250 | if not isinstance(self.variables[var], list): | 280 | if not isinstance(self.variables[var], list): |
@@ -277,7 +307,7 @@ class VariableHistory(object): | |||
277 | for (r, override) in d.overridedata[var]: | 307 | for (r, override) in d.overridedata[var]: |
278 | for event in self.variable(r): | 308 | for event in self.variable(r): |
279 | loginfo = event.copy() | 309 | loginfo = event.copy() |
280 | if 'flag' in loginfo and not loginfo['flag'].startswith("_"): | 310 | if 'flag' in loginfo and not loginfo['flag'].startswith(("_", ":")): |
281 | continue | 311 | continue |
282 | loginfo['variable'] = var | 312 | loginfo['variable'] = var |
283 | loginfo['op'] = 'override[%s]:%s' % (override, loginfo['op']) | 313 | loginfo['op'] = 'override[%s]:%s' % (override, loginfo['op']) |
@@ -303,7 +333,8 @@ class VariableHistory(object): | |||
303 | flag = '[%s] ' % (event['flag']) | 333 | flag = '[%s] ' % (event['flag']) |
304 | else: | 334 | else: |
305 | flag = '' | 335 | flag = '' |
306 | o.write("# %s %s:%s%s\n# %s\"%s\"\n" % (event['op'], event['file'], event['line'], display_func, flag, re.sub('\n', '\n# ', event['detail']))) | 336 | o.write("# %s %s:%s%s\n# %s\"%s\"\n" % \ |
337 | (event['op'], event['file'], event['line'], display_func, flag, re.sub('\n', '\n# ', str(event['detail'])))) | ||
307 | if len(history) > 1: | 338 | if len(history) > 1: |
308 | o.write("# pre-expansion value:\n") | 339 | o.write("# pre-expansion value:\n") |
309 | o.write('# "%s"\n' % (commentVal)) | 340 | o.write('# "%s"\n' % (commentVal)) |
@@ -329,6 +360,16 @@ class VariableHistory(object): | |||
329 | lines.append(line) | 360 | lines.append(line) |
330 | return lines | 361 | return lines |
331 | 362 | ||
363 | def get_variable_refs(self, var): | ||
364 | """Return a dict of file/line references""" | ||
365 | var_history = self.variable(var) | ||
366 | refs = {} | ||
367 | for event in var_history: | ||
368 | if event['file'] not in refs: | ||
369 | refs[event['file']] = [] | ||
370 | refs[event['file']].append(event['line']) | ||
371 | return refs | ||
372 | |||
332 | def get_variable_items_files(self, var): | 373 | def get_variable_items_files(self, var): |
333 | """ | 374 | """ |
334 | Use variable history to map items added to a list variable and | 375 | Use variable history to map items added to a list variable and |
@@ -342,12 +383,12 @@ class VariableHistory(object): | |||
342 | for event in history: | 383 | for event in history: |
343 | if 'flag' in event: | 384 | if 'flag' in event: |
344 | continue | 385 | continue |
345 | if event['op'] == '_remove': | 386 | if event['op'] == ':remove': |
346 | continue | 387 | continue |
347 | if isset and event['op'] == 'set?': | 388 | if isset and event['op'] == 'set?': |
348 | continue | 389 | continue |
349 | isset = True | 390 | isset = True |
350 | items = d.expand(event['detail']).split() | 391 | items = d.expand(str(event['detail'])).split() |
351 | for item in items: | 392 | for item in items: |
352 | # This is a little crude but is belt-and-braces to avoid us | 393 | # This is a little crude but is belt-and-braces to avoid us |
353 | # having to handle every possible operation type specifically | 394 | # having to handle every possible operation type specifically |
@@ -363,6 +404,23 @@ class VariableHistory(object): | |||
363 | else: | 404 | else: |
364 | self.variables[var] = [] | 405 | self.variables[var] = [] |
365 | 406 | ||
407 | def _print_rename_error(var, loginfo, renamedvars, fullvar=None): | ||
408 | info = "" | ||
409 | if "file" in loginfo: | ||
410 | info = " file: %s" % loginfo["file"] | ||
411 | if "line" in loginfo: | ||
412 | info += " line: %s" % loginfo["line"] | ||
413 | if fullvar and fullvar != var: | ||
414 | info += " referenced as: %s" % fullvar | ||
415 | if info: | ||
416 | info = " (%s)" % info.strip() | ||
417 | renameinfo = renamedvars[var] | ||
418 | if " " in renameinfo: | ||
419 | # A space signals a string to display instead of a rename | ||
420 | bb.erroronce('Variable %s %s%s' % (var, renameinfo, info)) | ||
421 | else: | ||
422 | bb.erroronce('Variable %s has been renamed to %s%s' % (var, renameinfo, info)) | ||
423 | |||
366 | class DataSmart(MutableMapping): | 424 | class DataSmart(MutableMapping): |
367 | def __init__(self): | 425 | def __init__(self): |
368 | self.dict = {} | 426 | self.dict = {} |
@@ -370,6 +428,8 @@ class DataSmart(MutableMapping): | |||
370 | self.inchistory = IncludeHistory() | 428 | self.inchistory = IncludeHistory() |
371 | self.varhistory = VariableHistory(self) | 429 | self.varhistory = VariableHistory(self) |
372 | self._tracking = False | 430 | self._tracking = False |
431 | self._var_renames = {} | ||
432 | self._var_renames.update(bitbake_renamed_vars) | ||
373 | 433 | ||
374 | self.expand_cache = {} | 434 | self.expand_cache = {} |
375 | 435 | ||
@@ -391,9 +451,9 @@ class DataSmart(MutableMapping): | |||
391 | def expandWithRefs(self, s, varname): | 451 | def expandWithRefs(self, s, varname): |
392 | 452 | ||
393 | if not isinstance(s, str): # sanity check | 453 | if not isinstance(s, str): # sanity check |
394 | return VariableParse(varname, self, s) | 454 | return VariableParse(varname, self, s, s) |
395 | 455 | ||
396 | varparse = VariableParse(varname, self) | 456 | varparse = VariableParse(varname, self, s) |
397 | 457 | ||
398 | while s.find('${') != -1: | 458 | while s.find('${') != -1: |
399 | olds = s | 459 | olds = s |
@@ -403,14 +463,17 @@ class DataSmart(MutableMapping): | |||
403 | s = __expand_python_regexp__.sub(varparse.python_sub, s) | 463 | s = __expand_python_regexp__.sub(varparse.python_sub, s) |
404 | except SyntaxError as e: | 464 | except SyntaxError as e: |
405 | # Likely unmatched brackets, just don't expand the expression | 465 | # Likely unmatched brackets, just don't expand the expression |
406 | if e.msg != "EOL while scanning string literal": | 466 | if e.msg != "EOL while scanning string literal" and not e.msg.startswith("unterminated string literal"): |
407 | raise | 467 | raise |
408 | if s == olds: | 468 | if s == olds: |
409 | break | 469 | break |
410 | except ExpansionError: | 470 | except ExpansionError as e: |
471 | e.addVar(varname) | ||
411 | raise | 472 | raise |
412 | except bb.parse.SkipRecipe: | 473 | except bb.parse.SkipRecipe: |
413 | raise | 474 | raise |
475 | except bb.BBHandledException: | ||
476 | raise | ||
414 | except Exception as exc: | 477 | except Exception as exc: |
415 | tb = sys.exc_info()[2] | 478 | tb = sys.exc_info()[2] |
416 | raise ExpansionError(varname, s, exc).with_traceback(tb) from exc | 479 | raise ExpansionError(varname, s, exc).with_traceback(tb) from exc |
@@ -422,24 +485,19 @@ class DataSmart(MutableMapping): | |||
422 | def expand(self, s, varname = None): | 485 | def expand(self, s, varname = None): |
423 | return self.expandWithRefs(s, varname).value | 486 | return self.expandWithRefs(s, varname).value |
424 | 487 | ||
425 | def finalize(self, parent = False): | ||
426 | return | ||
427 | |||
428 | def internal_finalize(self, parent = False): | ||
429 | """Performs final steps upon the datastore, including application of overrides""" | ||
430 | self.overrides = None | ||
431 | |||
432 | def need_overrides(self): | 488 | def need_overrides(self): |
433 | if self.overrides is not None: | 489 | if self.overrides is not None: |
434 | return | 490 | return |
435 | if self.inoverride: | 491 | if self.inoverride: |
436 | return | 492 | return |
493 | overrride_stack = [] | ||
437 | for count in range(5): | 494 | for count in range(5): |
438 | self.inoverride = True | 495 | self.inoverride = True |
439 | # Can end up here recursively so setup dummy values | 496 | # Can end up here recursively so setup dummy values |
440 | self.overrides = [] | 497 | self.overrides = [] |
441 | self.overridesset = set() | 498 | self.overridesset = set() |
442 | self.overrides = (self.getVar("OVERRIDES") or "").split(":") or [] | 499 | self.overrides = (self.getVar("OVERRIDES") or "").split(":") or [] |
500 | overrride_stack.append(self.overrides) | ||
443 | self.overridesset = set(self.overrides) | 501 | self.overridesset = set(self.overrides) |
444 | self.inoverride = False | 502 | self.inoverride = False |
445 | self.expand_cache = {} | 503 | self.expand_cache = {} |
@@ -449,7 +507,7 @@ class DataSmart(MutableMapping): | |||
449 | self.overrides = newoverrides | 507 | self.overrides = newoverrides |
450 | self.overridesset = set(self.overrides) | 508 | self.overridesset = set(self.overrides) |
451 | else: | 509 | else: |
452 | bb.fatal("Overrides could not be expanded into a stable state after 5 iterations, overrides must be being referenced by other overridden variables in some recursive fashion. Please provide your configuration to bitbake-devel so we can laugh, er, I mean try and understand how to make it work.") | 510 | bb.fatal("Overrides could not be expanded into a stable state after 5 iterations, overrides must be being referenced by other overridden variables in some recursive fashion. Please provide your configuration to bitbake-devel so we can laugh, er, I mean try and understand how to make it work. The list of failing override expansions: %s" % "\n".join(str(s) for s in overrride_stack)) |
453 | 511 | ||
454 | def initVar(self, var): | 512 | def initVar(self, var): |
455 | self.expand_cache = {} | 513 | self.expand_cache = {} |
@@ -460,27 +518,44 @@ class DataSmart(MutableMapping): | |||
460 | dest = self.dict | 518 | dest = self.dict |
461 | while dest: | 519 | while dest: |
462 | if var in dest: | 520 | if var in dest: |
463 | return dest[var], self.overridedata.get(var, None) | 521 | return dest[var] |
464 | 522 | ||
465 | if "_data" not in dest: | 523 | if "_data" not in dest: |
466 | break | 524 | break |
467 | dest = dest["_data"] | 525 | dest = dest["_data"] |
468 | return None, self.overridedata.get(var, None) | 526 | return None |
469 | 527 | ||
470 | def _makeShadowCopy(self, var): | 528 | def _makeShadowCopy(self, var): |
471 | if var in self.dict: | 529 | if var in self.dict: |
472 | return | 530 | return |
473 | 531 | ||
474 | local_var, _ = self._findVar(var) | 532 | local_var = self._findVar(var) |
475 | 533 | ||
476 | if local_var: | 534 | if local_var: |
477 | self.dict[var] = copy.copy(local_var) | 535 | self.dict[var] = copy.copy(local_var) |
478 | else: | 536 | else: |
479 | self.initVar(var) | 537 | self.initVar(var) |
480 | 538 | ||
539 | def hasOverrides(self, var): | ||
540 | return var in self.overridedata | ||
481 | 541 | ||
482 | def setVar(self, var, value, **loginfo): | 542 | def setVar(self, var, value, **loginfo): |
483 | #print("var=" + str(var) + " val=" + str(value)) | 543 | #print("var=" + str(var) + " val=" + str(value)) |
544 | |||
545 | if not var.startswith("__anon_") and ("_append" in var or "_prepend" in var or "_remove" in var): | ||
546 | info = "%s" % var | ||
547 | if "file" in loginfo: | ||
548 | info += " file: %s" % loginfo["file"] | ||
549 | if "line" in loginfo: | ||
550 | info += " line: %s" % loginfo["line"] | ||
551 | bb.fatal("Variable %s contains an operation using the old override syntax. Please convert this layer/metadata before attempting to use with a newer bitbake." % info) | ||
552 | |||
553 | shortvar = var.split(":", 1)[0] | ||
554 | if shortvar in self._var_renames: | ||
555 | _print_rename_error(shortvar, loginfo, self._var_renames, fullvar=var) | ||
556 | # Mark that we have seen a renamed variable | ||
557 | self.setVar("_FAILPARSINGERRORHANDLED", True) | ||
558 | |||
484 | self.expand_cache = {} | 559 | self.expand_cache = {} |
485 | parsing=False | 560 | parsing=False |
486 | if 'parsing' in loginfo: | 561 | if 'parsing' in loginfo: |
@@ -505,12 +580,10 @@ class DataSmart(MutableMapping): | |||
505 | else: | 580 | else: |
506 | loginfo['op'] = keyword | 581 | loginfo['op'] = keyword |
507 | self.varhistory.record(**loginfo) | 582 | self.varhistory.record(**loginfo) |
508 | # todo make sure keyword is not __doc__ or __module__ | ||
509 | # pay the cookie monster | 583 | # pay the cookie monster |
510 | 584 | ||
511 | # more cookies for the cookie monster | 585 | # more cookies for the cookie monster |
512 | if '_' in var: | 586 | self._setvar_update_overrides(base, **loginfo) |
513 | self._setvar_update_overrides(base, **loginfo) | ||
514 | 587 | ||
515 | if base in self.overridevars: | 588 | if base in self.overridevars: |
516 | self._setvar_update_overridevars(var, value) | 589 | self._setvar_update_overridevars(var, value) |
@@ -520,27 +593,27 @@ class DataSmart(MutableMapping): | |||
520 | self._makeShadowCopy(var) | 593 | self._makeShadowCopy(var) |
521 | 594 | ||
522 | if not parsing: | 595 | if not parsing: |
523 | if "_append" in self.dict[var]: | 596 | if ":append" in self.dict[var]: |
524 | del self.dict[var]["_append"] | 597 | del self.dict[var][":append"] |
525 | if "_prepend" in self.dict[var]: | 598 | if ":prepend" in self.dict[var]: |
526 | del self.dict[var]["_prepend"] | 599 | del self.dict[var][":prepend"] |
527 | if "_remove" in self.dict[var]: | 600 | if ":remove" in self.dict[var]: |
528 | del self.dict[var]["_remove"] | 601 | del self.dict[var][":remove"] |
529 | if var in self.overridedata: | 602 | if var in self.overridedata: |
530 | active = [] | 603 | active = [] |
531 | self.need_overrides() | 604 | self.need_overrides() |
532 | for (r, o) in self.overridedata[var]: | 605 | for (r, o) in self.overridedata[var]: |
533 | if o in self.overridesset: | 606 | if o in self.overridesset: |
534 | active.append(r) | 607 | active.append(r) |
535 | elif "_" in o: | 608 | elif ":" in o: |
536 | if set(o.split("_")).issubset(self.overridesset): | 609 | if set(o.split(":")).issubset(self.overridesset): |
537 | active.append(r) | 610 | active.append(r) |
538 | for a in active: | 611 | for a in active: |
539 | self.delVar(a) | 612 | self.delVar(a) |
540 | del self.overridedata[var] | 613 | del self.overridedata[var] |
541 | 614 | ||
542 | # more cookies for the cookie monster | 615 | # more cookies for the cookie monster |
543 | if '_' in var: | 616 | if ':' in var: |
544 | self._setvar_update_overrides(var, **loginfo) | 617 | self._setvar_update_overrides(var, **loginfo) |
545 | 618 | ||
546 | # setting var | 619 | # setting var |
@@ -562,12 +635,13 @@ class DataSmart(MutableMapping): | |||
562 | nextnew.update(vardata.references) | 635 | nextnew.update(vardata.references) |
563 | nextnew.update(vardata.contains.keys()) | 636 | nextnew.update(vardata.contains.keys()) |
564 | new = nextnew | 637 | new = nextnew |
565 | self.internal_finalize(True) | 638 | self.overrides = None |
639 | self.expand_cache = {} | ||
566 | 640 | ||
567 | def _setvar_update_overrides(self, var, **loginfo): | 641 | def _setvar_update_overrides(self, var, **loginfo): |
568 | # aka pay the cookie monster | 642 | # aka pay the cookie monster |
569 | override = var[var.rfind('_')+1:] | 643 | override = var[var.rfind(':')+1:] |
570 | shortvar = var[:var.rfind('_')] | 644 | shortvar = var[:var.rfind(':')] |
571 | while override and __override_regexp__.match(override): | 645 | while override and __override_regexp__.match(override): |
572 | if shortvar not in self.overridedata: | 646 | if shortvar not in self.overridedata: |
573 | self.overridedata[shortvar] = [] | 647 | self.overridedata[shortvar] = [] |
@@ -576,9 +650,9 @@ class DataSmart(MutableMapping): | |||
576 | self.overridedata[shortvar] = list(self.overridedata[shortvar]) | 650 | self.overridedata[shortvar] = list(self.overridedata[shortvar]) |
577 | self.overridedata[shortvar].append([var, override]) | 651 | self.overridedata[shortvar].append([var, override]) |
578 | override = None | 652 | override = None |
579 | if "_" in shortvar: | 653 | if ":" in shortvar: |
580 | override = var[shortvar.rfind('_')+1:] | 654 | override = var[shortvar.rfind(':')+1:] |
581 | shortvar = var[:shortvar.rfind('_')] | 655 | shortvar = var[:shortvar.rfind(':')] |
582 | if len(shortvar) == 0: | 656 | if len(shortvar) == 0: |
583 | override = None | 657 | override = None |
584 | 658 | ||
@@ -602,10 +676,11 @@ class DataSmart(MutableMapping): | |||
602 | self.varhistory.record(**loginfo) | 676 | self.varhistory.record(**loginfo) |
603 | self.setVar(newkey, val, ignore=True, parsing=True) | 677 | self.setVar(newkey, val, ignore=True, parsing=True) |
604 | 678 | ||
605 | for i in (__setvar_keyword__): | 679 | srcflags = self.getVarFlags(key, False, True) or {} |
606 | src = self.getVarFlag(key, i, False) | 680 | for i in srcflags: |
607 | if src is None: | 681 | if i not in (__setvar_keyword__): |
608 | continue | 682 | continue |
683 | src = srcflags[i] | ||
609 | 684 | ||
610 | dest = self.getVarFlag(newkey, i, False) or [] | 685 | dest = self.getVarFlag(newkey, i, False) or [] |
611 | dest.extend(src) | 686 | dest.extend(src) |
@@ -617,7 +692,7 @@ class DataSmart(MutableMapping): | |||
617 | self.overridedata[newkey].append([v.replace(key, newkey), o]) | 692 | self.overridedata[newkey].append([v.replace(key, newkey), o]) |
618 | self.renameVar(v, v.replace(key, newkey)) | 693 | self.renameVar(v, v.replace(key, newkey)) |
619 | 694 | ||
620 | if '_' in newkey and val is None: | 695 | if ':' in newkey and val is None: |
621 | self._setvar_update_overrides(newkey, **loginfo) | 696 | self._setvar_update_overrides(newkey, **loginfo) |
622 | 697 | ||
623 | loginfo['variable'] = key | 698 | loginfo['variable'] = key |
@@ -629,12 +704,12 @@ class DataSmart(MutableMapping): | |||
629 | def appendVar(self, var, value, **loginfo): | 704 | def appendVar(self, var, value, **loginfo): |
630 | loginfo['op'] = 'append' | 705 | loginfo['op'] = 'append' |
631 | self.varhistory.record(**loginfo) | 706 | self.varhistory.record(**loginfo) |
632 | self.setVar(var + "_append", value, ignore=True, parsing=True) | 707 | self.setVar(var + ":append", value, ignore=True, parsing=True) |
633 | 708 | ||
634 | def prependVar(self, var, value, **loginfo): | 709 | def prependVar(self, var, value, **loginfo): |
635 | loginfo['op'] = 'prepend' | 710 | loginfo['op'] = 'prepend' |
636 | self.varhistory.record(**loginfo) | 711 | self.varhistory.record(**loginfo) |
637 | self.setVar(var + "_prepend", value, ignore=True, parsing=True) | 712 | self.setVar(var + ":prepend", value, ignore=True, parsing=True) |
638 | 713 | ||
639 | def delVar(self, var, **loginfo): | 714 | def delVar(self, var, **loginfo): |
640 | self.expand_cache = {} | 715 | self.expand_cache = {} |
@@ -645,10 +720,10 @@ class DataSmart(MutableMapping): | |||
645 | self.dict[var] = {} | 720 | self.dict[var] = {} |
646 | if var in self.overridedata: | 721 | if var in self.overridedata: |
647 | del self.overridedata[var] | 722 | del self.overridedata[var] |
648 | if '_' in var: | 723 | if ':' in var: |
649 | override = var[var.rfind('_')+1:] | 724 | override = var[var.rfind(':')+1:] |
650 | shortvar = var[:var.rfind('_')] | 725 | shortvar = var[:var.rfind(':')] |
651 | while override and override.islower(): | 726 | while override and __override_regexp__.match(override): |
652 | try: | 727 | try: |
653 | if shortvar in self.overridedata: | 728 | if shortvar in self.overridedata: |
654 | # Force CoW by recreating the list first | 729 | # Force CoW by recreating the list first |
@@ -657,15 +732,23 @@ class DataSmart(MutableMapping): | |||
657 | except ValueError as e: | 732 | except ValueError as e: |
658 | pass | 733 | pass |
659 | override = None | 734 | override = None |
660 | if "_" in shortvar: | 735 | if ":" in shortvar: |
661 | override = var[shortvar.rfind('_')+1:] | 736 | override = var[shortvar.rfind(':')+1:] |
662 | shortvar = var[:shortvar.rfind('_')] | 737 | shortvar = var[:shortvar.rfind(':')] |
663 | if len(shortvar) == 0: | 738 | if len(shortvar) == 0: |
664 | override = None | 739 | override = None |
665 | 740 | ||
666 | def setVarFlag(self, var, flag, value, **loginfo): | 741 | def setVarFlag(self, var, flag, value, **loginfo): |
667 | self.expand_cache = {} | 742 | self.expand_cache = {} |
668 | 743 | ||
744 | if var == "BB_RENAMED_VARIABLES": | ||
745 | self._var_renames[flag] = value | ||
746 | |||
747 | if var in self._var_renames: | ||
748 | _print_rename_error(var, loginfo, self._var_renames) | ||
749 | # Mark that we have seen a renamed variable | ||
750 | self.setVar("_FAILPARSINGERRORHANDLED", True) | ||
751 | |||
669 | if 'op' not in loginfo: | 752 | if 'op' not in loginfo: |
670 | loginfo['op'] = "set" | 753 | loginfo['op'] = "set" |
671 | loginfo['flag'] = flag | 754 | loginfo['flag'] = flag |
@@ -674,7 +757,7 @@ class DataSmart(MutableMapping): | |||
674 | self._makeShadowCopy(var) | 757 | self._makeShadowCopy(var) |
675 | self.dict[var][flag] = value | 758 | self.dict[var][flag] = value |
676 | 759 | ||
677 | if flag == "_defaultval" and '_' in var: | 760 | if flag == "_defaultval" and ':' in var: |
678 | self._setvar_update_overrides(var, **loginfo) | 761 | self._setvar_update_overrides(var, **loginfo) |
679 | if flag == "_defaultval" and var in self.overridevars: | 762 | if flag == "_defaultval" and var in self.overridevars: |
680 | self._setvar_update_overridevars(var, value) | 763 | self._setvar_update_overridevars(var, value) |
@@ -695,22 +778,27 @@ class DataSmart(MutableMapping): | |||
695 | return None | 778 | return None |
696 | cachename = var + "[" + flag + "]" | 779 | cachename = var + "[" + flag + "]" |
697 | 780 | ||
781 | if not expand and retparser and cachename in self.expand_cache: | ||
782 | return self.expand_cache[cachename].unexpanded_value, self.expand_cache[cachename] | ||
783 | |||
698 | if expand and cachename in self.expand_cache: | 784 | if expand and cachename in self.expand_cache: |
699 | return self.expand_cache[cachename].value | 785 | return self.expand_cache[cachename].value |
700 | 786 | ||
701 | local_var, overridedata = self._findVar(var) | 787 | local_var = self._findVar(var) |
702 | value = None | 788 | value = None |
703 | removes = set() | 789 | removes = set() |
704 | if flag == "_content" and overridedata is not None and not parsing: | 790 | if flag == "_content" and not parsing: |
791 | overridedata = self.overridedata.get(var, None) | ||
792 | if flag == "_content" and not parsing and overridedata is not None: | ||
705 | match = False | 793 | match = False |
706 | active = {} | 794 | active = {} |
707 | self.need_overrides() | 795 | self.need_overrides() |
708 | for (r, o) in overridedata: | 796 | for (r, o) in overridedata: |
709 | # What about double overrides both with "_" in the name? | 797 | # FIXME What about double overrides both with "_" in the name? |
710 | if o in self.overridesset: | 798 | if o in self.overridesset: |
711 | active[o] = r | 799 | active[o] = r |
712 | elif "_" in o: | 800 | elif ":" in o: |
713 | if set(o.split("_")).issubset(self.overridesset): | 801 | if set(o.split(":")).issubset(self.overridesset): |
714 | active[o] = r | 802 | active[o] = r |
715 | 803 | ||
716 | mod = True | 804 | mod = True |
@@ -718,10 +806,10 @@ class DataSmart(MutableMapping): | |||
718 | mod = False | 806 | mod = False |
719 | for o in self.overrides: | 807 | for o in self.overrides: |
720 | for a in active.copy(): | 808 | for a in active.copy(): |
721 | if a.endswith("_" + o): | 809 | if a.endswith(":" + o): |
722 | t = active[a] | 810 | t = active[a] |
723 | del active[a] | 811 | del active[a] |
724 | active[a.replace("_" + o, "")] = t | 812 | active[a.replace(":" + o, "")] = t |
725 | mod = True | 813 | mod = True |
726 | elif a == o: | 814 | elif a == o: |
727 | match = active[a] | 815 | match = active[a] |
@@ -738,33 +826,35 @@ class DataSmart(MutableMapping): | |||
738 | value = copy.copy(local_var[flag]) | 826 | value = copy.copy(local_var[flag]) |
739 | elif flag == "_content" and "_defaultval" in local_var and not noweakdefault: | 827 | elif flag == "_content" and "_defaultval" in local_var and not noweakdefault: |
740 | value = copy.copy(local_var["_defaultval"]) | 828 | value = copy.copy(local_var["_defaultval"]) |
829 | elif "_defaultval_flag_"+flag in local_var and not noweakdefault: | ||
830 | value = copy.copy(local_var["_defaultval_flag_"+flag]) | ||
741 | 831 | ||
742 | 832 | ||
743 | if flag == "_content" and local_var is not None and "_append" in local_var and not parsing: | 833 | if flag == "_content" and local_var is not None and ":append" in local_var and not parsing: |
744 | if not value: | ||
745 | value = "" | ||
746 | self.need_overrides() | 834 | self.need_overrides() |
747 | for (r, o) in local_var["_append"]: | 835 | for (r, o) in local_var[":append"]: |
748 | match = True | 836 | match = True |
749 | if o: | 837 | if o: |
750 | for o2 in o.split("_"): | 838 | for o2 in o.split(":"): |
751 | if not o2 in self.overrides: | 839 | if not o2 in self.overrides: |
752 | match = False | 840 | match = False |
753 | if match: | 841 | if match: |
842 | if value is None: | ||
843 | value = "" | ||
754 | value = value + r | 844 | value = value + r |
755 | 845 | ||
756 | if flag == "_content" and local_var is not None and "_prepend" in local_var and not parsing: | 846 | if flag == "_content" and local_var is not None and ":prepend" in local_var and not parsing: |
757 | if not value: | ||
758 | value = "" | ||
759 | self.need_overrides() | 847 | self.need_overrides() |
760 | for (r, o) in local_var["_prepend"]: | 848 | for (r, o) in local_var[":prepend"]: |
761 | 849 | ||
762 | match = True | 850 | match = True |
763 | if o: | 851 | if o: |
764 | for o2 in o.split("_"): | 852 | for o2 in o.split(":"): |
765 | if not o2 in self.overrides: | 853 | if not o2 in self.overrides: |
766 | match = False | 854 | match = False |
767 | if match: | 855 | if match: |
856 | if value is None: | ||
857 | value = "" | ||
768 | value = r + value | 858 | value = r + value |
769 | 859 | ||
770 | parser = None | 860 | parser = None |
@@ -773,12 +863,12 @@ class DataSmart(MutableMapping): | |||
773 | if expand: | 863 | if expand: |
774 | value = parser.value | 864 | value = parser.value |
775 | 865 | ||
776 | if value and flag == "_content" and local_var is not None and "_remove" in local_var and not parsing: | 866 | if value and flag == "_content" and local_var is not None and ":remove" in local_var and not parsing: |
777 | self.need_overrides() | 867 | self.need_overrides() |
778 | for (r, o) in local_var["_remove"]: | 868 | for (r, o) in local_var[":remove"]: |
779 | match = True | 869 | match = True |
780 | if o: | 870 | if o: |
781 | for o2 in o.split("_"): | 871 | for o2 in o.split(":"): |
782 | if not o2 in self.overrides: | 872 | if not o2 in self.overrides: |
783 | match = False | 873 | match = False |
784 | if match: | 874 | if match: |
@@ -791,7 +881,7 @@ class DataSmart(MutableMapping): | |||
791 | expanded_removes[r] = self.expand(r).split() | 881 | expanded_removes[r] = self.expand(r).split() |
792 | 882 | ||
793 | parser.removes = set() | 883 | parser.removes = set() |
794 | val = "" | 884 | val = [] |
795 | for v in __whitespace_split__.split(parser.value): | 885 | for v in __whitespace_split__.split(parser.value): |
796 | skip = False | 886 | skip = False |
797 | for r in removes: | 887 | for r in removes: |
@@ -800,8 +890,8 @@ class DataSmart(MutableMapping): | |||
800 | skip = True | 890 | skip = True |
801 | if skip: | 891 | if skip: |
802 | continue | 892 | continue |
803 | val = val + v | 893 | val.append(v) |
804 | parser.value = val | 894 | parser.value = "".join(val) |
805 | if expand: | 895 | if expand: |
806 | value = parser.value | 896 | value = parser.value |
807 | 897 | ||
@@ -816,7 +906,7 @@ class DataSmart(MutableMapping): | |||
816 | def delVarFlag(self, var, flag, **loginfo): | 906 | def delVarFlag(self, var, flag, **loginfo): |
817 | self.expand_cache = {} | 907 | self.expand_cache = {} |
818 | 908 | ||
819 | local_var, _ = self._findVar(var) | 909 | local_var = self._findVar(var) |
820 | if not local_var: | 910 | if not local_var: |
821 | return | 911 | return |
822 | if not var in self.dict: | 912 | if not var in self.dict: |
@@ -829,6 +919,8 @@ class DataSmart(MutableMapping): | |||
829 | self.varhistory.record(**loginfo) | 919 | self.varhistory.record(**loginfo) |
830 | 920 | ||
831 | del self.dict[var][flag] | 921 | del self.dict[var][flag] |
922 | if ("_defaultval_flag_" + flag) in self.dict[var]: | ||
923 | del self.dict[var]["_defaultval_flag_" + flag] | ||
832 | 924 | ||
833 | def appendVarFlag(self, var, flag, value, **loginfo): | 925 | def appendVarFlag(self, var, flag, value, **loginfo): |
834 | loginfo['op'] = 'append' | 926 | loginfo['op'] = 'append' |
@@ -859,21 +951,26 @@ class DataSmart(MutableMapping): | |||
859 | self.dict[var][i] = flags[i] | 951 | self.dict[var][i] = flags[i] |
860 | 952 | ||
861 | def getVarFlags(self, var, expand = False, internalflags=False): | 953 | def getVarFlags(self, var, expand = False, internalflags=False): |
862 | local_var, _ = self._findVar(var) | 954 | local_var = self._findVar(var) |
863 | flags = {} | 955 | flags = {} |
864 | 956 | ||
865 | if local_var: | 957 | if local_var: |
866 | for i in local_var: | 958 | for i, val in local_var.items(): |
867 | if i.startswith("_") and not internalflags: | 959 | if i.startswith("_defaultval_flag_") and not internalflags: |
960 | i = i[len("_defaultval_flag_"):] | ||
961 | if i not in local_var: | ||
962 | flags[i] = val | ||
963 | elif i.startswith(("_", ":")) and not internalflags: | ||
868 | continue | 964 | continue |
869 | flags[i] = local_var[i] | 965 | else: |
966 | flags[i] = val | ||
967 | |||
870 | if expand and i in expand: | 968 | if expand and i in expand: |
871 | flags[i] = self.expand(flags[i], var + "[" + i + "]") | 969 | flags[i] = self.expand(flags[i], var + "[" + i + "]") |
872 | if len(flags) == 0: | 970 | if len(flags) == 0: |
873 | return None | 971 | return None |
874 | return flags | 972 | return flags |
875 | 973 | ||
876 | |||
877 | def delVarFlags(self, var, **loginfo): | 974 | def delVarFlags(self, var, **loginfo): |
878 | self.expand_cache = {} | 975 | self.expand_cache = {} |
879 | if not var in self.dict: | 976 | if not var in self.dict: |
@@ -905,6 +1002,7 @@ class DataSmart(MutableMapping): | |||
905 | data.inchistory = self.inchistory.copy() | 1002 | data.inchistory = self.inchistory.copy() |
906 | 1003 | ||
907 | data._tracking = self._tracking | 1004 | data._tracking = self._tracking |
1005 | data._var_renames = self._var_renames | ||
908 | 1006 | ||
909 | data.overrides = None | 1007 | data.overrides = None |
910 | data.overridevars = copy.copy(self.overridevars) | 1008 | data.overridevars = copy.copy(self.overridevars) |
@@ -927,7 +1025,7 @@ class DataSmart(MutableMapping): | |||
927 | value = self.getVar(variable, False) | 1025 | value = self.getVar(variable, False) |
928 | for key in keys: | 1026 | for key in keys: |
929 | referrervalue = self.getVar(key, False) | 1027 | referrervalue = self.getVar(key, False) |
930 | if referrervalue and ref in referrervalue: | 1028 | if referrervalue and isinstance(referrervalue, str) and ref in referrervalue: |
931 | self.setVar(key, referrervalue.replace(ref, value)) | 1029 | self.setVar(key, referrervalue.replace(ref, value)) |
932 | 1030 | ||
933 | def localkeys(self): | 1031 | def localkeys(self): |
@@ -962,8 +1060,8 @@ class DataSmart(MutableMapping): | |||
962 | for (r, o) in self.overridedata[var]: | 1060 | for (r, o) in self.overridedata[var]: |
963 | if o in self.overridesset: | 1061 | if o in self.overridesset: |
964 | overrides.add(var) | 1062 | overrides.add(var) |
965 | elif "_" in o: | 1063 | elif ":" in o: |
966 | if set(o.split("_")).issubset(self.overridesset): | 1064 | if set(o.split(":")).issubset(self.overridesset): |
967 | overrides.add(var) | 1065 | overrides.add(var) |
968 | 1066 | ||
969 | for k in keylist(self.dict): | 1067 | for k in keylist(self.dict): |
@@ -993,10 +1091,10 @@ class DataSmart(MutableMapping): | |||
993 | d = self.createCopy() | 1091 | d = self.createCopy() |
994 | bb.data.expandKeys(d) | 1092 | bb.data.expandKeys(d) |
995 | 1093 | ||
996 | config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST") or "").split()) | 1094 | config_ignore_vars = set((d.getVar("BB_HASHCONFIG_IGNORE_VARS") or "").split()) |
997 | keys = set(key for key in iter(d) if not key.startswith("__")) | 1095 | keys = set(key for key in iter(d) if not key.startswith("__")) |
998 | for key in keys: | 1096 | for key in keys: |
999 | if key in config_whitelist: | 1097 | if key in config_ignore_vars: |
1000 | continue | 1098 | continue |
1001 | 1099 | ||
1002 | value = d.getVar(key, False) or "" | 1100 | value = d.getVar(key, False) or "" |
@@ -1022,5 +1120,10 @@ class DataSmart(MutableMapping): | |||
1022 | value = d.getVar(i, False) or "" | 1120 | value = d.getVar(i, False) or "" |
1023 | data.update({i:value}) | 1121 | data.update({i:value}) |
1024 | 1122 | ||
1123 | moddeps = bb.codeparser.modulecode_deps | ||
1124 | for dep in sorted(moddeps): | ||
1125 | # Ignore visitor code, sort sets | ||
1126 | data.update({'moddep[%s]' % dep : [sorted(moddeps[dep][0]), sorted(moddeps[dep][1]), sorted(moddeps[dep][2]), sorted(moddeps[dep][3]), moddeps[dep][4]]}) | ||
1127 | |||
1025 | data_str = str([(k, data[k]) for k in sorted(data.keys())]) | 1128 | data_str = str([(k, data[k]) for k in sorted(data.keys())]) |
1026 | return hashlib.sha256(data_str.encode("utf-8")).hexdigest() | 1129 | return hashlib.sha256(data_str.encode("utf-8")).hexdigest() |