blob: 8d3825f39884745a558dc4ba1e41d2541bc00b8a [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake Smart Dictionary Implementation
3
4Functions for interacting with the data structure used by the
5BitBake build tools.
6
7"""
8
9# Copyright (C) 2003, 2004 Chris Larson
10# Copyright (C) 2004, 2005 Seb Frankengul
11# Copyright (C) 2005, 2006 Holger Hans Peter Freyther
12# Copyright (C) 2005 Uli Luckas
13# Copyright (C) 2005 ROAD GmbH
14#
Brad Bishopc342db32019-05-15 21:57:59 -040015# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050017# Based on functions from the base bb module, Copyright 2003 Holger Schurig
18
19import copy, re, sys, traceback
Andrew Geissler5199d832021-09-24 16:47:35 -050020from collections.abc import MutableMapping
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021import logging
22import hashlib
23import bb, bb.codeparser
24from bb import utils
25from bb.COW import COWDictBase
26
27logger = logging.getLogger("BitBake.Data")
28
Patrick Williams213cb262021-08-07 19:21:33 -050029__setvar_keyword__ = [":append", ":prepend", ":remove"]
30__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\-_+./~:]+?}")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032__expand_python_regexp__ = re.compile(r"\${@.+?}")
Brad Bishop19323692019-04-05 15:28:33 -040033__whitespace_split__ = re.compile(r'(\s)')
34__override_regexp__ = re.compile(r'[a-z0-9]+')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050035
Andrew Geissler7e0e3c02022-02-25 20:34:39 +000036bitbake_renamed_vars = {
37 "BB_ENV_WHITELIST": "BB_ENV_PASSTHROUGH",
38 "BB_ENV_EXTRAWHITE": "BB_ENV_PASSTHROUGH_ADDITIONS",
39 "BB_HASHBASE_WHITELIST": "BB_BASEHASH_IGNORE_VARS",
40 "BB_HASHCONFIG_WHITELIST": "BB_HASHCONFIG_IGNORE_VARS",
41 "BB_HASHTASK_WHITELIST": "BB_TASKHASH_IGNORE_TASKS",
42 "BB_SETSCENE_ENFORCE_WHITELIST": "BB_SETSCENE_ENFORCE_IGNORE_TASKS",
43 "MULTI_PROVIDER_WHITELIST": "BB_MULTI_PROVIDER_ALLOWED",
44 "BB_STAMP_WHITELIST": "is a deprecated variable and support has been removed",
45 "BB_STAMP_POLICY": "is a deprecated variable and support has been removed",
46}
47
Patrick Williamsc124f4f2015-09-15 14:41:29 -050048def infer_caller_details(loginfo, parent = False, varval = True):
49 """Save the caller the trouble of specifying everything."""
50 # Save effort.
51 if 'ignore' in loginfo and loginfo['ignore']:
52 return
53 # If nothing was provided, mark this as possibly unneeded.
54 if not loginfo:
55 loginfo['ignore'] = True
56 return
57 # Infer caller's likely values for variable (var) and value (value),
58 # to reduce clutter in the rest of the code.
59 above = None
60 def set_above():
61 try:
62 raise Exception
63 except Exception:
64 tb = sys.exc_info()[2]
65 if parent:
66 return tb.tb_frame.f_back.f_back.f_back
67 else:
68 return tb.tb_frame.f_back.f_back
69
70 if varval and ('variable' not in loginfo or 'detail' not in loginfo):
71 if not above:
72 above = set_above()
73 lcls = above.f_locals.items()
74 for k, v in lcls:
75 if k == 'value' and 'detail' not in loginfo:
76 loginfo['detail'] = v
77 if k == 'var' and 'variable' not in loginfo:
78 loginfo['variable'] = v
79 # Infer file/line/function from traceback
80 # Don't use traceback.extract_stack() since it fills the line contents which
81 # we don't need and that hits stat syscalls
82 if 'file' not in loginfo:
83 if not above:
84 above = set_above()
85 f = above.f_back
86 line = f.f_lineno
87 file = f.f_code.co_filename
88 func = f.f_code.co_name
89 loginfo['file'] = file
90 loginfo['line'] = line
91 if func not in loginfo:
92 loginfo['func'] = func
93
94class VariableParse:
95 def __init__(self, varname, d, val = None):
96 self.varname = varname
97 self.d = d
98 self.value = val
99
100 self.references = set()
101 self.execs = set()
102 self.contains = {}
103
104 def var_sub(self, match):
105 key = match.group()[2:-1]
106 if self.varname and key:
107 if self.varname == key:
108 raise Exception("variable %s references itself!" % self.varname)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800109 var = self.d.getVarFlag(key, "_content")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500110 self.references.add(key)
111 if var is not None:
112 return var
113 else:
114 return match.group()
115
116 def python_sub(self, match):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500117 if isinstance(match, str):
118 code = match
119 else:
120 code = match.group()[3:-1]
121
Brad Bishop19323692019-04-05 15:28:33 -0400122 if self.varname:
123 varname = 'Var <%s>' % self.varname
124 else:
125 varname = '<expansion>'
126 codeobj = compile(code.strip(), varname, "eval")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127
128 parser = bb.codeparser.PythonParser(self.varname, logger)
129 parser.parse_python(code)
130 if self.varname:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500131 vardeps = self.d.getVarFlag(self.varname, "vardeps")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500132 if vardeps is None:
133 parser.log.flush()
134 else:
135 parser.log.flush()
136 self.references |= parser.references
137 self.execs |= parser.execs
138
139 for k in parser.contains:
140 if k not in self.contains:
141 self.contains[k] = parser.contains[k].copy()
142 else:
143 self.contains[k].update(parser.contains[k])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600144 value = utils.better_eval(codeobj, DataContext(self.d), {'d' : self.d})
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500145 return str(value)
146
147
148class DataContext(dict):
149 def __init__(self, metadata, **kwargs):
150 self.metadata = metadata
151 dict.__init__(self, **kwargs)
152 self['d'] = metadata
153
154 def __missing__(self, key):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500155 value = self.metadata.getVar(key)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500156 if value is None or self.metadata.getVarFlag(key, 'func', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500157 raise KeyError(key)
158 else:
159 return value
160
161class ExpansionError(Exception):
162 def __init__(self, varname, expression, exception):
163 self.expression = expression
164 self.variablename = varname
165 self.exception = exception
Andrew Geissler5199d832021-09-24 16:47:35 -0500166 self.varlist = [varname or expression or ""]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500167 if varname:
168 if expression:
169 self.msg = "Failure expanding variable %s, expression was %s which triggered exception %s: %s" % (varname, expression, type(exception).__name__, exception)
170 else:
171 self.msg = "Failure expanding variable %s: %s: %s" % (varname, type(exception).__name__, exception)
172 else:
173 self.msg = "Failure expanding expression %s which triggered exception %s: %s" % (expression, type(exception).__name__, exception)
174 Exception.__init__(self, self.msg)
175 self.args = (varname, expression, exception)
Andrew Geissler5199d832021-09-24 16:47:35 -0500176
177 def addVar(self, varname):
178 if varname:
179 self.varlist.append(varname)
180
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500181 def __str__(self):
Andrew Geissler5199d832021-09-24 16:47:35 -0500182 chain = "\nThe variable dependency chain for the failure is: " + " -> ".join(self.varlist)
183 return self.msg + chain
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500184
185class IncludeHistory(object):
186 def __init__(self, parent = None, filename = '[TOP LEVEL]'):
187 self.parent = parent
188 self.filename = filename
189 self.children = []
190 self.current = self
191
192 def copy(self):
193 new = IncludeHistory(self.parent, self.filename)
194 for c in self.children:
195 new.children.append(c)
196 return new
197
198 def include(self, filename):
199 newfile = IncludeHistory(self.current, filename)
200 self.current.children.append(newfile)
201 self.current = newfile
202 return self
203
204 def __enter__(self):
205 pass
206
207 def __exit__(self, a, b, c):
208 if self.current.parent:
209 self.current = self.current.parent
210 else:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500211 bb.warn("Include log: Tried to finish '%s' at top level." % self.filename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500212 return False
213
214 def emit(self, o, level = 0):
215 """Emit an include history file, and its children."""
216 if level:
217 spaces = " " * (level - 1)
218 o.write("# %s%s" % (spaces, self.filename))
219 if len(self.children) > 0:
220 o.write(" includes:")
221 else:
222 o.write("#\n# INCLUDE HISTORY:\n#")
223 level = level + 1
224 for child in self.children:
225 o.write("\n")
226 child.emit(o, level)
227
228class VariableHistory(object):
229 def __init__(self, dataroot):
230 self.dataroot = dataroot
231 self.variables = COWDictBase.copy()
232
233 def copy(self):
234 new = VariableHistory(self.dataroot)
235 new.variables = self.variables.copy()
236 return new
237
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500238 def __getstate__(self):
239 vardict = {}
240 for k, v in self.variables.iteritems():
241 vardict[k] = v
242 return {'dataroot': self.dataroot,
243 'variables': vardict}
244
245 def __setstate__(self, state):
246 self.dataroot = state['dataroot']
247 self.variables = COWDictBase.copy()
248 for k, v in state['variables'].items():
249 self.variables[k] = v
250
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500251 def record(self, *kwonly, **loginfo):
252 if not self.dataroot._tracking:
253 return
254 if len(kwonly) > 0:
255 raise TypeError
256 infer_caller_details(loginfo, parent = True)
257 if 'ignore' in loginfo and loginfo['ignore']:
258 return
259 if 'op' not in loginfo or not loginfo['op']:
260 loginfo['op'] = 'set'
261 if 'detail' in loginfo:
262 loginfo['detail'] = str(loginfo['detail'])
263 if 'variable' not in loginfo or 'file' not in loginfo:
264 raise ValueError("record() missing variable or file.")
265 var = loginfo['variable']
266
267 if var not in self.variables:
268 self.variables[var] = []
269 if not isinstance(self.variables[var], list):
270 return
271 if 'nodups' in loginfo and loginfo in self.variables[var]:
272 return
273 self.variables[var].append(loginfo.copy())
274
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800275 def rename_variable_hist(self, oldvar, newvar):
276 if not self.dataroot._tracking:
277 return
278 if oldvar not in self.variables:
279 return
280 if newvar not in self.variables:
281 self.variables[newvar] = []
282 for i in self.variables[oldvar]:
283 self.variables[newvar].append(i.copy())
284
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285 def variable(self, var):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500286 varhistory = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500287 if var in self.variables:
288 varhistory.extend(self.variables[var])
289 return varhistory
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500290
291 def emit(self, var, oval, val, o, d):
292 history = self.variable(var)
293
294 # Append override history
295 if var in d.overridedata:
296 for (r, override) in d.overridedata[var]:
297 for event in self.variable(r):
298 loginfo = event.copy()
Patrick Williams213cb262021-08-07 19:21:33 -0500299 if 'flag' in loginfo and not loginfo['flag'].startswith(("_", ":")):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500300 continue
301 loginfo['variable'] = var
302 loginfo['op'] = 'override[%s]:%s' % (override, loginfo['op'])
303 history.append(loginfo)
304
305 commentVal = re.sub('\n', '\n#', str(oval))
306 if history:
307 if len(history) == 1:
308 o.write("#\n# $%s\n" % var)
309 else:
310 o.write("#\n# $%s [%d operations]\n" % (var, len(history)))
311 for event in history:
312 # o.write("# %s\n" % str(event))
313 if 'func' in event:
314 # If we have a function listed, this is internal
315 # code, not an operation in a config file, and the
316 # full path is distracting.
317 event['file'] = re.sub('.*/', '', event['file'])
318 display_func = ' [%s]' % event['func']
319 else:
320 display_func = ''
321 if 'flag' in event:
322 flag = '[%s] ' % (event['flag'])
323 else:
324 flag = ''
325 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'])))
326 if len(history) > 1:
327 o.write("# pre-expansion value:\n")
328 o.write('# "%s"\n' % (commentVal))
329 else:
330 o.write("#\n# $%s\n# [no history recorded]\n#\n" % var)
331 o.write('# "%s"\n' % (commentVal))
332
333 def get_variable_files(self, var):
334 """Get the files where operations are made on a variable"""
335 var_history = self.variable(var)
336 files = []
337 for event in var_history:
338 files.append(event['file'])
339 return files
340
341 def get_variable_lines(self, var, f):
342 """Get the line where a operation is made on a variable in file f"""
343 var_history = self.variable(var)
344 lines = []
345 for event in var_history:
346 if f== event['file']:
347 line = event['line']
348 lines.append(line)
349 return lines
350
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000351 def get_variable_refs(self, var):
352 """Return a dict of file/line references"""
353 var_history = self.variable(var)
354 refs = {}
355 for event in var_history:
356 if event['file'] not in refs:
357 refs[event['file']] = []
358 refs[event['file']].append(event['line'])
359 return refs
360
Andrew Geissler82c905d2020-04-13 13:39:40 -0500361 def get_variable_items_files(self, var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500362 """
363 Use variable history to map items added to a list variable and
364 the files in which they were added.
365 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500366 d = self.dataroot
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367 history = self.variable(var)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500368 finalitems = (d.getVar(var) or '').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369 filemap = {}
370 isset = False
371 for event in history:
372 if 'flag' in event:
373 continue
Patrick Williams213cb262021-08-07 19:21:33 -0500374 if event['op'] == ':remove':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500375 continue
376 if isset and event['op'] == 'set?':
377 continue
378 isset = True
379 items = d.expand(event['detail']).split()
380 for item in items:
381 # This is a little crude but is belt-and-braces to avoid us
382 # having to handle every possible operation type specifically
383 if item in finalitems and not item in filemap:
384 filemap[item] = event['file']
385 return filemap
386
387 def del_var_history(self, var, f=None, line=None):
388 """If file f and line are not given, the entire history of var is deleted"""
389 if var in self.variables:
390 if f and line:
391 self.variables[var] = [ x for x in self.variables[var] if x['file']!=f and x['line']!=line]
392 else:
393 self.variables[var] = []
394
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000395def _print_rename_error(var, loginfo, renamedvars, fullvar=None):
396 info = ""
397 if "file" in loginfo:
398 info = " file: %s" % loginfo["file"]
399 if "line" in loginfo:
400 info += " line: %s" % loginfo["line"]
401 if fullvar and fullvar != var:
402 info += " referenced as: %s" % fullvar
403 if info:
404 info = " (%s)" % info.strip()
405 renameinfo = renamedvars[var]
406 if " " in renameinfo:
407 # A space signals a string to display instead of a rename
408 bb.erroronce('Variable %s %s%s' % (var, renameinfo, info))
409 else:
410 bb.erroronce('Variable %s has been renamed to %s%s' % (var, renameinfo, info))
411
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500412class DataSmart(MutableMapping):
413 def __init__(self):
414 self.dict = {}
415
416 self.inchistory = IncludeHistory()
417 self.varhistory = VariableHistory(self)
418 self._tracking = False
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000419 self._var_renames = {}
420 self._var_renames.update(bitbake_renamed_vars)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500421
422 self.expand_cache = {}
423
424 # cookie monster tribute
425 # Need to be careful about writes to overridedata as
426 # its only a shallow copy, could influence other data store
427 # copies!
428 self.overridedata = {}
429 self.overrides = None
430 self.overridevars = set(["OVERRIDES", "FILE"])
431 self.inoverride = False
432
433 def enableTracking(self):
434 self._tracking = True
435
436 def disableTracking(self):
437 self._tracking = False
438
439 def expandWithRefs(self, s, varname):
440
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 if not isinstance(s, str): # sanity check
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500442 return VariableParse(varname, self, s)
443
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 varparse = VariableParse(varname, self)
445
446 while s.find('${') != -1:
447 olds = s
448 try:
449 s = __expand_var_regexp__.sub(varparse.var_sub, s)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500450 try:
451 s = __expand_python_regexp__.sub(varparse.python_sub, s)
452 except SyntaxError as e:
453 # Likely unmatched brackets, just don't expand the expression
Andrew Geissler5199d832021-09-24 16:47:35 -0500454 if e.msg != "EOL while scanning string literal" and not e.msg.startswith("unterminated string literal"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500455 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500456 if s == olds:
457 break
Andrew Geissler5199d832021-09-24 16:47:35 -0500458 except ExpansionError as e:
459 e.addVar(varname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500460 raise
461 except bb.parse.SkipRecipe:
462 raise
Andrew Geissler5199d832021-09-24 16:47:35 -0500463 except bb.BBHandledException:
464 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500465 except Exception as exc:
Brad Bishop19323692019-04-05 15:28:33 -0400466 tb = sys.exc_info()[2]
467 raise ExpansionError(varname, s, exc).with_traceback(tb) from exc
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468
469 varparse.value = s
470
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471 return varparse
472
473 def expand(self, s, varname = None):
474 return self.expandWithRefs(s, varname).value
475
476 def finalize(self, parent = False):
477 return
478
479 def internal_finalize(self, parent = False):
480 """Performs final steps upon the datastore, including application of overrides"""
481 self.overrides = None
482
483 def need_overrides(self):
Patrick Williamsd7e96312015-09-22 08:09:05 -0500484 if self.overrides is not None:
485 return
486 if self.inoverride:
487 return
488 for count in range(5):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500489 self.inoverride = True
490 # Can end up here recursively so setup dummy values
491 self.overrides = []
492 self.overridesset = set()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500493 self.overrides = (self.getVar("OVERRIDES") or "").split(":") or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500494 self.overridesset = set(self.overrides)
495 self.inoverride = False
496 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500497 newoverrides = (self.getVar("OVERRIDES") or "").split(":") or []
Patrick Williamsd7e96312015-09-22 08:09:05 -0500498 if newoverrides == self.overrides:
499 break
500 self.overrides = newoverrides
501 self.overridesset = set(self.overrides)
502 else:
503 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.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500504
505 def initVar(self, var):
506 self.expand_cache = {}
507 if not var in self.dict:
508 self.dict[var] = {}
509
510 def _findVar(self, var):
511 dest = self.dict
512 while dest:
513 if var in dest:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500514 return dest[var], self.overridedata.get(var, None)
515
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500516 if "_data" not in dest:
517 break
518 dest = dest["_data"]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500519 return None, self.overridedata.get(var, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500520
521 def _makeShadowCopy(self, var):
522 if var in self.dict:
523 return
524
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500525 local_var, _ = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526
527 if local_var:
528 self.dict[var] = copy.copy(local_var)
529 else:
530 self.initVar(var)
531
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000532 def hasOverrides(self, var):
533 return var in self.overridedata
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534
535 def setVar(self, var, value, **loginfo):
536 #print("var=" + str(var) + " val=" + str(value))
Patrick Williams213cb262021-08-07 19:21:33 -0500537
Andrew Geissler595f6302022-01-24 19:11:47 +0000538 if not var.startswith("__anon_") and ("_append" in var or "_prepend" in var or "_remove" in var):
Patrick Williams213cb262021-08-07 19:21:33 -0500539 info = "%s" % var
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000540 if "file" in loginfo:
541 info += " file: %s" % loginfo["file"]
542 if "line" in loginfo:
543 info += " line: %s" % loginfo["line"]
Patrick Williams213cb262021-08-07 19:21:33 -0500544 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)
545
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000546 shortvar = var.split(":", 1)[0]
547 if shortvar in self._var_renames:
548 _print_rename_error(shortvar, loginfo, self._var_renames, fullvar=var)
549 # Mark that we have seen a renamed variable
550 self.setVar("_FAILPARSINGERRORHANDLED", True)
551
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800552 self.expand_cache = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500553 parsing=False
554 if 'parsing' in loginfo:
555 parsing=True
556
557 if 'op' not in loginfo:
558 loginfo['op'] = "set"
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800559
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560 match = __setvar_regexp__.match(var)
561 if match and match.group("keyword") in __setvar_keyword__:
562 base = match.group('base')
563 keyword = match.group("keyword")
564 override = match.group('add')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500565 l = self.getVarFlag(base, keyword, False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566 l.append([value, override])
567 self.setVarFlag(base, keyword, l, ignore=True)
568 # And cause that to be recorded:
569 loginfo['detail'] = value
570 loginfo['variable'] = base
571 if override:
572 loginfo['op'] = '%s[%s]' % (keyword, override)
573 else:
574 loginfo['op'] = keyword
575 self.varhistory.record(**loginfo)
576 # todo make sure keyword is not __doc__ or __module__
577 # pay the cookie monster
578
579 # more cookies for the cookie monster
Patrick Williams213cb262021-08-07 19:21:33 -0500580 if ':' in var:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581 self._setvar_update_overrides(base, **loginfo)
582
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500583 if base in self.overridevars:
Patrick Williamsd7e96312015-09-22 08:09:05 -0500584 self._setvar_update_overridevars(var, value)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585 return
586
587 if not var in self.dict:
588 self._makeShadowCopy(var)
589
590 if not parsing:
Patrick Williams213cb262021-08-07 19:21:33 -0500591 if ":append" in self.dict[var]:
592 del self.dict[var][":append"]
593 if ":prepend" in self.dict[var]:
594 del self.dict[var][":prepend"]
595 if ":remove" in self.dict[var]:
596 del self.dict[var][":remove"]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597 if var in self.overridedata:
598 active = []
599 self.need_overrides()
600 for (r, o) in self.overridedata[var]:
601 if o in self.overridesset:
602 active.append(r)
Patrick Williams213cb262021-08-07 19:21:33 -0500603 elif ":" in o:
604 if set(o.split(":")).issubset(self.overridesset):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500605 active.append(r)
606 for a in active:
607 self.delVar(a)
608 del self.overridedata[var]
609
610 # more cookies for the cookie monster
Patrick Williams213cb262021-08-07 19:21:33 -0500611 if ':' in var:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612 self._setvar_update_overrides(var, **loginfo)
613
614 # setting var
615 self.dict[var]["_content"] = value
616 self.varhistory.record(**loginfo)
617
618 if var in self.overridevars:
Patrick Williamsd7e96312015-09-22 08:09:05 -0500619 self._setvar_update_overridevars(var, value)
620
621 def _setvar_update_overridevars(self, var, value):
622 vardata = self.expandWithRefs(value, var)
623 new = vardata.references
624 new.update(vardata.contains.keys())
625 while not new.issubset(self.overridevars):
626 nextnew = set()
627 self.overridevars.update(new)
628 for i in new:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500629 vardata = self.expandWithRefs(self.getVar(i), i)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500630 nextnew.update(vardata.references)
631 nextnew.update(vardata.contains.keys())
632 new = nextnew
633 self.internal_finalize(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500634
635 def _setvar_update_overrides(self, var, **loginfo):
636 # aka pay the cookie monster
Patrick Williams213cb262021-08-07 19:21:33 -0500637 override = var[var.rfind(':')+1:]
638 shortvar = var[:var.rfind(':')]
Brad Bishop19323692019-04-05 15:28:33 -0400639 while override and __override_regexp__.match(override):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500640 if shortvar not in self.overridedata:
641 self.overridedata[shortvar] = []
642 if [var, override] not in self.overridedata[shortvar]:
643 # Force CoW by recreating the list first
644 self.overridedata[shortvar] = list(self.overridedata[shortvar])
645 self.overridedata[shortvar].append([var, override])
646 override = None
Patrick Williams213cb262021-08-07 19:21:33 -0500647 if ":" in shortvar:
648 override = var[shortvar.rfind(':')+1:]
649 shortvar = var[:shortvar.rfind(':')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500650 if len(shortvar) == 0:
651 override = None
652
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500653 def getVar(self, var, expand=True, noweakdefault=False, parsing=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 return self.getVarFlag(var, "_content", expand, noweakdefault, parsing)
655
656 def renameVar(self, key, newkey, **loginfo):
657 """
658 Rename the variable key to newkey
659 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500660 if key == newkey:
661 bb.warn("Calling renameVar with equivalent keys (%s) is invalid" % key)
662 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500663
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500664 val = self.getVar(key, 0, parsing=True)
665 if val is not None:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800666 self.varhistory.rename_variable_hist(key, newkey)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500667 loginfo['variable'] = newkey
668 loginfo['op'] = 'rename from %s' % key
669 loginfo['detail'] = val
670 self.varhistory.record(**loginfo)
671 self.setVar(newkey, val, ignore=True, parsing=True)
672
673 for i in (__setvar_keyword__):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500674 src = self.getVarFlag(key, i, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500675 if src is None:
676 continue
677
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500678 dest = self.getVarFlag(newkey, i, False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500679 dest.extend(src)
680 self.setVarFlag(newkey, i, dest, ignore=True)
681
682 if key in self.overridedata:
683 self.overridedata[newkey] = []
684 for (v, o) in self.overridedata[key]:
685 self.overridedata[newkey].append([v.replace(key, newkey), o])
686 self.renameVar(v, v.replace(key, newkey))
687
Patrick Williams213cb262021-08-07 19:21:33 -0500688 if ':' in newkey and val is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 self._setvar_update_overrides(newkey, **loginfo)
690
691 loginfo['variable'] = key
692 loginfo['op'] = 'rename (to)'
693 loginfo['detail'] = newkey
694 self.varhistory.record(**loginfo)
695 self.delVar(key, ignore=True)
696
697 def appendVar(self, var, value, **loginfo):
698 loginfo['op'] = 'append'
699 self.varhistory.record(**loginfo)
Patrick Williams213cb262021-08-07 19:21:33 -0500700 self.setVar(var + ":append", value, ignore=True, parsing=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701
702 def prependVar(self, var, value, **loginfo):
703 loginfo['op'] = 'prepend'
704 self.varhistory.record(**loginfo)
Patrick Williams213cb262021-08-07 19:21:33 -0500705 self.setVar(var + ":prepend", value, ignore=True, parsing=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500706
707 def delVar(self, var, **loginfo):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800708 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500709
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710 loginfo['detail'] = ""
711 loginfo['op'] = 'del'
712 self.varhistory.record(**loginfo)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500713 self.dict[var] = {}
714 if var in self.overridedata:
715 del self.overridedata[var]
Patrick Williams213cb262021-08-07 19:21:33 -0500716 if ':' in var:
717 override = var[var.rfind(':')+1:]
718 shortvar = var[:var.rfind(':')]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500719 while override and override.islower():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720 try:
721 if shortvar in self.overridedata:
722 # Force CoW by recreating the list first
723 self.overridedata[shortvar] = list(self.overridedata[shortvar])
724 self.overridedata[shortvar].remove([var, override])
725 except ValueError as e:
726 pass
727 override = None
Patrick Williams213cb262021-08-07 19:21:33 -0500728 if ":" in shortvar:
729 override = var[shortvar.rfind(':')+1:]
730 shortvar = var[:shortvar.rfind(':')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731 if len(shortvar) == 0:
732 override = None
733
734 def setVarFlag(self, var, flag, value, **loginfo):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800735 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500736
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000737 if var == "BB_RENAMED_VARIABLES":
738 self._var_renames[flag] = value
739
740 if var in self._var_renames:
741 _print_rename_error(var, loginfo, self._var_renames)
742 # Mark that we have seen a renamed variable
743 self.setVar("_FAILPARSINGERRORHANDLED", True)
744
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500745 if 'op' not in loginfo:
746 loginfo['op'] = "set"
747 loginfo['flag'] = flag
748 self.varhistory.record(**loginfo)
749 if not var in self.dict:
750 self._makeShadowCopy(var)
751 self.dict[var][flag] = value
752
Patrick Williams213cb262021-08-07 19:21:33 -0500753 if flag == "_defaultval" and ':' in var:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500754 self._setvar_update_overrides(var, **loginfo)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500755 if flag == "_defaultval" and var in self.overridevars:
756 self._setvar_update_overridevars(var, value)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500757
758 if flag == "unexport" or flag == "export":
759 if not "__exportlist" in self.dict:
760 self._makeShadowCopy("__exportlist")
761 if not "_content" in self.dict["__exportlist"]:
762 self.dict["__exportlist"]["_content"] = set()
763 self.dict["__exportlist"]["_content"].add(var)
764
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800765 def getVarFlag(self, var, flag, expand=True, noweakdefault=False, parsing=False, retparser=False):
766 if flag == "_content":
767 cachename = var
768 else:
769 if not flag:
770 bb.warn("Calling getVarFlag with flag unset is invalid")
771 return None
772 cachename = var + "[" + flag + "]"
773
774 if expand and cachename in self.expand_cache:
775 return self.expand_cache[cachename].value
776
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500777 local_var, overridedata = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500778 value = None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800779 removes = set()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500780 if flag == "_content" and overridedata is not None and not parsing:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500781 match = False
782 active = {}
783 self.need_overrides()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500784 for (r, o) in overridedata:
Patrick Williams213cb262021-08-07 19:21:33 -0500785 # FIXME What about double overrides both with "_" in the name?
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500786 if o in self.overridesset:
787 active[o] = r
Patrick Williams213cb262021-08-07 19:21:33 -0500788 elif ":" in o:
789 if set(o.split(":")).issubset(self.overridesset):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500790 active[o] = r
791
792 mod = True
793 while mod:
794 mod = False
795 for o in self.overrides:
796 for a in active.copy():
Patrick Williams213cb262021-08-07 19:21:33 -0500797 if a.endswith(":" + o):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798 t = active[a]
799 del active[a]
Patrick Williams213cb262021-08-07 19:21:33 -0500800 active[a.replace(":" + o, "")] = t
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500801 mod = True
802 elif a == o:
803 match = active[a]
804 del active[a]
805 if match:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800806 value, subparser = self.getVarFlag(match, "_content", False, retparser=True)
807 if hasattr(subparser, "removes"):
808 # We have to carry the removes from the overridden variable to apply at the
809 # end of processing
810 removes = subparser.removes
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811
812 if local_var is not None and value is None:
813 if flag in local_var:
814 value = copy.copy(local_var[flag])
815 elif flag == "_content" and "_defaultval" in local_var and not noweakdefault:
816 value = copy.copy(local_var["_defaultval"])
817
818
Patrick Williams213cb262021-08-07 19:21:33 -0500819 if flag == "_content" and local_var is not None and ":append" in local_var and not parsing:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500820 self.need_overrides()
Patrick Williams213cb262021-08-07 19:21:33 -0500821 for (r, o) in local_var[":append"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500822 match = True
823 if o:
Patrick Williams213cb262021-08-07 19:21:33 -0500824 for o2 in o.split(":"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825 if not o2 in self.overrides:
826 match = False
827 if match:
Patrick Williams213cb262021-08-07 19:21:33 -0500828 if value is None:
829 value = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830 value = value + r
831
Patrick Williams213cb262021-08-07 19:21:33 -0500832 if flag == "_content" and local_var is not None and ":prepend" in local_var and not parsing:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833 self.need_overrides()
Patrick Williams213cb262021-08-07 19:21:33 -0500834 for (r, o) in local_var[":prepend"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500835
836 match = True
837 if o:
Patrick Williams213cb262021-08-07 19:21:33 -0500838 for o2 in o.split(":"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839 if not o2 in self.overrides:
840 match = False
841 if match:
Patrick Williams213cb262021-08-07 19:21:33 -0500842 if value is None:
843 value = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844 value = r + value
845
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800846 parser = None
847 if expand or retparser:
848 parser = self.expandWithRefs(value, cachename)
849 if expand:
850 value = parser.value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851
Patrick Williams213cb262021-08-07 19:21:33 -0500852 if value and flag == "_content" and local_var is not None and ":remove" in local_var and not parsing:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853 self.need_overrides()
Patrick Williams213cb262021-08-07 19:21:33 -0500854 for (r, o) in local_var[":remove"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855 match = True
856 if o:
Patrick Williams213cb262021-08-07 19:21:33 -0500857 for o2 in o.split(":"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500858 if not o2 in self.overrides:
859 match = False
860 if match:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800861 removes.add(r)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500862
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800863 if value and flag == "_content" and not parsing:
864 if removes and parser:
865 expanded_removes = {}
866 for r in removes:
867 expanded_removes[r] = self.expand(r).split()
868
869 parser.removes = set()
Andrew Geissler595f6302022-01-24 19:11:47 +0000870 val = []
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800871 for v in __whitespace_split__.split(parser.value):
872 skip = False
873 for r in removes:
874 if v in expanded_removes[r]:
875 parser.removes.add(r)
876 skip = True
877 if skip:
878 continue
Andrew Geissler595f6302022-01-24 19:11:47 +0000879 val.append(v)
880 parser.value = "".join(val)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800881 if expand:
882 value = parser.value
883
884 if parser:
885 self.expand_cache[cachename] = parser
886
887 if retparser:
888 return value, parser
889
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890 return value
891
892 def delVarFlag(self, var, flag, **loginfo):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800893 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500894
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500895 local_var, _ = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896 if not local_var:
897 return
898 if not var in self.dict:
899 self._makeShadowCopy(var)
900
901 if var in self.dict and flag in self.dict[var]:
902 loginfo['detail'] = ""
903 loginfo['op'] = 'delFlag'
904 loginfo['flag'] = flag
905 self.varhistory.record(**loginfo)
906
907 del self.dict[var][flag]
908
909 def appendVarFlag(self, var, flag, value, **loginfo):
910 loginfo['op'] = 'append'
911 loginfo['flag'] = flag
912 self.varhistory.record(**loginfo)
913 newvalue = (self.getVarFlag(var, flag, False) or "") + value
914 self.setVarFlag(var, flag, newvalue, ignore=True)
915
916 def prependVarFlag(self, var, flag, value, **loginfo):
917 loginfo['op'] = 'prepend'
918 loginfo['flag'] = flag
919 self.varhistory.record(**loginfo)
920 newvalue = value + (self.getVarFlag(var, flag, False) or "")
921 self.setVarFlag(var, flag, newvalue, ignore=True)
922
923 def setVarFlags(self, var, flags, **loginfo):
924 self.expand_cache = {}
925 infer_caller_details(loginfo)
926 if not var in self.dict:
927 self._makeShadowCopy(var)
928
929 for i in flags:
930 if i == "_content":
931 continue
932 loginfo['flag'] = i
933 loginfo['detail'] = flags[i]
934 self.varhistory.record(**loginfo)
935 self.dict[var][i] = flags[i]
936
937 def getVarFlags(self, var, expand = False, internalflags=False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500938 local_var, _ = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500939 flags = {}
940
941 if local_var:
942 for i in local_var:
Patrick Williams213cb262021-08-07 19:21:33 -0500943 if i.startswith(("_", ":")) and not internalflags:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500944 continue
945 flags[i] = local_var[i]
946 if expand and i in expand:
947 flags[i] = self.expand(flags[i], var + "[" + i + "]")
948 if len(flags) == 0:
949 return None
950 return flags
951
952
953 def delVarFlags(self, var, **loginfo):
954 self.expand_cache = {}
955 if not var in self.dict:
956 self._makeShadowCopy(var)
957
958 if var in self.dict:
959 content = None
960
961 loginfo['op'] = 'delete flags'
962 self.varhistory.record(**loginfo)
963
964 # try to save the content
965 if "_content" in self.dict[var]:
966 content = self.dict[var]["_content"]
967 self.dict[var] = {}
968 self.dict[var]["_content"] = content
969 else:
970 del self.dict[var]
971
972 def createCopy(self):
973 """
974 Create a copy of self by setting _data to self
975 """
976 # we really want this to be a DataSmart...
977 data = DataSmart()
978 data.dict["_data"] = self.dict
979 data.varhistory = self.varhistory.copy()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500980 data.varhistory.dataroot = data
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500981 data.inchistory = self.inchistory.copy()
982
983 data._tracking = self._tracking
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000984 data._var_renames = self._var_renames
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500985
986 data.overrides = None
987 data.overridevars = copy.copy(self.overridevars)
988 # Should really be a deepcopy but has heavy overhead.
989 # Instead, we're careful with writes.
990 data.overridedata = copy.copy(self.overridedata)
991
992 return data
993
994 def expandVarref(self, variable, parents=False):
995 """Find all references to variable in the data and expand it
996 in place, optionally descending to parent datastores."""
997
998 if parents:
999 keys = iter(self)
1000 else:
1001 keys = self.localkeys()
1002
1003 ref = '${%s}' % variable
1004 value = self.getVar(variable, False)
1005 for key in keys:
1006 referrervalue = self.getVar(key, False)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001007 if referrervalue and isinstance(referrervalue, str) and ref in referrervalue:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008 self.setVar(key, referrervalue.replace(ref, value))
1009
1010 def localkeys(self):
1011 for key in self.dict:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001012 if key not in ['_data']:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001013 yield key
1014
1015 def __iter__(self):
1016 deleted = set()
1017 overrides = set()
1018 def keylist(d):
1019 klist = set()
1020 for key in d:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001021 if key in ["_data"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001022 continue
1023 if key in deleted:
1024 continue
1025 if key in overrides:
1026 continue
1027 if not d[key]:
1028 deleted.add(key)
1029 continue
1030 klist.add(key)
1031
1032 if "_data" in d:
1033 klist |= keylist(d["_data"])
1034
1035 return klist
1036
1037 self.need_overrides()
1038 for var in self.overridedata:
1039 for (r, o) in self.overridedata[var]:
1040 if o in self.overridesset:
1041 overrides.add(var)
Patrick Williams213cb262021-08-07 19:21:33 -05001042 elif ":" in o:
1043 if set(o.split(":")).issubset(self.overridesset):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001044 overrides.add(var)
1045
1046 for k in keylist(self.dict):
1047 yield k
1048
1049 for k in overrides:
1050 yield k
1051
1052 def __len__(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001053 return len(frozenset(iter(self)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001054
1055 def __getitem__(self, item):
1056 value = self.getVar(item, False)
1057 if value is None:
1058 raise KeyError(item)
1059 else:
1060 return value
1061
1062 def __setitem__(self, var, value):
1063 self.setVar(var, value)
1064
1065 def __delitem__(self, var):
1066 self.delVar(var)
1067
1068 def get_hash(self):
1069 data = {}
1070 d = self.createCopy()
1071 bb.data.expandKeys(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001072
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001073 config_ignore_vars = set((d.getVar("BB_HASHCONFIG_IGNORE_VARS") or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001074 keys = set(key for key in iter(d) if not key.startswith("__"))
1075 for key in keys:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001076 if key in config_ignore_vars:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001077 continue
1078
1079 value = d.getVar(key, False) or ""
Andrew Geissler82c905d2020-04-13 13:39:40 -05001080 if type(value) is type(self):
1081 data.update({key:value.get_hash()})
1082 else:
1083 data.update({key:value})
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001084
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001085 varflags = d.getVarFlags(key, internalflags = True, expand=["vardepvalue"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001086 if not varflags:
1087 continue
1088 for f in varflags:
1089 if f == "_content":
1090 continue
1091 data.update({'%s[%s]' % (key, f):varflags[f]})
1092
1093 for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]:
1094 bb_list = d.getVar(key, False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001095 data.update({key:str(bb_list)})
1096
1097 if key == "__BBANONFUNCS":
1098 for i in bb_list:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001099 value = d.getVar(i, False) or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001100 data.update({i:value})
1101
1102 data_str = str([(k, data[k]) for k in sorted(data.keys())])
Brad Bishop19323692019-04-05 15:28:33 -04001103 return hashlib.sha256(data_str.encode("utf-8")).hexdigest()