blob: f48726a348ccb27c60bec01eb15d74a0099a9b13 [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
20from collections import MutableMapping
21import logging
22import hashlib
23import bb, bb.codeparser
24from bb import utils
25from bb.COW import COWDictBase
26
27logger = logging.getLogger("BitBake.Data")
28
29__setvar_keyword__ = ["_append", "_prepend", "_remove"]
Brad Bishop19323692019-04-05 15:28:33 -040030__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
36def infer_caller_details(loginfo, parent = False, varval = True):
37 """Save the caller the trouble of specifying everything."""
38 # Save effort.
39 if 'ignore' in loginfo and loginfo['ignore']:
40 return
41 # If nothing was provided, mark this as possibly unneeded.
42 if not loginfo:
43 loginfo['ignore'] = True
44 return
45 # Infer caller's likely values for variable (var) and value (value),
46 # to reduce clutter in the rest of the code.
47 above = None
48 def set_above():
49 try:
50 raise Exception
51 except Exception:
52 tb = sys.exc_info()[2]
53 if parent:
54 return tb.tb_frame.f_back.f_back.f_back
55 else:
56 return tb.tb_frame.f_back.f_back
57
58 if varval and ('variable' not in loginfo or 'detail' not in loginfo):
59 if not above:
60 above = set_above()
61 lcls = above.f_locals.items()
62 for k, v in lcls:
63 if k == 'value' and 'detail' not in loginfo:
64 loginfo['detail'] = v
65 if k == 'var' and 'variable' not in loginfo:
66 loginfo['variable'] = v
67 # Infer file/line/function from traceback
68 # Don't use traceback.extract_stack() since it fills the line contents which
69 # we don't need and that hits stat syscalls
70 if 'file' not in loginfo:
71 if not above:
72 above = set_above()
73 f = above.f_back
74 line = f.f_lineno
75 file = f.f_code.co_filename
76 func = f.f_code.co_name
77 loginfo['file'] = file
78 loginfo['line'] = line
79 if func not in loginfo:
80 loginfo['func'] = func
81
82class VariableParse:
83 def __init__(self, varname, d, val = None):
84 self.varname = varname
85 self.d = d
86 self.value = val
87
88 self.references = set()
89 self.execs = set()
90 self.contains = {}
91
92 def var_sub(self, match):
93 key = match.group()[2:-1]
94 if self.varname and key:
95 if self.varname == key:
96 raise Exception("variable %s references itself!" % self.varname)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080097 var = self.d.getVarFlag(key, "_content")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 self.references.add(key)
99 if var is not None:
100 return var
101 else:
102 return match.group()
103
104 def python_sub(self, match):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500105 if isinstance(match, str):
106 code = match
107 else:
108 code = match.group()[3:-1]
109
Brad Bishop19323692019-04-05 15:28:33 -0400110 if self.varname:
111 varname = 'Var <%s>' % self.varname
112 else:
113 varname = '<expansion>'
114 codeobj = compile(code.strip(), varname, "eval")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115
116 parser = bb.codeparser.PythonParser(self.varname, logger)
117 parser.parse_python(code)
118 if self.varname:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500119 vardeps = self.d.getVarFlag(self.varname, "vardeps")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500120 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
127 for k in parser.contains:
128 if k not in self.contains:
129 self.contains[k] = parser.contains[k].copy()
130 else:
131 self.contains[k].update(parser.contains[k])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600132 value = utils.better_eval(codeobj, DataContext(self.d), {'d' : self.d})
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500133 return str(value)
134
135
136class DataContext(dict):
137 def __init__(self, metadata, **kwargs):
138 self.metadata = metadata
139 dict.__init__(self, **kwargs)
140 self['d'] = metadata
141
142 def __missing__(self, key):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500143 value = self.metadata.getVar(key)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500144 if value is None or self.metadata.getVarFlag(key, 'func', False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500145 raise KeyError(key)
146 else:
147 return value
148
149class ExpansionError(Exception):
150 def __init__(self, varname, expression, exception):
151 self.expression = expression
152 self.variablename = varname
153 self.exception = exception
154 if varname:
155 if expression:
156 self.msg = "Failure expanding variable %s, expression was %s which triggered exception %s: %s" % (varname, expression, type(exception).__name__, exception)
157 else:
158 self.msg = "Failure expanding variable %s: %s: %s" % (varname, type(exception).__name__, exception)
159 else:
160 self.msg = "Failure expanding expression %s which triggered exception %s: %s" % (expression, type(exception).__name__, exception)
161 Exception.__init__(self, self.msg)
162 self.args = (varname, expression, exception)
163 def __str__(self):
164 return self.msg
165
166class IncludeHistory(object):
167 def __init__(self, parent = None, filename = '[TOP LEVEL]'):
168 self.parent = parent
169 self.filename = filename
170 self.children = []
171 self.current = self
172
173 def copy(self):
174 new = IncludeHistory(self.parent, self.filename)
175 for c in self.children:
176 new.children.append(c)
177 return new
178
179 def include(self, filename):
180 newfile = IncludeHistory(self.current, filename)
181 self.current.children.append(newfile)
182 self.current = newfile
183 return self
184
185 def __enter__(self):
186 pass
187
188 def __exit__(self, a, b, c):
189 if self.current.parent:
190 self.current = self.current.parent
191 else:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500192 bb.warn("Include log: Tried to finish '%s' at top level." % self.filename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500193 return False
194
195 def emit(self, o, level = 0):
196 """Emit an include history file, and its children."""
197 if level:
198 spaces = " " * (level - 1)
199 o.write("# %s%s" % (spaces, self.filename))
200 if len(self.children) > 0:
201 o.write(" includes:")
202 else:
203 o.write("#\n# INCLUDE HISTORY:\n#")
204 level = level + 1
205 for child in self.children:
206 o.write("\n")
207 child.emit(o, level)
208
209class VariableHistory(object):
210 def __init__(self, dataroot):
211 self.dataroot = dataroot
212 self.variables = COWDictBase.copy()
213
214 def copy(self):
215 new = VariableHistory(self.dataroot)
216 new.variables = self.variables.copy()
217 return new
218
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500219 def __getstate__(self):
220 vardict = {}
221 for k, v in self.variables.iteritems():
222 vardict[k] = v
223 return {'dataroot': self.dataroot,
224 'variables': vardict}
225
226 def __setstate__(self, state):
227 self.dataroot = state['dataroot']
228 self.variables = COWDictBase.copy()
229 for k, v in state['variables'].items():
230 self.variables[k] = v
231
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500232 def record(self, *kwonly, **loginfo):
233 if not self.dataroot._tracking:
234 return
235 if len(kwonly) > 0:
236 raise TypeError
237 infer_caller_details(loginfo, parent = True)
238 if 'ignore' in loginfo and loginfo['ignore']:
239 return
240 if 'op' not in loginfo or not loginfo['op']:
241 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:
245 raise ValueError("record() missing variable or file.")
246 var = loginfo['variable']
247
248 if var not in self.variables:
249 self.variables[var] = []
250 if not isinstance(self.variables[var], list):
251 return
252 if 'nodups' in loginfo and loginfo in self.variables[var]:
253 return
254 self.variables[var].append(loginfo.copy())
255
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800256 def rename_variable_hist(self, oldvar, newvar):
257 if not self.dataroot._tracking:
258 return
259 if oldvar not in self.variables:
260 return
261 if newvar not in self.variables:
262 self.variables[newvar] = []
263 for i in self.variables[oldvar]:
264 self.variables[newvar].append(i.copy())
265
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266 def variable(self, var):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500267 varhistory = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500268 if var in self.variables:
269 varhistory.extend(self.variables[var])
270 return varhistory
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271
272 def emit(self, var, oval, val, o, d):
273 history = self.variable(var)
274
275 # Append override history
276 if var in d.overridedata:
277 for (r, override) in d.overridedata[var]:
278 for event in self.variable(r):
279 loginfo = event.copy()
280 if 'flag' in loginfo and not loginfo['flag'].startswith("_"):
281 continue
282 loginfo['variable'] = var
283 loginfo['op'] = 'override[%s]:%s' % (override, loginfo['op'])
284 history.append(loginfo)
285
286 commentVal = re.sub('\n', '\n#', str(oval))
287 if history:
288 if len(history) == 1:
289 o.write("#\n# $%s\n" % var)
290 else:
291 o.write("#\n# $%s [%d operations]\n" % (var, len(history)))
292 for event in history:
293 # o.write("# %s\n" % str(event))
294 if 'func' in event:
295 # If we have a function listed, this is internal
296 # code, not an operation in a config file, and the
297 # full path is distracting.
298 event['file'] = re.sub('.*/', '', event['file'])
299 display_func = ' [%s]' % event['func']
300 else:
301 display_func = ''
302 if 'flag' in event:
303 flag = '[%s] ' % (event['flag'])
304 else:
305 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'])))
307 if len(history) > 1:
308 o.write("# pre-expansion value:\n")
309 o.write('# "%s"\n' % (commentVal))
310 else:
311 o.write("#\n# $%s\n# [no history recorded]\n#\n" % var)
312 o.write('# "%s"\n' % (commentVal))
313
314 def get_variable_files(self, var):
315 """Get the files where operations are made on a variable"""
316 var_history = self.variable(var)
317 files = []
318 for event in var_history:
319 files.append(event['file'])
320 return files
321
322 def get_variable_lines(self, var, f):
323 """Get the line where a operation is made on a variable in file f"""
324 var_history = self.variable(var)
325 lines = []
326 for event in var_history:
327 if f== event['file']:
328 line = event['line']
329 lines.append(line)
330 return lines
331
Andrew Geissler82c905d2020-04-13 13:39:40 -0500332 def get_variable_items_files(self, var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500333 """
334 Use variable history to map items added to a list variable and
335 the files in which they were added.
336 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500337 d = self.dataroot
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500338 history = self.variable(var)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500339 finalitems = (d.getVar(var) or '').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340 filemap = {}
341 isset = False
342 for event in history:
343 if 'flag' in event:
344 continue
345 if event['op'] == '_remove':
346 continue
347 if isset and event['op'] == 'set?':
348 continue
349 isset = True
350 items = d.expand(event['detail']).split()
351 for item in items:
352 # This is a little crude but is belt-and-braces to avoid us
353 # having to handle every possible operation type specifically
354 if item in finalitems and not item in filemap:
355 filemap[item] = event['file']
356 return filemap
357
358 def del_var_history(self, var, f=None, line=None):
359 """If file f and line are not given, the entire history of var is deleted"""
360 if var in self.variables:
361 if f and line:
362 self.variables[var] = [ x for x in self.variables[var] if x['file']!=f and x['line']!=line]
363 else:
364 self.variables[var] = []
365
366class DataSmart(MutableMapping):
367 def __init__(self):
368 self.dict = {}
369
370 self.inchistory = IncludeHistory()
371 self.varhistory = VariableHistory(self)
372 self._tracking = False
373
374 self.expand_cache = {}
375
376 # cookie monster tribute
377 # Need to be careful about writes to overridedata as
378 # its only a shallow copy, could influence other data store
379 # copies!
380 self.overridedata = {}
381 self.overrides = None
382 self.overridevars = set(["OVERRIDES", "FILE"])
383 self.inoverride = False
384
385 def enableTracking(self):
386 self._tracking = True
387
388 def disableTracking(self):
389 self._tracking = False
390
391 def expandWithRefs(self, s, varname):
392
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600393 if not isinstance(s, str): # sanity check
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500394 return VariableParse(varname, self, s)
395
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500396 varparse = VariableParse(varname, self)
397
398 while s.find('${') != -1:
399 olds = s
400 try:
401 s = __expand_var_regexp__.sub(varparse.var_sub, s)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500402 try:
403 s = __expand_python_regexp__.sub(varparse.python_sub, s)
404 except SyntaxError as e:
405 # Likely unmatched brackets, just don't expand the expression
406 if e.msg != "EOL while scanning string literal":
407 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408 if s == olds:
409 break
410 except ExpansionError:
411 raise
412 except bb.parse.SkipRecipe:
413 raise
414 except Exception as exc:
Brad Bishop19323692019-04-05 15:28:33 -0400415 tb = sys.exc_info()[2]
416 raise ExpansionError(varname, s, exc).with_traceback(tb) from exc
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500417
418 varparse.value = s
419
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420 return varparse
421
422 def expand(self, s, varname = None):
423 return self.expandWithRefs(s, varname).value
424
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):
Patrick Williamsd7e96312015-09-22 08:09:05 -0500433 if self.overrides is not None:
434 return
435 if self.inoverride:
436 return
437 for count in range(5):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500438 self.inoverride = True
439 # Can end up here recursively so setup dummy values
440 self.overrides = []
441 self.overridesset = set()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500442 self.overrides = (self.getVar("OVERRIDES") or "").split(":") or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500443 self.overridesset = set(self.overrides)
444 self.inoverride = False
445 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500446 newoverrides = (self.getVar("OVERRIDES") or "").split(":") or []
Patrick Williamsd7e96312015-09-22 08:09:05 -0500447 if newoverrides == self.overrides:
448 break
449 self.overrides = newoverrides
450 self.overridesset = set(self.overrides)
451 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.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500453
454 def initVar(self, var):
455 self.expand_cache = {}
456 if not var in self.dict:
457 self.dict[var] = {}
458
459 def _findVar(self, var):
460 dest = self.dict
461 while dest:
462 if var in dest:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500463 return dest[var], self.overridedata.get(var, None)
464
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500465 if "_data" not in dest:
466 break
467 dest = dest["_data"]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500468 return None, self.overridedata.get(var, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469
470 def _makeShadowCopy(self, var):
471 if var in self.dict:
472 return
473
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500474 local_var, _ = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475
476 if local_var:
477 self.dict[var] = copy.copy(local_var)
478 else:
479 self.initVar(var)
480
481
482 def setVar(self, var, value, **loginfo):
483 #print("var=" + str(var) + " val=" + str(value))
Andrew Geissler5f350902021-07-23 13:09:54 -0400484 var = var.replace(":", "_")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800485 self.expand_cache = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500486 parsing=False
487 if 'parsing' in loginfo:
488 parsing=True
489
490 if 'op' not in loginfo:
491 loginfo['op'] = "set"
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800492
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493 match = __setvar_regexp__.match(var)
494 if match and match.group("keyword") in __setvar_keyword__:
495 base = match.group('base')
496 keyword = match.group("keyword")
497 override = match.group('add')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500498 l = self.getVarFlag(base, keyword, False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500499 l.append([value, override])
500 self.setVarFlag(base, keyword, l, ignore=True)
501 # And cause that to be recorded:
502 loginfo['detail'] = value
503 loginfo['variable'] = base
504 if override:
505 loginfo['op'] = '%s[%s]' % (keyword, override)
506 else:
507 loginfo['op'] = keyword
508 self.varhistory.record(**loginfo)
509 # todo make sure keyword is not __doc__ or __module__
510 # pay the cookie monster
511
512 # more cookies for the cookie monster
513 if '_' in var:
514 self._setvar_update_overrides(base, **loginfo)
515
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500516 if base in self.overridevars:
Patrick Williamsd7e96312015-09-22 08:09:05 -0500517 self._setvar_update_overridevars(var, value)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500518 return
519
520 if not var in self.dict:
521 self._makeShadowCopy(var)
522
523 if not parsing:
524 if "_append" in self.dict[var]:
525 del self.dict[var]["_append"]
526 if "_prepend" in self.dict[var]:
527 del self.dict[var]["_prepend"]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500528 if "_remove" in self.dict[var]:
529 del self.dict[var]["_remove"]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500530 if var in self.overridedata:
531 active = []
532 self.need_overrides()
533 for (r, o) in self.overridedata[var]:
534 if o in self.overridesset:
535 active.append(r)
536 elif "_" in o:
537 if set(o.split("_")).issubset(self.overridesset):
538 active.append(r)
539 for a in active:
540 self.delVar(a)
541 del self.overridedata[var]
542
543 # more cookies for the cookie monster
544 if '_' in var:
545 self._setvar_update_overrides(var, **loginfo)
546
547 # setting var
548 self.dict[var]["_content"] = value
549 self.varhistory.record(**loginfo)
550
551 if var in self.overridevars:
Patrick Williamsd7e96312015-09-22 08:09:05 -0500552 self._setvar_update_overridevars(var, value)
553
554 def _setvar_update_overridevars(self, var, value):
555 vardata = self.expandWithRefs(value, var)
556 new = vardata.references
557 new.update(vardata.contains.keys())
558 while not new.issubset(self.overridevars):
559 nextnew = set()
560 self.overridevars.update(new)
561 for i in new:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500562 vardata = self.expandWithRefs(self.getVar(i), i)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500563 nextnew.update(vardata.references)
564 nextnew.update(vardata.contains.keys())
565 new = nextnew
566 self.internal_finalize(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500567
568 def _setvar_update_overrides(self, var, **loginfo):
569 # aka pay the cookie monster
570 override = var[var.rfind('_')+1:]
571 shortvar = var[:var.rfind('_')]
Brad Bishop19323692019-04-05 15:28:33 -0400572 while override and __override_regexp__.match(override):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573 if shortvar not in self.overridedata:
574 self.overridedata[shortvar] = []
575 if [var, override] not in self.overridedata[shortvar]:
576 # Force CoW by recreating the list first
577 self.overridedata[shortvar] = list(self.overridedata[shortvar])
578 self.overridedata[shortvar].append([var, override])
579 override = None
580 if "_" in shortvar:
581 override = var[shortvar.rfind('_')+1:]
582 shortvar = var[:shortvar.rfind('_')]
583 if len(shortvar) == 0:
584 override = None
585
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500586 def getVar(self, var, expand=True, noweakdefault=False, parsing=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500587 return self.getVarFlag(var, "_content", expand, noweakdefault, parsing)
588
589 def renameVar(self, key, newkey, **loginfo):
590 """
591 Rename the variable key to newkey
592 """
Andrew Geissler5f350902021-07-23 13:09:54 -0400593 key = key.replace(":", "_")
594 newkey = newkey.replace(":", "_")
Andrew Geissler82c905d2020-04-13 13:39:40 -0500595 if key == newkey:
596 bb.warn("Calling renameVar with equivalent keys (%s) is invalid" % key)
597 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500598
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599 val = self.getVar(key, 0, parsing=True)
600 if val is not None:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800601 self.varhistory.rename_variable_hist(key, newkey)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602 loginfo['variable'] = newkey
603 loginfo['op'] = 'rename from %s' % key
604 loginfo['detail'] = val
605 self.varhistory.record(**loginfo)
606 self.setVar(newkey, val, ignore=True, parsing=True)
607
608 for i in (__setvar_keyword__):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500609 src = self.getVarFlag(key, i, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500610 if src is None:
611 continue
612
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500613 dest = self.getVarFlag(newkey, i, False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500614 dest.extend(src)
615 self.setVarFlag(newkey, i, dest, ignore=True)
616
617 if key in self.overridedata:
618 self.overridedata[newkey] = []
619 for (v, o) in self.overridedata[key]:
620 self.overridedata[newkey].append([v.replace(key, newkey), o])
621 self.renameVar(v, v.replace(key, newkey))
622
623 if '_' in newkey and val is None:
624 self._setvar_update_overrides(newkey, **loginfo)
625
626 loginfo['variable'] = key
627 loginfo['op'] = 'rename (to)'
628 loginfo['detail'] = newkey
629 self.varhistory.record(**loginfo)
630 self.delVar(key, ignore=True)
631
632 def appendVar(self, var, value, **loginfo):
633 loginfo['op'] = 'append'
634 self.varhistory.record(**loginfo)
635 self.setVar(var + "_append", value, ignore=True, parsing=True)
636
637 def prependVar(self, var, value, **loginfo):
638 loginfo['op'] = 'prepend'
639 self.varhistory.record(**loginfo)
640 self.setVar(var + "_prepend", value, ignore=True, parsing=True)
641
642 def delVar(self, var, **loginfo):
Andrew Geissler5f350902021-07-23 13:09:54 -0400643 var = var.replace(":", "_")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800644 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500645
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646 loginfo['detail'] = ""
647 loginfo['op'] = 'del'
648 self.varhistory.record(**loginfo)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500649 self.dict[var] = {}
650 if var in self.overridedata:
651 del self.overridedata[var]
652 if '_' in var:
653 override = var[var.rfind('_')+1:]
654 shortvar = var[:var.rfind('_')]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500655 while override and override.islower():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656 try:
657 if shortvar in self.overridedata:
658 # Force CoW by recreating the list first
659 self.overridedata[shortvar] = list(self.overridedata[shortvar])
660 self.overridedata[shortvar].remove([var, override])
661 except ValueError as e:
662 pass
663 override = None
664 if "_" in shortvar:
665 override = var[shortvar.rfind('_')+1:]
666 shortvar = var[:shortvar.rfind('_')]
667 if len(shortvar) == 0:
668 override = None
669
670 def setVarFlag(self, var, flag, value, **loginfo):
Andrew Geissler5f350902021-07-23 13:09:54 -0400671 var = var.replace(":", "_")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800672 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500673
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500674 if 'op' not in loginfo:
675 loginfo['op'] = "set"
676 loginfo['flag'] = flag
677 self.varhistory.record(**loginfo)
678 if not var in self.dict:
679 self._makeShadowCopy(var)
680 self.dict[var][flag] = value
681
682 if flag == "_defaultval" and '_' in var:
683 self._setvar_update_overrides(var, **loginfo)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500684 if flag == "_defaultval" and var in self.overridevars:
685 self._setvar_update_overridevars(var, value)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686
687 if flag == "unexport" or flag == "export":
688 if not "__exportlist" in self.dict:
689 self._makeShadowCopy("__exportlist")
690 if not "_content" in self.dict["__exportlist"]:
691 self.dict["__exportlist"]["_content"] = set()
692 self.dict["__exportlist"]["_content"].add(var)
693
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800694 def getVarFlag(self, var, flag, expand=True, noweakdefault=False, parsing=False, retparser=False):
Andrew Geissler5f350902021-07-23 13:09:54 -0400695 var = var.replace(":", "_")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800696 if flag == "_content":
697 cachename = var
698 else:
699 if not flag:
700 bb.warn("Calling getVarFlag with flag unset is invalid")
701 return None
702 cachename = var + "[" + flag + "]"
703
704 if expand and cachename in self.expand_cache:
705 return self.expand_cache[cachename].value
706
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500707 local_var, overridedata = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708 value = None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800709 removes = set()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500710 if flag == "_content" and overridedata is not None and not parsing:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500711 match = False
712 active = {}
713 self.need_overrides()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500714 for (r, o) in overridedata:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500715 # What about double overrides both with "_" in the name?
716 if o in self.overridesset:
717 active[o] = r
718 elif "_" in o:
719 if set(o.split("_")).issubset(self.overridesset):
720 active[o] = r
721
722 mod = True
723 while mod:
724 mod = False
725 for o in self.overrides:
726 for a in active.copy():
727 if a.endswith("_" + o):
728 t = active[a]
729 del active[a]
730 active[a.replace("_" + o, "")] = t
731 mod = True
732 elif a == o:
733 match = active[a]
734 del active[a]
735 if match:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800736 value, subparser = self.getVarFlag(match, "_content", False, retparser=True)
737 if hasattr(subparser, "removes"):
738 # We have to carry the removes from the overridden variable to apply at the
739 # end of processing
740 removes = subparser.removes
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741
742 if local_var is not None and value is None:
743 if flag in local_var:
744 value = copy.copy(local_var[flag])
745 elif flag == "_content" and "_defaultval" in local_var and not noweakdefault:
746 value = copy.copy(local_var["_defaultval"])
747
748
749 if flag == "_content" and local_var is not None and "_append" in local_var and not parsing:
750 if not value:
751 value = ""
752 self.need_overrides()
753 for (r, o) in local_var["_append"]:
754 match = True
755 if o:
756 for o2 in o.split("_"):
757 if not o2 in self.overrides:
758 match = False
759 if match:
760 value = value + r
761
762 if flag == "_content" and local_var is not None and "_prepend" in local_var and not parsing:
763 if not value:
764 value = ""
765 self.need_overrides()
766 for (r, o) in local_var["_prepend"]:
767
768 match = True
769 if o:
770 for o2 in o.split("_"):
771 if not o2 in self.overrides:
772 match = False
773 if match:
774 value = r + value
775
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800776 parser = None
777 if expand or retparser:
778 parser = self.expandWithRefs(value, cachename)
779 if expand:
780 value = parser.value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500781
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800782 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 -0500783 self.need_overrides()
784 for (r, o) in local_var["_remove"]:
785 match = True
786 if o:
787 for o2 in o.split("_"):
788 if not o2 in self.overrides:
789 match = False
790 if match:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800791 removes.add(r)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800793 if value and flag == "_content" and not parsing:
794 if removes and parser:
795 expanded_removes = {}
796 for r in removes:
797 expanded_removes[r] = self.expand(r).split()
798
799 parser.removes = set()
800 val = ""
801 for v in __whitespace_split__.split(parser.value):
802 skip = False
803 for r in removes:
804 if v in expanded_removes[r]:
805 parser.removes.add(r)
806 skip = True
807 if skip:
808 continue
809 val = val + v
810 parser.value = val
811 if expand:
812 value = parser.value
813
814 if parser:
815 self.expand_cache[cachename] = parser
816
817 if retparser:
818 return value, parser
819
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500820 return value
821
822 def delVarFlag(self, var, flag, **loginfo):
Andrew Geissler5f350902021-07-23 13:09:54 -0400823 var = var.replace(":", "_")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800824 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500825
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500826 local_var, _ = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500827 if not local_var:
828 return
829 if not var in self.dict:
830 self._makeShadowCopy(var)
831
832 if var in self.dict and flag in self.dict[var]:
833 loginfo['detail'] = ""
834 loginfo['op'] = 'delFlag'
835 loginfo['flag'] = flag
836 self.varhistory.record(**loginfo)
837
838 del self.dict[var][flag]
839
840 def appendVarFlag(self, var, flag, value, **loginfo):
Andrew Geissler5f350902021-07-23 13:09:54 -0400841 var = var.replace(":", "_")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500842 loginfo['op'] = 'append'
843 loginfo['flag'] = flag
844 self.varhistory.record(**loginfo)
845 newvalue = (self.getVarFlag(var, flag, False) or "") + value
846 self.setVarFlag(var, flag, newvalue, ignore=True)
847
848 def prependVarFlag(self, var, flag, value, **loginfo):
Andrew Geissler5f350902021-07-23 13:09:54 -0400849 var = var.replace(":", "_")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500850 loginfo['op'] = 'prepend'
851 loginfo['flag'] = flag
852 self.varhistory.record(**loginfo)
853 newvalue = value + (self.getVarFlag(var, flag, False) or "")
854 self.setVarFlag(var, flag, newvalue, ignore=True)
855
856 def setVarFlags(self, var, flags, **loginfo):
Andrew Geissler5f350902021-07-23 13:09:54 -0400857 var = var.replace(":", "_")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500858 self.expand_cache = {}
859 infer_caller_details(loginfo)
860 if not var in self.dict:
861 self._makeShadowCopy(var)
862
863 for i in flags:
864 if i == "_content":
865 continue
866 loginfo['flag'] = i
867 loginfo['detail'] = flags[i]
868 self.varhistory.record(**loginfo)
869 self.dict[var][i] = flags[i]
870
871 def getVarFlags(self, var, expand = False, internalflags=False):
Andrew Geissler5f350902021-07-23 13:09:54 -0400872 var = var.replace(":", "_")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500873 local_var, _ = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500874 flags = {}
875
876 if local_var:
877 for i in local_var:
878 if i.startswith("_") and not internalflags:
879 continue
880 flags[i] = local_var[i]
881 if expand and i in expand:
882 flags[i] = self.expand(flags[i], var + "[" + i + "]")
883 if len(flags) == 0:
884 return None
885 return flags
886
887
888 def delVarFlags(self, var, **loginfo):
Andrew Geissler5f350902021-07-23 13:09:54 -0400889 var = var.replace(":", "_")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890 self.expand_cache = {}
891 if not var in self.dict:
892 self._makeShadowCopy(var)
893
894 if var in self.dict:
895 content = None
896
897 loginfo['op'] = 'delete flags'
898 self.varhistory.record(**loginfo)
899
900 # try to save the content
901 if "_content" in self.dict[var]:
902 content = self.dict[var]["_content"]
903 self.dict[var] = {}
904 self.dict[var]["_content"] = content
905 else:
906 del self.dict[var]
907
908 def createCopy(self):
909 """
910 Create a copy of self by setting _data to self
911 """
912 # we really want this to be a DataSmart...
913 data = DataSmart()
914 data.dict["_data"] = self.dict
915 data.varhistory = self.varhistory.copy()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500916 data.varhistory.dataroot = data
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500917 data.inchistory = self.inchistory.copy()
918
919 data._tracking = self._tracking
920
921 data.overrides = None
922 data.overridevars = copy.copy(self.overridevars)
923 # Should really be a deepcopy but has heavy overhead.
924 # Instead, we're careful with writes.
925 data.overridedata = copy.copy(self.overridedata)
926
927 return data
928
929 def expandVarref(self, variable, parents=False):
930 """Find all references to variable in the data and expand it
931 in place, optionally descending to parent datastores."""
932
933 if parents:
934 keys = iter(self)
935 else:
936 keys = self.localkeys()
937
938 ref = '${%s}' % variable
939 value = self.getVar(variable, False)
940 for key in keys:
941 referrervalue = self.getVar(key, False)
942 if referrervalue and ref in referrervalue:
943 self.setVar(key, referrervalue.replace(ref, value))
944
945 def localkeys(self):
946 for key in self.dict:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500947 if key not in ['_data']:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500948 yield key
949
950 def __iter__(self):
951 deleted = set()
952 overrides = set()
953 def keylist(d):
954 klist = set()
955 for key in d:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500956 if key in ["_data"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500957 continue
958 if key in deleted:
959 continue
960 if key in overrides:
961 continue
962 if not d[key]:
963 deleted.add(key)
964 continue
965 klist.add(key)
966
967 if "_data" in d:
968 klist |= keylist(d["_data"])
969
970 return klist
971
972 self.need_overrides()
973 for var in self.overridedata:
974 for (r, o) in self.overridedata[var]:
975 if o in self.overridesset:
976 overrides.add(var)
977 elif "_" in o:
978 if set(o.split("_")).issubset(self.overridesset):
979 overrides.add(var)
980
981 for k in keylist(self.dict):
982 yield k
983
984 for k in overrides:
985 yield k
986
987 def __len__(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600988 return len(frozenset(iter(self)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500989
990 def __getitem__(self, item):
991 value = self.getVar(item, False)
992 if value is None:
993 raise KeyError(item)
994 else:
995 return value
996
997 def __setitem__(self, var, value):
998 self.setVar(var, value)
999
1000 def __delitem__(self, var):
1001 self.delVar(var)
1002
1003 def get_hash(self):
1004 data = {}
1005 d = self.createCopy()
1006 bb.data.expandKeys(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001008 config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST") or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001009 keys = set(key for key in iter(d) if not key.startswith("__"))
1010 for key in keys:
1011 if key in config_whitelist:
1012 continue
1013
1014 value = d.getVar(key, False) or ""
Andrew Geissler82c905d2020-04-13 13:39:40 -05001015 if type(value) is type(self):
1016 data.update({key:value.get_hash()})
1017 else:
1018 data.update({key:value})
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001019
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001020 varflags = d.getVarFlags(key, internalflags = True, expand=["vardepvalue"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021 if not varflags:
1022 continue
1023 for f in varflags:
1024 if f == "_content":
1025 continue
1026 data.update({'%s[%s]' % (key, f):varflags[f]})
1027
1028 for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]:
1029 bb_list = d.getVar(key, False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001030 data.update({key:str(bb_list)})
1031
1032 if key == "__BBANONFUNCS":
1033 for i in bb_list:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001034 value = d.getVar(i, False) or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001035 data.update({i:value})
1036
1037 data_str = str([(k, data[k]) for k in sorted(data.keys())])
Brad Bishop19323692019-04-05 15:28:33 -04001038 return hashlib.sha256(data_str.encode("utf-8")).hexdigest()