blob: 65528c6ae6ea999d1f92c4924728dbbc63fb9a12 [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
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
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()
Patrick Williams213cb262021-08-07 19:21:33 -0500280 if 'flag' in loginfo and not loginfo['flag'].startswith(("_", ":")):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500281 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
Patrick Williams213cb262021-08-07 19:21:33 -0500345 if event['op'] == ':remove':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346 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))
Patrick Williams213cb262021-08-07 19:21:33 -0500484
485 if "_append" in var or "_prepend" in var or "_remove" in var:
486 info = "%s" % var
487 if "filename" in loginfo:
488 info += " file: %s" % loginfo[filename]
489 if "lineno" in loginfo:
490 info += " line: %s" % loginfo[lineno]
491 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)
492
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800493 self.expand_cache = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500494 parsing=False
495 if 'parsing' in loginfo:
496 parsing=True
497
498 if 'op' not in loginfo:
499 loginfo['op'] = "set"
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800500
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500501 match = __setvar_regexp__.match(var)
502 if match and match.group("keyword") in __setvar_keyword__:
503 base = match.group('base')
504 keyword = match.group("keyword")
505 override = match.group('add')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500506 l = self.getVarFlag(base, keyword, False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500507 l.append([value, override])
508 self.setVarFlag(base, keyword, l, ignore=True)
509 # And cause that to be recorded:
510 loginfo['detail'] = value
511 loginfo['variable'] = base
512 if override:
513 loginfo['op'] = '%s[%s]' % (keyword, override)
514 else:
515 loginfo['op'] = keyword
516 self.varhistory.record(**loginfo)
517 # todo make sure keyword is not __doc__ or __module__
518 # pay the cookie monster
519
520 # more cookies for the cookie monster
Patrick Williams213cb262021-08-07 19:21:33 -0500521 if ':' in var:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500522 self._setvar_update_overrides(base, **loginfo)
523
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524 if base in self.overridevars:
Patrick Williamsd7e96312015-09-22 08:09:05 -0500525 self._setvar_update_overridevars(var, value)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526 return
527
528 if not var in self.dict:
529 self._makeShadowCopy(var)
530
531 if not parsing:
Patrick Williams213cb262021-08-07 19:21:33 -0500532 if ":append" in self.dict[var]:
533 del self.dict[var][":append"]
534 if ":prepend" in self.dict[var]:
535 del self.dict[var][":prepend"]
536 if ":remove" in self.dict[var]:
537 del self.dict[var][":remove"]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500538 if var in self.overridedata:
539 active = []
540 self.need_overrides()
541 for (r, o) in self.overridedata[var]:
542 if o in self.overridesset:
543 active.append(r)
Patrick Williams213cb262021-08-07 19:21:33 -0500544 elif ":" in o:
545 if set(o.split(":")).issubset(self.overridesset):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500546 active.append(r)
547 for a in active:
548 self.delVar(a)
549 del self.overridedata[var]
550
551 # more cookies for the cookie monster
Patrick Williams213cb262021-08-07 19:21:33 -0500552 if ':' in var:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500553 self._setvar_update_overrides(var, **loginfo)
554
555 # setting var
556 self.dict[var]["_content"] = value
557 self.varhistory.record(**loginfo)
558
559 if var in self.overridevars:
Patrick Williamsd7e96312015-09-22 08:09:05 -0500560 self._setvar_update_overridevars(var, value)
561
562 def _setvar_update_overridevars(self, var, value):
563 vardata = self.expandWithRefs(value, var)
564 new = vardata.references
565 new.update(vardata.contains.keys())
566 while not new.issubset(self.overridevars):
567 nextnew = set()
568 self.overridevars.update(new)
569 for i in new:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500570 vardata = self.expandWithRefs(self.getVar(i), i)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500571 nextnew.update(vardata.references)
572 nextnew.update(vardata.contains.keys())
573 new = nextnew
574 self.internal_finalize(True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575
576 def _setvar_update_overrides(self, var, **loginfo):
577 # aka pay the cookie monster
Patrick Williams213cb262021-08-07 19:21:33 -0500578 override = var[var.rfind(':')+1:]
579 shortvar = var[:var.rfind(':')]
Brad Bishop19323692019-04-05 15:28:33 -0400580 while override and __override_regexp__.match(override):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581 if shortvar not in self.overridedata:
582 self.overridedata[shortvar] = []
583 if [var, override] not in self.overridedata[shortvar]:
584 # Force CoW by recreating the list first
585 self.overridedata[shortvar] = list(self.overridedata[shortvar])
586 self.overridedata[shortvar].append([var, override])
587 override = None
Patrick Williams213cb262021-08-07 19:21:33 -0500588 if ":" in shortvar:
589 override = var[shortvar.rfind(':')+1:]
590 shortvar = var[:shortvar.rfind(':')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591 if len(shortvar) == 0:
592 override = None
593
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500594 def getVar(self, var, expand=True, noweakdefault=False, parsing=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595 return self.getVarFlag(var, "_content", expand, noweakdefault, parsing)
596
597 def renameVar(self, key, newkey, **loginfo):
598 """
599 Rename the variable key to newkey
600 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500601 if key == newkey:
602 bb.warn("Calling renameVar with equivalent keys (%s) is invalid" % key)
603 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500604
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500605 val = self.getVar(key, 0, parsing=True)
606 if val is not None:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800607 self.varhistory.rename_variable_hist(key, newkey)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500608 loginfo['variable'] = newkey
609 loginfo['op'] = 'rename from %s' % key
610 loginfo['detail'] = val
611 self.varhistory.record(**loginfo)
612 self.setVar(newkey, val, ignore=True, parsing=True)
613
614 for i in (__setvar_keyword__):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500615 src = self.getVarFlag(key, i, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616 if src is None:
617 continue
618
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500619 dest = self.getVarFlag(newkey, i, False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620 dest.extend(src)
621 self.setVarFlag(newkey, i, dest, ignore=True)
622
623 if key in self.overridedata:
624 self.overridedata[newkey] = []
625 for (v, o) in self.overridedata[key]:
626 self.overridedata[newkey].append([v.replace(key, newkey), o])
627 self.renameVar(v, v.replace(key, newkey))
628
Patrick Williams213cb262021-08-07 19:21:33 -0500629 if ':' in newkey and val is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630 self._setvar_update_overrides(newkey, **loginfo)
631
632 loginfo['variable'] = key
633 loginfo['op'] = 'rename (to)'
634 loginfo['detail'] = newkey
635 self.varhistory.record(**loginfo)
636 self.delVar(key, ignore=True)
637
638 def appendVar(self, var, value, **loginfo):
639 loginfo['op'] = 'append'
640 self.varhistory.record(**loginfo)
Patrick Williams213cb262021-08-07 19:21:33 -0500641 self.setVar(var + ":append", value, ignore=True, parsing=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500642
643 def prependVar(self, var, value, **loginfo):
644 loginfo['op'] = 'prepend'
645 self.varhistory.record(**loginfo)
Patrick Williams213cb262021-08-07 19:21:33 -0500646 self.setVar(var + ":prepend", value, ignore=True, parsing=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500647
648 def delVar(self, var, **loginfo):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800649 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500650
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 loginfo['detail'] = ""
652 loginfo['op'] = 'del'
653 self.varhistory.record(**loginfo)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 self.dict[var] = {}
655 if var in self.overridedata:
656 del self.overridedata[var]
Patrick Williams213cb262021-08-07 19:21:33 -0500657 if ':' in var:
658 override = var[var.rfind(':')+1:]
659 shortvar = var[:var.rfind(':')]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500660 while override and override.islower():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661 try:
662 if shortvar in self.overridedata:
663 # Force CoW by recreating the list first
664 self.overridedata[shortvar] = list(self.overridedata[shortvar])
665 self.overridedata[shortvar].remove([var, override])
666 except ValueError as e:
667 pass
668 override = None
Patrick Williams213cb262021-08-07 19:21:33 -0500669 if ":" in shortvar:
670 override = var[shortvar.rfind(':')+1:]
671 shortvar = var[:shortvar.rfind(':')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500672 if len(shortvar) == 0:
673 override = None
674
675 def setVarFlag(self, var, flag, value, **loginfo):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800676 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500677
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500678 if 'op' not in loginfo:
679 loginfo['op'] = "set"
680 loginfo['flag'] = flag
681 self.varhistory.record(**loginfo)
682 if not var in self.dict:
683 self._makeShadowCopy(var)
684 self.dict[var][flag] = value
685
Patrick Williams213cb262021-08-07 19:21:33 -0500686 if flag == "_defaultval" and ':' in var:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687 self._setvar_update_overrides(var, **loginfo)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500688 if flag == "_defaultval" and var in self.overridevars:
689 self._setvar_update_overridevars(var, value)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500690
691 if flag == "unexport" or flag == "export":
692 if not "__exportlist" in self.dict:
693 self._makeShadowCopy("__exportlist")
694 if not "_content" in self.dict["__exportlist"]:
695 self.dict["__exportlist"]["_content"] = set()
696 self.dict["__exportlist"]["_content"].add(var)
697
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800698 def getVarFlag(self, var, flag, expand=True, noweakdefault=False, parsing=False, retparser=False):
699 if flag == "_content":
700 cachename = var
701 else:
702 if not flag:
703 bb.warn("Calling getVarFlag with flag unset is invalid")
704 return None
705 cachename = var + "[" + flag + "]"
706
707 if expand and cachename in self.expand_cache:
708 return self.expand_cache[cachename].value
709
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500710 local_var, overridedata = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500711 value = None
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800712 removes = set()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500713 if flag == "_content" and overridedata is not None and not parsing:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500714 match = False
715 active = {}
716 self.need_overrides()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500717 for (r, o) in overridedata:
Patrick Williams213cb262021-08-07 19:21:33 -0500718 # FIXME What about double overrides both with "_" in the name?
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500719 if o in self.overridesset:
720 active[o] = r
Patrick Williams213cb262021-08-07 19:21:33 -0500721 elif ":" in o:
722 if set(o.split(":")).issubset(self.overridesset):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500723 active[o] = r
724
725 mod = True
726 while mod:
727 mod = False
728 for o in self.overrides:
729 for a in active.copy():
Patrick Williams213cb262021-08-07 19:21:33 -0500730 if a.endswith(":" + o):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731 t = active[a]
732 del active[a]
Patrick Williams213cb262021-08-07 19:21:33 -0500733 active[a.replace(":" + o, "")] = t
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734 mod = True
735 elif a == o:
736 match = active[a]
737 del active[a]
738 if match:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800739 value, subparser = self.getVarFlag(match, "_content", False, retparser=True)
740 if hasattr(subparser, "removes"):
741 # We have to carry the removes from the overridden variable to apply at the
742 # end of processing
743 removes = subparser.removes
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744
745 if local_var is not None and value is None:
746 if flag in local_var:
747 value = copy.copy(local_var[flag])
748 elif flag == "_content" and "_defaultval" in local_var and not noweakdefault:
749 value = copy.copy(local_var["_defaultval"])
750
751
Patrick Williams213cb262021-08-07 19:21:33 -0500752 if flag == "_content" and local_var is not None and ":append" in local_var and not parsing:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753 self.need_overrides()
Patrick Williams213cb262021-08-07 19:21:33 -0500754 for (r, o) in local_var[":append"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500755 match = True
756 if o:
Patrick Williams213cb262021-08-07 19:21:33 -0500757 for o2 in o.split(":"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758 if not o2 in self.overrides:
759 match = False
760 if match:
Patrick Williams213cb262021-08-07 19:21:33 -0500761 if value is None:
762 value = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500763 value = value + r
764
Patrick Williams213cb262021-08-07 19:21:33 -0500765 if flag == "_content" and local_var is not None and ":prepend" in local_var and not parsing:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500766 self.need_overrides()
Patrick Williams213cb262021-08-07 19:21:33 -0500767 for (r, o) in local_var[":prepend"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500768
769 match = True
770 if o:
Patrick Williams213cb262021-08-07 19:21:33 -0500771 for o2 in o.split(":"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500772 if not o2 in self.overrides:
773 match = False
774 if match:
Patrick Williams213cb262021-08-07 19:21:33 -0500775 if value is None:
776 value = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500777 value = r + value
778
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800779 parser = None
780 if expand or retparser:
781 parser = self.expandWithRefs(value, cachename)
782 if expand:
783 value = parser.value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784
Patrick Williams213cb262021-08-07 19:21:33 -0500785 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 -0500786 self.need_overrides()
Patrick Williams213cb262021-08-07 19:21:33 -0500787 for (r, o) in local_var[":remove"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788 match = True
789 if o:
Patrick Williams213cb262021-08-07 19:21:33 -0500790 for o2 in o.split(":"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791 if not o2 in self.overrides:
792 match = False
793 if match:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800794 removes.add(r)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800796 if value and flag == "_content" and not parsing:
797 if removes and parser:
798 expanded_removes = {}
799 for r in removes:
800 expanded_removes[r] = self.expand(r).split()
801
802 parser.removes = set()
803 val = ""
804 for v in __whitespace_split__.split(parser.value):
805 skip = False
806 for r in removes:
807 if v in expanded_removes[r]:
808 parser.removes.add(r)
809 skip = True
810 if skip:
811 continue
812 val = val + v
813 parser.value = val
814 if expand:
815 value = parser.value
816
817 if parser:
818 self.expand_cache[cachename] = parser
819
820 if retparser:
821 return value, parser
822
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823 return value
824
825 def delVarFlag(self, var, flag, **loginfo):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800826 self.expand_cache = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500827
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500828 local_var, _ = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500829 if not local_var:
830 return
831 if not var in self.dict:
832 self._makeShadowCopy(var)
833
834 if var in self.dict and flag in self.dict[var]:
835 loginfo['detail'] = ""
836 loginfo['op'] = 'delFlag'
837 loginfo['flag'] = flag
838 self.varhistory.record(**loginfo)
839
840 del self.dict[var][flag]
841
842 def appendVarFlag(self, var, flag, value, **loginfo):
843 loginfo['op'] = 'append'
844 loginfo['flag'] = flag
845 self.varhistory.record(**loginfo)
846 newvalue = (self.getVarFlag(var, flag, False) or "") + value
847 self.setVarFlag(var, flag, newvalue, ignore=True)
848
849 def prependVarFlag(self, var, flag, value, **loginfo):
850 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):
857 self.expand_cache = {}
858 infer_caller_details(loginfo)
859 if not var in self.dict:
860 self._makeShadowCopy(var)
861
862 for i in flags:
863 if i == "_content":
864 continue
865 loginfo['flag'] = i
866 loginfo['detail'] = flags[i]
867 self.varhistory.record(**loginfo)
868 self.dict[var][i] = flags[i]
869
870 def getVarFlags(self, var, expand = False, internalflags=False):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500871 local_var, _ = self._findVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500872 flags = {}
873
874 if local_var:
875 for i in local_var:
Patrick Williams213cb262021-08-07 19:21:33 -0500876 if i.startswith(("_", ":")) and not internalflags:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500877 continue
878 flags[i] = local_var[i]
879 if expand and i in expand:
880 flags[i] = self.expand(flags[i], var + "[" + i + "]")
881 if len(flags) == 0:
882 return None
883 return flags
884
885
886 def delVarFlags(self, var, **loginfo):
887 self.expand_cache = {}
888 if not var in self.dict:
889 self._makeShadowCopy(var)
890
891 if var in self.dict:
892 content = None
893
894 loginfo['op'] = 'delete flags'
895 self.varhistory.record(**loginfo)
896
897 # try to save the content
898 if "_content" in self.dict[var]:
899 content = self.dict[var]["_content"]
900 self.dict[var] = {}
901 self.dict[var]["_content"] = content
902 else:
903 del self.dict[var]
904
905 def createCopy(self):
906 """
907 Create a copy of self by setting _data to self
908 """
909 # we really want this to be a DataSmart...
910 data = DataSmart()
911 data.dict["_data"] = self.dict
912 data.varhistory = self.varhistory.copy()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500913 data.varhistory.dataroot = data
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500914 data.inchistory = self.inchistory.copy()
915
916 data._tracking = self._tracking
917
918 data.overrides = None
919 data.overridevars = copy.copy(self.overridevars)
920 # Should really be a deepcopy but has heavy overhead.
921 # Instead, we're careful with writes.
922 data.overridedata = copy.copy(self.overridedata)
923
924 return data
925
926 def expandVarref(self, variable, parents=False):
927 """Find all references to variable in the data and expand it
928 in place, optionally descending to parent datastores."""
929
930 if parents:
931 keys = iter(self)
932 else:
933 keys = self.localkeys()
934
935 ref = '${%s}' % variable
936 value = self.getVar(variable, False)
937 for key in keys:
938 referrervalue = self.getVar(key, False)
939 if referrervalue and ref in referrervalue:
940 self.setVar(key, referrervalue.replace(ref, value))
941
942 def localkeys(self):
943 for key in self.dict:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500944 if key not in ['_data']:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500945 yield key
946
947 def __iter__(self):
948 deleted = set()
949 overrides = set()
950 def keylist(d):
951 klist = set()
952 for key in d:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500953 if key in ["_data"]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954 continue
955 if key in deleted:
956 continue
957 if key in overrides:
958 continue
959 if not d[key]:
960 deleted.add(key)
961 continue
962 klist.add(key)
963
964 if "_data" in d:
965 klist |= keylist(d["_data"])
966
967 return klist
968
969 self.need_overrides()
970 for var in self.overridedata:
971 for (r, o) in self.overridedata[var]:
972 if o in self.overridesset:
973 overrides.add(var)
Patrick Williams213cb262021-08-07 19:21:33 -0500974 elif ":" in o:
975 if set(o.split(":")).issubset(self.overridesset):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500976 overrides.add(var)
977
978 for k in keylist(self.dict):
979 yield k
980
981 for k in overrides:
982 yield k
983
984 def __len__(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600985 return len(frozenset(iter(self)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500986
987 def __getitem__(self, item):
988 value = self.getVar(item, False)
989 if value is None:
990 raise KeyError(item)
991 else:
992 return value
993
994 def __setitem__(self, var, value):
995 self.setVar(var, value)
996
997 def __delitem__(self, var):
998 self.delVar(var)
999
1000 def get_hash(self):
1001 data = {}
1002 d = self.createCopy()
1003 bb.data.expandKeys(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001004
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001005 config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST") or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001006 keys = set(key for key in iter(d) if not key.startswith("__"))
1007 for key in keys:
1008 if key in config_whitelist:
1009 continue
1010
1011 value = d.getVar(key, False) or ""
Andrew Geissler82c905d2020-04-13 13:39:40 -05001012 if type(value) is type(self):
1013 data.update({key:value.get_hash()})
1014 else:
1015 data.update({key:value})
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001017 varflags = d.getVarFlags(key, internalflags = True, expand=["vardepvalue"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001018 if not varflags:
1019 continue
1020 for f in varflags:
1021 if f == "_content":
1022 continue
1023 data.update({'%s[%s]' % (key, f):varflags[f]})
1024
1025 for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]:
1026 bb_list = d.getVar(key, False) or []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001027 data.update({key:str(bb_list)})
1028
1029 if key == "__BBANONFUNCS":
1030 for i in bb_list:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001031 value = d.getVar(i, False) or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001032 data.update({i:value})
1033
1034 data_str = str([(k, data[k]) for k in sorted(data.keys())])
Brad Bishop19323692019-04-05 15:28:33 -04001035 return hashlib.sha256(data_str.encode("utf-8")).hexdigest()