1*4882a593Smuzhiyun""" 2*4882a593SmuzhiyunBitBake Smart Dictionary Implementation 3*4882a593Smuzhiyun 4*4882a593SmuzhiyunFunctions for interacting with the data structure used by the 5*4882a593SmuzhiyunBitBake build tools. 6*4882a593Smuzhiyun 7*4882a593Smuzhiyun""" 8*4882a593Smuzhiyun 9*4882a593Smuzhiyun# Copyright (C) 2003, 2004 Chris Larson 10*4882a593Smuzhiyun# Copyright (C) 2004, 2005 Seb Frankengul 11*4882a593Smuzhiyun# Copyright (C) 2005, 2006 Holger Hans Peter Freyther 12*4882a593Smuzhiyun# Copyright (C) 2005 Uli Luckas 13*4882a593Smuzhiyun# Copyright (C) 2005 ROAD GmbH 14*4882a593Smuzhiyun# 15*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 16*4882a593Smuzhiyun# 17*4882a593Smuzhiyun# Based on functions from the base bb module, Copyright 2003 Holger Schurig 18*4882a593Smuzhiyun 19*4882a593Smuzhiyunimport copy, re, sys, traceback 20*4882a593Smuzhiyunfrom collections.abc import MutableMapping 21*4882a593Smuzhiyunimport logging 22*4882a593Smuzhiyunimport hashlib 23*4882a593Smuzhiyunimport bb, bb.codeparser 24*4882a593Smuzhiyunfrom bb import utils 25*4882a593Smuzhiyunfrom bb.COW import COWDictBase 26*4882a593Smuzhiyun 27*4882a593Smuzhiyunlogger = logging.getLogger("BitBake.Data") 28*4882a593Smuzhiyun 29*4882a593Smuzhiyun__setvar_keyword__ = [":append", ":prepend", ":remove"] 30*4882a593Smuzhiyun__setvar_regexp__ = re.compile(r'(?P<base>.*?)(?P<keyword>:append|:prepend|:remove)(:(?P<add>[^A-Z]*))?$') 31*4882a593Smuzhiyun__expand_var_regexp__ = re.compile(r"\${[a-zA-Z0-9\-_+./~:]+?}") 32*4882a593Smuzhiyun__expand_python_regexp__ = re.compile(r"\${@.+?}") 33*4882a593Smuzhiyun__whitespace_split__ = re.compile(r'(\s)') 34*4882a593Smuzhiyun__override_regexp__ = re.compile(r'[a-z0-9]+') 35*4882a593Smuzhiyun 36*4882a593Smuzhiyunbitbake_renamed_vars = { 37*4882a593Smuzhiyun "BB_ENV_WHITELIST": "BB_ENV_PASSTHROUGH", 38*4882a593Smuzhiyun "BB_ENV_EXTRAWHITE": "BB_ENV_PASSTHROUGH_ADDITIONS", 39*4882a593Smuzhiyun "BB_HASHBASE_WHITELIST": "BB_BASEHASH_IGNORE_VARS", 40*4882a593Smuzhiyun "BB_HASHCONFIG_WHITELIST": "BB_HASHCONFIG_IGNORE_VARS", 41*4882a593Smuzhiyun "BB_HASHTASK_WHITELIST": "BB_TASKHASH_IGNORE_TASKS", 42*4882a593Smuzhiyun "BB_SETSCENE_ENFORCE_WHITELIST": "BB_SETSCENE_ENFORCE_IGNORE_TASKS", 43*4882a593Smuzhiyun "MULTI_PROVIDER_WHITELIST": "BB_MULTI_PROVIDER_ALLOWED", 44*4882a593Smuzhiyun "BB_STAMP_WHITELIST": "is a deprecated variable and support has been removed", 45*4882a593Smuzhiyun "BB_STAMP_POLICY": "is a deprecated variable and support has been removed", 46*4882a593Smuzhiyun} 47*4882a593Smuzhiyun 48*4882a593Smuzhiyundef infer_caller_details(loginfo, parent = False, varval = True): 49*4882a593Smuzhiyun """Save the caller the trouble of specifying everything.""" 50*4882a593Smuzhiyun # Save effort. 51*4882a593Smuzhiyun if 'ignore' in loginfo and loginfo['ignore']: 52*4882a593Smuzhiyun return 53*4882a593Smuzhiyun # If nothing was provided, mark this as possibly unneeded. 54*4882a593Smuzhiyun if not loginfo: 55*4882a593Smuzhiyun loginfo['ignore'] = True 56*4882a593Smuzhiyun return 57*4882a593Smuzhiyun # Infer caller's likely values for variable (var) and value (value), 58*4882a593Smuzhiyun # to reduce clutter in the rest of the code. 59*4882a593Smuzhiyun above = None 60*4882a593Smuzhiyun def set_above(): 61*4882a593Smuzhiyun try: 62*4882a593Smuzhiyun raise Exception 63*4882a593Smuzhiyun except Exception: 64*4882a593Smuzhiyun tb = sys.exc_info()[2] 65*4882a593Smuzhiyun if parent: 66*4882a593Smuzhiyun return tb.tb_frame.f_back.f_back.f_back 67*4882a593Smuzhiyun else: 68*4882a593Smuzhiyun return tb.tb_frame.f_back.f_back 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun if varval and ('variable' not in loginfo or 'detail' not in loginfo): 71*4882a593Smuzhiyun if not above: 72*4882a593Smuzhiyun above = set_above() 73*4882a593Smuzhiyun lcls = above.f_locals.items() 74*4882a593Smuzhiyun for k, v in lcls: 75*4882a593Smuzhiyun if k == 'value' and 'detail' not in loginfo: 76*4882a593Smuzhiyun loginfo['detail'] = v 77*4882a593Smuzhiyun if k == 'var' and 'variable' not in loginfo: 78*4882a593Smuzhiyun loginfo['variable'] = v 79*4882a593Smuzhiyun # Infer file/line/function from traceback 80*4882a593Smuzhiyun # Don't use traceback.extract_stack() since it fills the line contents which 81*4882a593Smuzhiyun # we don't need and that hits stat syscalls 82*4882a593Smuzhiyun if 'file' not in loginfo: 83*4882a593Smuzhiyun if not above: 84*4882a593Smuzhiyun above = set_above() 85*4882a593Smuzhiyun f = above.f_back 86*4882a593Smuzhiyun line = f.f_lineno 87*4882a593Smuzhiyun file = f.f_code.co_filename 88*4882a593Smuzhiyun func = f.f_code.co_name 89*4882a593Smuzhiyun loginfo['file'] = file 90*4882a593Smuzhiyun loginfo['line'] = line 91*4882a593Smuzhiyun if func not in loginfo: 92*4882a593Smuzhiyun loginfo['func'] = func 93*4882a593Smuzhiyun 94*4882a593Smuzhiyunclass VariableParse: 95*4882a593Smuzhiyun def __init__(self, varname, d, val = None): 96*4882a593Smuzhiyun self.varname = varname 97*4882a593Smuzhiyun self.d = d 98*4882a593Smuzhiyun self.value = val 99*4882a593Smuzhiyun 100*4882a593Smuzhiyun self.references = set() 101*4882a593Smuzhiyun self.execs = set() 102*4882a593Smuzhiyun self.contains = {} 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun def var_sub(self, match): 105*4882a593Smuzhiyun key = match.group()[2:-1] 106*4882a593Smuzhiyun if self.varname and key: 107*4882a593Smuzhiyun if self.varname == key: 108*4882a593Smuzhiyun raise Exception("variable %s references itself!" % self.varname) 109*4882a593Smuzhiyun var = self.d.getVarFlag(key, "_content") 110*4882a593Smuzhiyun self.references.add(key) 111*4882a593Smuzhiyun if var is not None: 112*4882a593Smuzhiyun return var 113*4882a593Smuzhiyun else: 114*4882a593Smuzhiyun return match.group() 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun def python_sub(self, match): 117*4882a593Smuzhiyun if isinstance(match, str): 118*4882a593Smuzhiyun code = match 119*4882a593Smuzhiyun else: 120*4882a593Smuzhiyun code = match.group()[3:-1] 121*4882a593Smuzhiyun 122*4882a593Smuzhiyun if self.varname: 123*4882a593Smuzhiyun varname = 'Var <%s>' % self.varname 124*4882a593Smuzhiyun else: 125*4882a593Smuzhiyun varname = '<expansion>' 126*4882a593Smuzhiyun codeobj = compile(code.strip(), varname, "eval") 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun parser = bb.codeparser.PythonParser(self.varname, logger) 129*4882a593Smuzhiyun parser.parse_python(code) 130*4882a593Smuzhiyun if self.varname: 131*4882a593Smuzhiyun vardeps = self.d.getVarFlag(self.varname, "vardeps") 132*4882a593Smuzhiyun if vardeps is None: 133*4882a593Smuzhiyun parser.log.flush() 134*4882a593Smuzhiyun else: 135*4882a593Smuzhiyun parser.log.flush() 136*4882a593Smuzhiyun self.references |= parser.references 137*4882a593Smuzhiyun self.execs |= parser.execs 138*4882a593Smuzhiyun 139*4882a593Smuzhiyun for k in parser.contains: 140*4882a593Smuzhiyun if k not in self.contains: 141*4882a593Smuzhiyun self.contains[k] = parser.contains[k].copy() 142*4882a593Smuzhiyun else: 143*4882a593Smuzhiyun self.contains[k].update(parser.contains[k]) 144*4882a593Smuzhiyun value = utils.better_eval(codeobj, DataContext(self.d), {'d' : self.d}) 145*4882a593Smuzhiyun return str(value) 146*4882a593Smuzhiyun 147*4882a593Smuzhiyun 148*4882a593Smuzhiyunclass DataContext(dict): 149*4882a593Smuzhiyun def __init__(self, metadata, **kwargs): 150*4882a593Smuzhiyun self.metadata = metadata 151*4882a593Smuzhiyun dict.__init__(self, **kwargs) 152*4882a593Smuzhiyun self['d'] = metadata 153*4882a593Smuzhiyun 154*4882a593Smuzhiyun def __missing__(self, key): 155*4882a593Smuzhiyun # Skip commonly accessed invalid variables 156*4882a593Smuzhiyun if key in ['bb', 'oe', 'int', 'bool', 'time', 'str', 'os']: 157*4882a593Smuzhiyun raise KeyError(key) 158*4882a593Smuzhiyun value = self.metadata.getVar(key) 159*4882a593Smuzhiyun if value is None or self.metadata.getVarFlag(key, 'func', False): 160*4882a593Smuzhiyun raise KeyError(key) 161*4882a593Smuzhiyun else: 162*4882a593Smuzhiyun return value 163*4882a593Smuzhiyun 164*4882a593Smuzhiyunclass ExpansionError(Exception): 165*4882a593Smuzhiyun def __init__(self, varname, expression, exception): 166*4882a593Smuzhiyun self.expression = expression 167*4882a593Smuzhiyun self.variablename = varname 168*4882a593Smuzhiyun self.exception = exception 169*4882a593Smuzhiyun self.varlist = [varname or expression or ""] 170*4882a593Smuzhiyun if varname: 171*4882a593Smuzhiyun if expression: 172*4882a593Smuzhiyun self.msg = "Failure expanding variable %s, expression was %s which triggered exception %s: %s" % (varname, expression, type(exception).__name__, exception) 173*4882a593Smuzhiyun else: 174*4882a593Smuzhiyun self.msg = "Failure expanding variable %s: %s: %s" % (varname, type(exception).__name__, exception) 175*4882a593Smuzhiyun else: 176*4882a593Smuzhiyun self.msg = "Failure expanding expression %s which triggered exception %s: %s" % (expression, type(exception).__name__, exception) 177*4882a593Smuzhiyun Exception.__init__(self, self.msg) 178*4882a593Smuzhiyun self.args = (varname, expression, exception) 179*4882a593Smuzhiyun 180*4882a593Smuzhiyun def addVar(self, varname): 181*4882a593Smuzhiyun if varname: 182*4882a593Smuzhiyun self.varlist.append(varname) 183*4882a593Smuzhiyun 184*4882a593Smuzhiyun def __str__(self): 185*4882a593Smuzhiyun chain = "\nThe variable dependency chain for the failure is: " + " -> ".join(self.varlist) 186*4882a593Smuzhiyun return self.msg + chain 187*4882a593Smuzhiyun 188*4882a593Smuzhiyunclass IncludeHistory(object): 189*4882a593Smuzhiyun def __init__(self, parent = None, filename = '[TOP LEVEL]'): 190*4882a593Smuzhiyun self.parent = parent 191*4882a593Smuzhiyun self.filename = filename 192*4882a593Smuzhiyun self.children = [] 193*4882a593Smuzhiyun self.current = self 194*4882a593Smuzhiyun 195*4882a593Smuzhiyun def copy(self): 196*4882a593Smuzhiyun new = IncludeHistory(self.parent, self.filename) 197*4882a593Smuzhiyun for c in self.children: 198*4882a593Smuzhiyun new.children.append(c) 199*4882a593Smuzhiyun return new 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun def include(self, filename): 202*4882a593Smuzhiyun newfile = IncludeHistory(self.current, filename) 203*4882a593Smuzhiyun self.current.children.append(newfile) 204*4882a593Smuzhiyun self.current = newfile 205*4882a593Smuzhiyun return self 206*4882a593Smuzhiyun 207*4882a593Smuzhiyun def __enter__(self): 208*4882a593Smuzhiyun pass 209*4882a593Smuzhiyun 210*4882a593Smuzhiyun def __exit__(self, a, b, c): 211*4882a593Smuzhiyun if self.current.parent: 212*4882a593Smuzhiyun self.current = self.current.parent 213*4882a593Smuzhiyun else: 214*4882a593Smuzhiyun bb.warn("Include log: Tried to finish '%s' at top level." % self.filename) 215*4882a593Smuzhiyun return False 216*4882a593Smuzhiyun 217*4882a593Smuzhiyun def emit(self, o, level = 0): 218*4882a593Smuzhiyun """Emit an include history file, and its children.""" 219*4882a593Smuzhiyun if level: 220*4882a593Smuzhiyun spaces = " " * (level - 1) 221*4882a593Smuzhiyun o.write("# %s%s" % (spaces, self.filename)) 222*4882a593Smuzhiyun if len(self.children) > 0: 223*4882a593Smuzhiyun o.write(" includes:") 224*4882a593Smuzhiyun else: 225*4882a593Smuzhiyun o.write("#\n# INCLUDE HISTORY:\n#") 226*4882a593Smuzhiyun level = level + 1 227*4882a593Smuzhiyun for child in self.children: 228*4882a593Smuzhiyun o.write("\n") 229*4882a593Smuzhiyun child.emit(o, level) 230*4882a593Smuzhiyun 231*4882a593Smuzhiyunclass VariableHistory(object): 232*4882a593Smuzhiyun def __init__(self, dataroot): 233*4882a593Smuzhiyun self.dataroot = dataroot 234*4882a593Smuzhiyun self.variables = COWDictBase.copy() 235*4882a593Smuzhiyun 236*4882a593Smuzhiyun def copy(self): 237*4882a593Smuzhiyun new = VariableHistory(self.dataroot) 238*4882a593Smuzhiyun new.variables = self.variables.copy() 239*4882a593Smuzhiyun return new 240*4882a593Smuzhiyun 241*4882a593Smuzhiyun def __getstate__(self): 242*4882a593Smuzhiyun vardict = {} 243*4882a593Smuzhiyun for k, v in self.variables.iteritems(): 244*4882a593Smuzhiyun vardict[k] = v 245*4882a593Smuzhiyun return {'dataroot': self.dataroot, 246*4882a593Smuzhiyun 'variables': vardict} 247*4882a593Smuzhiyun 248*4882a593Smuzhiyun def __setstate__(self, state): 249*4882a593Smuzhiyun self.dataroot = state['dataroot'] 250*4882a593Smuzhiyun self.variables = COWDictBase.copy() 251*4882a593Smuzhiyun for k, v in state['variables'].items(): 252*4882a593Smuzhiyun self.variables[k] = v 253*4882a593Smuzhiyun 254*4882a593Smuzhiyun def record(self, *kwonly, **loginfo): 255*4882a593Smuzhiyun if not self.dataroot._tracking: 256*4882a593Smuzhiyun return 257*4882a593Smuzhiyun if len(kwonly) > 0: 258*4882a593Smuzhiyun raise TypeError 259*4882a593Smuzhiyun infer_caller_details(loginfo, parent = True) 260*4882a593Smuzhiyun if 'ignore' in loginfo and loginfo['ignore']: 261*4882a593Smuzhiyun return 262*4882a593Smuzhiyun if 'op' not in loginfo or not loginfo['op']: 263*4882a593Smuzhiyun loginfo['op'] = 'set' 264*4882a593Smuzhiyun if 'detail' in loginfo: 265*4882a593Smuzhiyun loginfo['detail'] = str(loginfo['detail']) 266*4882a593Smuzhiyun if 'variable' not in loginfo or 'file' not in loginfo: 267*4882a593Smuzhiyun raise ValueError("record() missing variable or file.") 268*4882a593Smuzhiyun var = loginfo['variable'] 269*4882a593Smuzhiyun 270*4882a593Smuzhiyun if var not in self.variables: 271*4882a593Smuzhiyun self.variables[var] = [] 272*4882a593Smuzhiyun if not isinstance(self.variables[var], list): 273*4882a593Smuzhiyun return 274*4882a593Smuzhiyun if 'nodups' in loginfo and loginfo in self.variables[var]: 275*4882a593Smuzhiyun return 276*4882a593Smuzhiyun self.variables[var].append(loginfo.copy()) 277*4882a593Smuzhiyun 278*4882a593Smuzhiyun def rename_variable_hist(self, oldvar, newvar): 279*4882a593Smuzhiyun if not self.dataroot._tracking: 280*4882a593Smuzhiyun return 281*4882a593Smuzhiyun if oldvar not in self.variables: 282*4882a593Smuzhiyun return 283*4882a593Smuzhiyun if newvar not in self.variables: 284*4882a593Smuzhiyun self.variables[newvar] = [] 285*4882a593Smuzhiyun for i in self.variables[oldvar]: 286*4882a593Smuzhiyun self.variables[newvar].append(i.copy()) 287*4882a593Smuzhiyun 288*4882a593Smuzhiyun def variable(self, var): 289*4882a593Smuzhiyun varhistory = [] 290*4882a593Smuzhiyun if var in self.variables: 291*4882a593Smuzhiyun varhistory.extend(self.variables[var]) 292*4882a593Smuzhiyun return varhistory 293*4882a593Smuzhiyun 294*4882a593Smuzhiyun def emit(self, var, oval, val, o, d): 295*4882a593Smuzhiyun history = self.variable(var) 296*4882a593Smuzhiyun 297*4882a593Smuzhiyun # Append override history 298*4882a593Smuzhiyun if var in d.overridedata: 299*4882a593Smuzhiyun for (r, override) in d.overridedata[var]: 300*4882a593Smuzhiyun for event in self.variable(r): 301*4882a593Smuzhiyun loginfo = event.copy() 302*4882a593Smuzhiyun if 'flag' in loginfo and not loginfo['flag'].startswith(("_", ":")): 303*4882a593Smuzhiyun continue 304*4882a593Smuzhiyun loginfo['variable'] = var 305*4882a593Smuzhiyun loginfo['op'] = 'override[%s]:%s' % (override, loginfo['op']) 306*4882a593Smuzhiyun history.append(loginfo) 307*4882a593Smuzhiyun 308*4882a593Smuzhiyun commentVal = re.sub('\n', '\n#', str(oval)) 309*4882a593Smuzhiyun if history: 310*4882a593Smuzhiyun if len(history) == 1: 311*4882a593Smuzhiyun o.write("#\n# $%s\n" % var) 312*4882a593Smuzhiyun else: 313*4882a593Smuzhiyun o.write("#\n# $%s [%d operations]\n" % (var, len(history))) 314*4882a593Smuzhiyun for event in history: 315*4882a593Smuzhiyun # o.write("# %s\n" % str(event)) 316*4882a593Smuzhiyun if 'func' in event: 317*4882a593Smuzhiyun # If we have a function listed, this is internal 318*4882a593Smuzhiyun # code, not an operation in a config file, and the 319*4882a593Smuzhiyun # full path is distracting. 320*4882a593Smuzhiyun event['file'] = re.sub('.*/', '', event['file']) 321*4882a593Smuzhiyun display_func = ' [%s]' % event['func'] 322*4882a593Smuzhiyun else: 323*4882a593Smuzhiyun display_func = '' 324*4882a593Smuzhiyun if 'flag' in event: 325*4882a593Smuzhiyun flag = '[%s] ' % (event['flag']) 326*4882a593Smuzhiyun else: 327*4882a593Smuzhiyun flag = '' 328*4882a593Smuzhiyun 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']))) 329*4882a593Smuzhiyun if len(history) > 1: 330*4882a593Smuzhiyun o.write("# pre-expansion value:\n") 331*4882a593Smuzhiyun o.write('# "%s"\n' % (commentVal)) 332*4882a593Smuzhiyun else: 333*4882a593Smuzhiyun o.write("#\n# $%s\n# [no history recorded]\n#\n" % var) 334*4882a593Smuzhiyun o.write('# "%s"\n' % (commentVal)) 335*4882a593Smuzhiyun 336*4882a593Smuzhiyun def get_variable_files(self, var): 337*4882a593Smuzhiyun """Get the files where operations are made on a variable""" 338*4882a593Smuzhiyun var_history = self.variable(var) 339*4882a593Smuzhiyun files = [] 340*4882a593Smuzhiyun for event in var_history: 341*4882a593Smuzhiyun files.append(event['file']) 342*4882a593Smuzhiyun return files 343*4882a593Smuzhiyun 344*4882a593Smuzhiyun def get_variable_lines(self, var, f): 345*4882a593Smuzhiyun """Get the line where a operation is made on a variable in file f""" 346*4882a593Smuzhiyun var_history = self.variable(var) 347*4882a593Smuzhiyun lines = [] 348*4882a593Smuzhiyun for event in var_history: 349*4882a593Smuzhiyun if f== event['file']: 350*4882a593Smuzhiyun line = event['line'] 351*4882a593Smuzhiyun lines.append(line) 352*4882a593Smuzhiyun return lines 353*4882a593Smuzhiyun 354*4882a593Smuzhiyun def get_variable_refs(self, var): 355*4882a593Smuzhiyun """Return a dict of file/line references""" 356*4882a593Smuzhiyun var_history = self.variable(var) 357*4882a593Smuzhiyun refs = {} 358*4882a593Smuzhiyun for event in var_history: 359*4882a593Smuzhiyun if event['file'] not in refs: 360*4882a593Smuzhiyun refs[event['file']] = [] 361*4882a593Smuzhiyun refs[event['file']].append(event['line']) 362*4882a593Smuzhiyun return refs 363*4882a593Smuzhiyun 364*4882a593Smuzhiyun def get_variable_items_files(self, var): 365*4882a593Smuzhiyun """ 366*4882a593Smuzhiyun Use variable history to map items added to a list variable and 367*4882a593Smuzhiyun the files in which they were added. 368*4882a593Smuzhiyun """ 369*4882a593Smuzhiyun d = self.dataroot 370*4882a593Smuzhiyun history = self.variable(var) 371*4882a593Smuzhiyun finalitems = (d.getVar(var) or '').split() 372*4882a593Smuzhiyun filemap = {} 373*4882a593Smuzhiyun isset = False 374*4882a593Smuzhiyun for event in history: 375*4882a593Smuzhiyun if 'flag' in event: 376*4882a593Smuzhiyun continue 377*4882a593Smuzhiyun if event['op'] == ':remove': 378*4882a593Smuzhiyun continue 379*4882a593Smuzhiyun if isset and event['op'] == 'set?': 380*4882a593Smuzhiyun continue 381*4882a593Smuzhiyun isset = True 382*4882a593Smuzhiyun items = d.expand(event['detail']).split() 383*4882a593Smuzhiyun for item in items: 384*4882a593Smuzhiyun # This is a little crude but is belt-and-braces to avoid us 385*4882a593Smuzhiyun # having to handle every possible operation type specifically 386*4882a593Smuzhiyun if item in finalitems and not item in filemap: 387*4882a593Smuzhiyun filemap[item] = event['file'] 388*4882a593Smuzhiyun return filemap 389*4882a593Smuzhiyun 390*4882a593Smuzhiyun def del_var_history(self, var, f=None, line=None): 391*4882a593Smuzhiyun """If file f and line are not given, the entire history of var is deleted""" 392*4882a593Smuzhiyun if var in self.variables: 393*4882a593Smuzhiyun if f and line: 394*4882a593Smuzhiyun self.variables[var] = [ x for x in self.variables[var] if x['file']!=f and x['line']!=line] 395*4882a593Smuzhiyun else: 396*4882a593Smuzhiyun self.variables[var] = [] 397*4882a593Smuzhiyun 398*4882a593Smuzhiyundef _print_rename_error(var, loginfo, renamedvars, fullvar=None): 399*4882a593Smuzhiyun info = "" 400*4882a593Smuzhiyun if "file" in loginfo: 401*4882a593Smuzhiyun info = " file: %s" % loginfo["file"] 402*4882a593Smuzhiyun if "line" in loginfo: 403*4882a593Smuzhiyun info += " line: %s" % loginfo["line"] 404*4882a593Smuzhiyun if fullvar and fullvar != var: 405*4882a593Smuzhiyun info += " referenced as: %s" % fullvar 406*4882a593Smuzhiyun if info: 407*4882a593Smuzhiyun info = " (%s)" % info.strip() 408*4882a593Smuzhiyun renameinfo = renamedvars[var] 409*4882a593Smuzhiyun if " " in renameinfo: 410*4882a593Smuzhiyun # A space signals a string to display instead of a rename 411*4882a593Smuzhiyun bb.erroronce('Variable %s %s%s' % (var, renameinfo, info)) 412*4882a593Smuzhiyun else: 413*4882a593Smuzhiyun bb.erroronce('Variable %s has been renamed to %s%s' % (var, renameinfo, info)) 414*4882a593Smuzhiyun 415*4882a593Smuzhiyunclass DataSmart(MutableMapping): 416*4882a593Smuzhiyun def __init__(self): 417*4882a593Smuzhiyun self.dict = {} 418*4882a593Smuzhiyun 419*4882a593Smuzhiyun self.inchistory = IncludeHistory() 420*4882a593Smuzhiyun self.varhistory = VariableHistory(self) 421*4882a593Smuzhiyun self._tracking = False 422*4882a593Smuzhiyun self._var_renames = {} 423*4882a593Smuzhiyun self._var_renames.update(bitbake_renamed_vars) 424*4882a593Smuzhiyun 425*4882a593Smuzhiyun self.expand_cache = {} 426*4882a593Smuzhiyun 427*4882a593Smuzhiyun # cookie monster tribute 428*4882a593Smuzhiyun # Need to be careful about writes to overridedata as 429*4882a593Smuzhiyun # its only a shallow copy, could influence other data store 430*4882a593Smuzhiyun # copies! 431*4882a593Smuzhiyun self.overridedata = {} 432*4882a593Smuzhiyun self.overrides = None 433*4882a593Smuzhiyun self.overridevars = set(["OVERRIDES", "FILE"]) 434*4882a593Smuzhiyun self.inoverride = False 435*4882a593Smuzhiyun 436*4882a593Smuzhiyun def enableTracking(self): 437*4882a593Smuzhiyun self._tracking = True 438*4882a593Smuzhiyun 439*4882a593Smuzhiyun def disableTracking(self): 440*4882a593Smuzhiyun self._tracking = False 441*4882a593Smuzhiyun 442*4882a593Smuzhiyun def expandWithRefs(self, s, varname): 443*4882a593Smuzhiyun 444*4882a593Smuzhiyun if not isinstance(s, str): # sanity check 445*4882a593Smuzhiyun return VariableParse(varname, self, s) 446*4882a593Smuzhiyun 447*4882a593Smuzhiyun varparse = VariableParse(varname, self) 448*4882a593Smuzhiyun 449*4882a593Smuzhiyun while s.find('${') != -1: 450*4882a593Smuzhiyun olds = s 451*4882a593Smuzhiyun try: 452*4882a593Smuzhiyun s = __expand_var_regexp__.sub(varparse.var_sub, s) 453*4882a593Smuzhiyun try: 454*4882a593Smuzhiyun s = __expand_python_regexp__.sub(varparse.python_sub, s) 455*4882a593Smuzhiyun except SyntaxError as e: 456*4882a593Smuzhiyun # Likely unmatched brackets, just don't expand the expression 457*4882a593Smuzhiyun if e.msg != "EOL while scanning string literal" and not e.msg.startswith("unterminated string literal"): 458*4882a593Smuzhiyun raise 459*4882a593Smuzhiyun if s == olds: 460*4882a593Smuzhiyun break 461*4882a593Smuzhiyun except ExpansionError as e: 462*4882a593Smuzhiyun e.addVar(varname) 463*4882a593Smuzhiyun raise 464*4882a593Smuzhiyun except bb.parse.SkipRecipe: 465*4882a593Smuzhiyun raise 466*4882a593Smuzhiyun except bb.BBHandledException: 467*4882a593Smuzhiyun raise 468*4882a593Smuzhiyun except Exception as exc: 469*4882a593Smuzhiyun tb = sys.exc_info()[2] 470*4882a593Smuzhiyun raise ExpansionError(varname, s, exc).with_traceback(tb) from exc 471*4882a593Smuzhiyun 472*4882a593Smuzhiyun varparse.value = s 473*4882a593Smuzhiyun 474*4882a593Smuzhiyun return varparse 475*4882a593Smuzhiyun 476*4882a593Smuzhiyun def expand(self, s, varname = None): 477*4882a593Smuzhiyun return self.expandWithRefs(s, varname).value 478*4882a593Smuzhiyun 479*4882a593Smuzhiyun def finalize(self, parent = False): 480*4882a593Smuzhiyun return 481*4882a593Smuzhiyun 482*4882a593Smuzhiyun def internal_finalize(self, parent = False): 483*4882a593Smuzhiyun """Performs final steps upon the datastore, including application of overrides""" 484*4882a593Smuzhiyun self.overrides = None 485*4882a593Smuzhiyun 486*4882a593Smuzhiyun def need_overrides(self): 487*4882a593Smuzhiyun if self.overrides is not None: 488*4882a593Smuzhiyun return 489*4882a593Smuzhiyun if self.inoverride: 490*4882a593Smuzhiyun return 491*4882a593Smuzhiyun for count in range(5): 492*4882a593Smuzhiyun self.inoverride = True 493*4882a593Smuzhiyun # Can end up here recursively so setup dummy values 494*4882a593Smuzhiyun self.overrides = [] 495*4882a593Smuzhiyun self.overridesset = set() 496*4882a593Smuzhiyun self.overrides = (self.getVar("OVERRIDES") or "").split(":") or [] 497*4882a593Smuzhiyun self.overridesset = set(self.overrides) 498*4882a593Smuzhiyun self.inoverride = False 499*4882a593Smuzhiyun self.expand_cache = {} 500*4882a593Smuzhiyun newoverrides = (self.getVar("OVERRIDES") or "").split(":") or [] 501*4882a593Smuzhiyun if newoverrides == self.overrides: 502*4882a593Smuzhiyun break 503*4882a593Smuzhiyun self.overrides = newoverrides 504*4882a593Smuzhiyun self.overridesset = set(self.overrides) 505*4882a593Smuzhiyun else: 506*4882a593Smuzhiyun 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.") 507*4882a593Smuzhiyun 508*4882a593Smuzhiyun def initVar(self, var): 509*4882a593Smuzhiyun self.expand_cache = {} 510*4882a593Smuzhiyun if not var in self.dict: 511*4882a593Smuzhiyun self.dict[var] = {} 512*4882a593Smuzhiyun 513*4882a593Smuzhiyun def _findVar(self, var): 514*4882a593Smuzhiyun dest = self.dict 515*4882a593Smuzhiyun while dest: 516*4882a593Smuzhiyun if var in dest: 517*4882a593Smuzhiyun return dest[var], self.overridedata.get(var, None) 518*4882a593Smuzhiyun 519*4882a593Smuzhiyun if "_data" not in dest: 520*4882a593Smuzhiyun break 521*4882a593Smuzhiyun dest = dest["_data"] 522*4882a593Smuzhiyun return None, self.overridedata.get(var, None) 523*4882a593Smuzhiyun 524*4882a593Smuzhiyun def _makeShadowCopy(self, var): 525*4882a593Smuzhiyun if var in self.dict: 526*4882a593Smuzhiyun return 527*4882a593Smuzhiyun 528*4882a593Smuzhiyun local_var, _ = self._findVar(var) 529*4882a593Smuzhiyun 530*4882a593Smuzhiyun if local_var: 531*4882a593Smuzhiyun self.dict[var] = copy.copy(local_var) 532*4882a593Smuzhiyun else: 533*4882a593Smuzhiyun self.initVar(var) 534*4882a593Smuzhiyun 535*4882a593Smuzhiyun def hasOverrides(self, var): 536*4882a593Smuzhiyun return var in self.overridedata 537*4882a593Smuzhiyun 538*4882a593Smuzhiyun def setVar(self, var, value, **loginfo): 539*4882a593Smuzhiyun #print("var=" + str(var) + " val=" + str(value)) 540*4882a593Smuzhiyun 541*4882a593Smuzhiyun if not var.startswith("__anon_") and ("_append" in var or "_prepend" in var or "_remove" in var): 542*4882a593Smuzhiyun info = "%s" % var 543*4882a593Smuzhiyun if "file" in loginfo: 544*4882a593Smuzhiyun info += " file: %s" % loginfo["file"] 545*4882a593Smuzhiyun if "line" in loginfo: 546*4882a593Smuzhiyun info += " line: %s" % loginfo["line"] 547*4882a593Smuzhiyun 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) 548*4882a593Smuzhiyun 549*4882a593Smuzhiyun shortvar = var.split(":", 1)[0] 550*4882a593Smuzhiyun if shortvar in self._var_renames: 551*4882a593Smuzhiyun _print_rename_error(shortvar, loginfo, self._var_renames, fullvar=var) 552*4882a593Smuzhiyun # Mark that we have seen a renamed variable 553*4882a593Smuzhiyun self.setVar("_FAILPARSINGERRORHANDLED", True) 554*4882a593Smuzhiyun 555*4882a593Smuzhiyun self.expand_cache = {} 556*4882a593Smuzhiyun parsing=False 557*4882a593Smuzhiyun if 'parsing' in loginfo: 558*4882a593Smuzhiyun parsing=True 559*4882a593Smuzhiyun 560*4882a593Smuzhiyun if 'op' not in loginfo: 561*4882a593Smuzhiyun loginfo['op'] = "set" 562*4882a593Smuzhiyun 563*4882a593Smuzhiyun match = __setvar_regexp__.match(var) 564*4882a593Smuzhiyun if match and match.group("keyword") in __setvar_keyword__: 565*4882a593Smuzhiyun base = match.group('base') 566*4882a593Smuzhiyun keyword = match.group("keyword") 567*4882a593Smuzhiyun override = match.group('add') 568*4882a593Smuzhiyun l = self.getVarFlag(base, keyword, False) or [] 569*4882a593Smuzhiyun l.append([value, override]) 570*4882a593Smuzhiyun self.setVarFlag(base, keyword, l, ignore=True) 571*4882a593Smuzhiyun # And cause that to be recorded: 572*4882a593Smuzhiyun loginfo['detail'] = value 573*4882a593Smuzhiyun loginfo['variable'] = base 574*4882a593Smuzhiyun if override: 575*4882a593Smuzhiyun loginfo['op'] = '%s[%s]' % (keyword, override) 576*4882a593Smuzhiyun else: 577*4882a593Smuzhiyun loginfo['op'] = keyword 578*4882a593Smuzhiyun self.varhistory.record(**loginfo) 579*4882a593Smuzhiyun # todo make sure keyword is not __doc__ or __module__ 580*4882a593Smuzhiyun # pay the cookie monster 581*4882a593Smuzhiyun 582*4882a593Smuzhiyun # more cookies for the cookie monster 583*4882a593Smuzhiyun if ':' in var: 584*4882a593Smuzhiyun self._setvar_update_overrides(base, **loginfo) 585*4882a593Smuzhiyun 586*4882a593Smuzhiyun if base in self.overridevars: 587*4882a593Smuzhiyun self._setvar_update_overridevars(var, value) 588*4882a593Smuzhiyun return 589*4882a593Smuzhiyun 590*4882a593Smuzhiyun if not var in self.dict: 591*4882a593Smuzhiyun self._makeShadowCopy(var) 592*4882a593Smuzhiyun 593*4882a593Smuzhiyun if not parsing: 594*4882a593Smuzhiyun if ":append" in self.dict[var]: 595*4882a593Smuzhiyun del self.dict[var][":append"] 596*4882a593Smuzhiyun if ":prepend" in self.dict[var]: 597*4882a593Smuzhiyun del self.dict[var][":prepend"] 598*4882a593Smuzhiyun if ":remove" in self.dict[var]: 599*4882a593Smuzhiyun del self.dict[var][":remove"] 600*4882a593Smuzhiyun if var in self.overridedata: 601*4882a593Smuzhiyun active = [] 602*4882a593Smuzhiyun self.need_overrides() 603*4882a593Smuzhiyun for (r, o) in self.overridedata[var]: 604*4882a593Smuzhiyun if o in self.overridesset: 605*4882a593Smuzhiyun active.append(r) 606*4882a593Smuzhiyun elif ":" in o: 607*4882a593Smuzhiyun if set(o.split(":")).issubset(self.overridesset): 608*4882a593Smuzhiyun active.append(r) 609*4882a593Smuzhiyun for a in active: 610*4882a593Smuzhiyun self.delVar(a) 611*4882a593Smuzhiyun del self.overridedata[var] 612*4882a593Smuzhiyun 613*4882a593Smuzhiyun # more cookies for the cookie monster 614*4882a593Smuzhiyun if ':' in var: 615*4882a593Smuzhiyun self._setvar_update_overrides(var, **loginfo) 616*4882a593Smuzhiyun 617*4882a593Smuzhiyun # setting var 618*4882a593Smuzhiyun self.dict[var]["_content"] = value 619*4882a593Smuzhiyun self.varhistory.record(**loginfo) 620*4882a593Smuzhiyun 621*4882a593Smuzhiyun if var in self.overridevars: 622*4882a593Smuzhiyun self._setvar_update_overridevars(var, value) 623*4882a593Smuzhiyun 624*4882a593Smuzhiyun def _setvar_update_overridevars(self, var, value): 625*4882a593Smuzhiyun vardata = self.expandWithRefs(value, var) 626*4882a593Smuzhiyun new = vardata.references 627*4882a593Smuzhiyun new.update(vardata.contains.keys()) 628*4882a593Smuzhiyun while not new.issubset(self.overridevars): 629*4882a593Smuzhiyun nextnew = set() 630*4882a593Smuzhiyun self.overridevars.update(new) 631*4882a593Smuzhiyun for i in new: 632*4882a593Smuzhiyun vardata = self.expandWithRefs(self.getVar(i), i) 633*4882a593Smuzhiyun nextnew.update(vardata.references) 634*4882a593Smuzhiyun nextnew.update(vardata.contains.keys()) 635*4882a593Smuzhiyun new = nextnew 636*4882a593Smuzhiyun self.internal_finalize(True) 637*4882a593Smuzhiyun 638*4882a593Smuzhiyun def _setvar_update_overrides(self, var, **loginfo): 639*4882a593Smuzhiyun # aka pay the cookie monster 640*4882a593Smuzhiyun override = var[var.rfind(':')+1:] 641*4882a593Smuzhiyun shortvar = var[:var.rfind(':')] 642*4882a593Smuzhiyun while override and __override_regexp__.match(override): 643*4882a593Smuzhiyun if shortvar not in self.overridedata: 644*4882a593Smuzhiyun self.overridedata[shortvar] = [] 645*4882a593Smuzhiyun if [var, override] not in self.overridedata[shortvar]: 646*4882a593Smuzhiyun # Force CoW by recreating the list first 647*4882a593Smuzhiyun self.overridedata[shortvar] = list(self.overridedata[shortvar]) 648*4882a593Smuzhiyun self.overridedata[shortvar].append([var, override]) 649*4882a593Smuzhiyun override = None 650*4882a593Smuzhiyun if ":" in shortvar: 651*4882a593Smuzhiyun override = var[shortvar.rfind(':')+1:] 652*4882a593Smuzhiyun shortvar = var[:shortvar.rfind(':')] 653*4882a593Smuzhiyun if len(shortvar) == 0: 654*4882a593Smuzhiyun override = None 655*4882a593Smuzhiyun 656*4882a593Smuzhiyun def getVar(self, var, expand=True, noweakdefault=False, parsing=False): 657*4882a593Smuzhiyun return self.getVarFlag(var, "_content", expand, noweakdefault, parsing) 658*4882a593Smuzhiyun 659*4882a593Smuzhiyun def renameVar(self, key, newkey, **loginfo): 660*4882a593Smuzhiyun """ 661*4882a593Smuzhiyun Rename the variable key to newkey 662*4882a593Smuzhiyun """ 663*4882a593Smuzhiyun if key == newkey: 664*4882a593Smuzhiyun bb.warn("Calling renameVar with equivalent keys (%s) is invalid" % key) 665*4882a593Smuzhiyun return 666*4882a593Smuzhiyun 667*4882a593Smuzhiyun val = self.getVar(key, 0, parsing=True) 668*4882a593Smuzhiyun if val is not None: 669*4882a593Smuzhiyun self.varhistory.rename_variable_hist(key, newkey) 670*4882a593Smuzhiyun loginfo['variable'] = newkey 671*4882a593Smuzhiyun loginfo['op'] = 'rename from %s' % key 672*4882a593Smuzhiyun loginfo['detail'] = val 673*4882a593Smuzhiyun self.varhistory.record(**loginfo) 674*4882a593Smuzhiyun self.setVar(newkey, val, ignore=True, parsing=True) 675*4882a593Smuzhiyun 676*4882a593Smuzhiyun srcflags = self.getVarFlags(key, False, True) or {} 677*4882a593Smuzhiyun for i in srcflags: 678*4882a593Smuzhiyun if i not in (__setvar_keyword__): 679*4882a593Smuzhiyun continue 680*4882a593Smuzhiyun src = srcflags[i] 681*4882a593Smuzhiyun 682*4882a593Smuzhiyun dest = self.getVarFlag(newkey, i, False) or [] 683*4882a593Smuzhiyun dest.extend(src) 684*4882a593Smuzhiyun self.setVarFlag(newkey, i, dest, ignore=True) 685*4882a593Smuzhiyun 686*4882a593Smuzhiyun if key in self.overridedata: 687*4882a593Smuzhiyun self.overridedata[newkey] = [] 688*4882a593Smuzhiyun for (v, o) in self.overridedata[key]: 689*4882a593Smuzhiyun self.overridedata[newkey].append([v.replace(key, newkey), o]) 690*4882a593Smuzhiyun self.renameVar(v, v.replace(key, newkey)) 691*4882a593Smuzhiyun 692*4882a593Smuzhiyun if ':' in newkey and val is None: 693*4882a593Smuzhiyun self._setvar_update_overrides(newkey, **loginfo) 694*4882a593Smuzhiyun 695*4882a593Smuzhiyun loginfo['variable'] = key 696*4882a593Smuzhiyun loginfo['op'] = 'rename (to)' 697*4882a593Smuzhiyun loginfo['detail'] = newkey 698*4882a593Smuzhiyun self.varhistory.record(**loginfo) 699*4882a593Smuzhiyun self.delVar(key, ignore=True) 700*4882a593Smuzhiyun 701*4882a593Smuzhiyun def appendVar(self, var, value, **loginfo): 702*4882a593Smuzhiyun loginfo['op'] = 'append' 703*4882a593Smuzhiyun self.varhistory.record(**loginfo) 704*4882a593Smuzhiyun self.setVar(var + ":append", value, ignore=True, parsing=True) 705*4882a593Smuzhiyun 706*4882a593Smuzhiyun def prependVar(self, var, value, **loginfo): 707*4882a593Smuzhiyun loginfo['op'] = 'prepend' 708*4882a593Smuzhiyun self.varhistory.record(**loginfo) 709*4882a593Smuzhiyun self.setVar(var + ":prepend", value, ignore=True, parsing=True) 710*4882a593Smuzhiyun 711*4882a593Smuzhiyun def delVar(self, var, **loginfo): 712*4882a593Smuzhiyun self.expand_cache = {} 713*4882a593Smuzhiyun 714*4882a593Smuzhiyun loginfo['detail'] = "" 715*4882a593Smuzhiyun loginfo['op'] = 'del' 716*4882a593Smuzhiyun self.varhistory.record(**loginfo) 717*4882a593Smuzhiyun self.dict[var] = {} 718*4882a593Smuzhiyun if var in self.overridedata: 719*4882a593Smuzhiyun del self.overridedata[var] 720*4882a593Smuzhiyun if ':' in var: 721*4882a593Smuzhiyun override = var[var.rfind(':')+1:] 722*4882a593Smuzhiyun shortvar = var[:var.rfind(':')] 723*4882a593Smuzhiyun while override and override.islower(): 724*4882a593Smuzhiyun try: 725*4882a593Smuzhiyun if shortvar in self.overridedata: 726*4882a593Smuzhiyun # Force CoW by recreating the list first 727*4882a593Smuzhiyun self.overridedata[shortvar] = list(self.overridedata[shortvar]) 728*4882a593Smuzhiyun self.overridedata[shortvar].remove([var, override]) 729*4882a593Smuzhiyun except ValueError as e: 730*4882a593Smuzhiyun pass 731*4882a593Smuzhiyun override = None 732*4882a593Smuzhiyun if ":" in shortvar: 733*4882a593Smuzhiyun override = var[shortvar.rfind(':')+1:] 734*4882a593Smuzhiyun shortvar = var[:shortvar.rfind(':')] 735*4882a593Smuzhiyun if len(shortvar) == 0: 736*4882a593Smuzhiyun override = None 737*4882a593Smuzhiyun 738*4882a593Smuzhiyun def setVarFlag(self, var, flag, value, **loginfo): 739*4882a593Smuzhiyun self.expand_cache = {} 740*4882a593Smuzhiyun 741*4882a593Smuzhiyun if var == "BB_RENAMED_VARIABLES": 742*4882a593Smuzhiyun self._var_renames[flag] = value 743*4882a593Smuzhiyun 744*4882a593Smuzhiyun if var in self._var_renames: 745*4882a593Smuzhiyun _print_rename_error(var, loginfo, self._var_renames) 746*4882a593Smuzhiyun # Mark that we have seen a renamed variable 747*4882a593Smuzhiyun self.setVar("_FAILPARSINGERRORHANDLED", True) 748*4882a593Smuzhiyun 749*4882a593Smuzhiyun if 'op' not in loginfo: 750*4882a593Smuzhiyun loginfo['op'] = "set" 751*4882a593Smuzhiyun loginfo['flag'] = flag 752*4882a593Smuzhiyun self.varhistory.record(**loginfo) 753*4882a593Smuzhiyun if not var in self.dict: 754*4882a593Smuzhiyun self._makeShadowCopy(var) 755*4882a593Smuzhiyun self.dict[var][flag] = value 756*4882a593Smuzhiyun 757*4882a593Smuzhiyun if flag == "_defaultval" and ':' in var: 758*4882a593Smuzhiyun self._setvar_update_overrides(var, **loginfo) 759*4882a593Smuzhiyun if flag == "_defaultval" and var in self.overridevars: 760*4882a593Smuzhiyun self._setvar_update_overridevars(var, value) 761*4882a593Smuzhiyun 762*4882a593Smuzhiyun if flag == "unexport" or flag == "export": 763*4882a593Smuzhiyun if not "__exportlist" in self.dict: 764*4882a593Smuzhiyun self._makeShadowCopy("__exportlist") 765*4882a593Smuzhiyun if not "_content" in self.dict["__exportlist"]: 766*4882a593Smuzhiyun self.dict["__exportlist"]["_content"] = set() 767*4882a593Smuzhiyun self.dict["__exportlist"]["_content"].add(var) 768*4882a593Smuzhiyun 769*4882a593Smuzhiyun def getVarFlag(self, var, flag, expand=True, noweakdefault=False, parsing=False, retparser=False): 770*4882a593Smuzhiyun if flag == "_content": 771*4882a593Smuzhiyun cachename = var 772*4882a593Smuzhiyun else: 773*4882a593Smuzhiyun if not flag: 774*4882a593Smuzhiyun bb.warn("Calling getVarFlag with flag unset is invalid") 775*4882a593Smuzhiyun return None 776*4882a593Smuzhiyun cachename = var + "[" + flag + "]" 777*4882a593Smuzhiyun 778*4882a593Smuzhiyun if expand and cachename in self.expand_cache: 779*4882a593Smuzhiyun return self.expand_cache[cachename].value 780*4882a593Smuzhiyun 781*4882a593Smuzhiyun local_var, overridedata = self._findVar(var) 782*4882a593Smuzhiyun value = None 783*4882a593Smuzhiyun removes = set() 784*4882a593Smuzhiyun if flag == "_content" and overridedata is not None and not parsing: 785*4882a593Smuzhiyun match = False 786*4882a593Smuzhiyun active = {} 787*4882a593Smuzhiyun self.need_overrides() 788*4882a593Smuzhiyun for (r, o) in overridedata: 789*4882a593Smuzhiyun # FIXME What about double overrides both with "_" in the name? 790*4882a593Smuzhiyun if o in self.overridesset: 791*4882a593Smuzhiyun active[o] = r 792*4882a593Smuzhiyun elif ":" in o: 793*4882a593Smuzhiyun if set(o.split(":")).issubset(self.overridesset): 794*4882a593Smuzhiyun active[o] = r 795*4882a593Smuzhiyun 796*4882a593Smuzhiyun mod = True 797*4882a593Smuzhiyun while mod: 798*4882a593Smuzhiyun mod = False 799*4882a593Smuzhiyun for o in self.overrides: 800*4882a593Smuzhiyun for a in active.copy(): 801*4882a593Smuzhiyun if a.endswith(":" + o): 802*4882a593Smuzhiyun t = active[a] 803*4882a593Smuzhiyun del active[a] 804*4882a593Smuzhiyun active[a.replace(":" + o, "")] = t 805*4882a593Smuzhiyun mod = True 806*4882a593Smuzhiyun elif a == o: 807*4882a593Smuzhiyun match = active[a] 808*4882a593Smuzhiyun del active[a] 809*4882a593Smuzhiyun if match: 810*4882a593Smuzhiyun value, subparser = self.getVarFlag(match, "_content", False, retparser=True) 811*4882a593Smuzhiyun if hasattr(subparser, "removes"): 812*4882a593Smuzhiyun # We have to carry the removes from the overridden variable to apply at the 813*4882a593Smuzhiyun # end of processing 814*4882a593Smuzhiyun removes = subparser.removes 815*4882a593Smuzhiyun 816*4882a593Smuzhiyun if local_var is not None and value is None: 817*4882a593Smuzhiyun if flag in local_var: 818*4882a593Smuzhiyun value = copy.copy(local_var[flag]) 819*4882a593Smuzhiyun elif flag == "_content" and "_defaultval" in local_var and not noweakdefault: 820*4882a593Smuzhiyun value = copy.copy(local_var["_defaultval"]) 821*4882a593Smuzhiyun 822*4882a593Smuzhiyun 823*4882a593Smuzhiyun if flag == "_content" and local_var is not None and ":append" in local_var and not parsing: 824*4882a593Smuzhiyun self.need_overrides() 825*4882a593Smuzhiyun for (r, o) in local_var[":append"]: 826*4882a593Smuzhiyun match = True 827*4882a593Smuzhiyun if o: 828*4882a593Smuzhiyun for o2 in o.split(":"): 829*4882a593Smuzhiyun if not o2 in self.overrides: 830*4882a593Smuzhiyun match = False 831*4882a593Smuzhiyun if match: 832*4882a593Smuzhiyun if value is None: 833*4882a593Smuzhiyun value = "" 834*4882a593Smuzhiyun value = value + r 835*4882a593Smuzhiyun 836*4882a593Smuzhiyun if flag == "_content" and local_var is not None and ":prepend" in local_var and not parsing: 837*4882a593Smuzhiyun self.need_overrides() 838*4882a593Smuzhiyun for (r, o) in local_var[":prepend"]: 839*4882a593Smuzhiyun 840*4882a593Smuzhiyun match = True 841*4882a593Smuzhiyun if o: 842*4882a593Smuzhiyun for o2 in o.split(":"): 843*4882a593Smuzhiyun if not o2 in self.overrides: 844*4882a593Smuzhiyun match = False 845*4882a593Smuzhiyun if match: 846*4882a593Smuzhiyun if value is None: 847*4882a593Smuzhiyun value = "" 848*4882a593Smuzhiyun value = r + value 849*4882a593Smuzhiyun 850*4882a593Smuzhiyun parser = None 851*4882a593Smuzhiyun if expand or retparser: 852*4882a593Smuzhiyun parser = self.expandWithRefs(value, cachename) 853*4882a593Smuzhiyun if expand: 854*4882a593Smuzhiyun value = parser.value 855*4882a593Smuzhiyun 856*4882a593Smuzhiyun if value and flag == "_content" and local_var is not None and ":remove" in local_var and not parsing: 857*4882a593Smuzhiyun self.need_overrides() 858*4882a593Smuzhiyun for (r, o) in local_var[":remove"]: 859*4882a593Smuzhiyun match = True 860*4882a593Smuzhiyun if o: 861*4882a593Smuzhiyun for o2 in o.split(":"): 862*4882a593Smuzhiyun if not o2 in self.overrides: 863*4882a593Smuzhiyun match = False 864*4882a593Smuzhiyun if match: 865*4882a593Smuzhiyun removes.add(r) 866*4882a593Smuzhiyun 867*4882a593Smuzhiyun if value and flag == "_content" and not parsing: 868*4882a593Smuzhiyun if removes and parser: 869*4882a593Smuzhiyun expanded_removes = {} 870*4882a593Smuzhiyun for r in removes: 871*4882a593Smuzhiyun expanded_removes[r] = self.expand(r).split() 872*4882a593Smuzhiyun 873*4882a593Smuzhiyun parser.removes = set() 874*4882a593Smuzhiyun val = [] 875*4882a593Smuzhiyun for v in __whitespace_split__.split(parser.value): 876*4882a593Smuzhiyun skip = False 877*4882a593Smuzhiyun for r in removes: 878*4882a593Smuzhiyun if v in expanded_removes[r]: 879*4882a593Smuzhiyun parser.removes.add(r) 880*4882a593Smuzhiyun skip = True 881*4882a593Smuzhiyun if skip: 882*4882a593Smuzhiyun continue 883*4882a593Smuzhiyun val.append(v) 884*4882a593Smuzhiyun parser.value = "".join(val) 885*4882a593Smuzhiyun if expand: 886*4882a593Smuzhiyun value = parser.value 887*4882a593Smuzhiyun 888*4882a593Smuzhiyun if parser: 889*4882a593Smuzhiyun self.expand_cache[cachename] = parser 890*4882a593Smuzhiyun 891*4882a593Smuzhiyun if retparser: 892*4882a593Smuzhiyun return value, parser 893*4882a593Smuzhiyun 894*4882a593Smuzhiyun return value 895*4882a593Smuzhiyun 896*4882a593Smuzhiyun def delVarFlag(self, var, flag, **loginfo): 897*4882a593Smuzhiyun self.expand_cache = {} 898*4882a593Smuzhiyun 899*4882a593Smuzhiyun local_var, _ = self._findVar(var) 900*4882a593Smuzhiyun if not local_var: 901*4882a593Smuzhiyun return 902*4882a593Smuzhiyun if not var in self.dict: 903*4882a593Smuzhiyun self._makeShadowCopy(var) 904*4882a593Smuzhiyun 905*4882a593Smuzhiyun if var in self.dict and flag in self.dict[var]: 906*4882a593Smuzhiyun loginfo['detail'] = "" 907*4882a593Smuzhiyun loginfo['op'] = 'delFlag' 908*4882a593Smuzhiyun loginfo['flag'] = flag 909*4882a593Smuzhiyun self.varhistory.record(**loginfo) 910*4882a593Smuzhiyun 911*4882a593Smuzhiyun del self.dict[var][flag] 912*4882a593Smuzhiyun 913*4882a593Smuzhiyun def appendVarFlag(self, var, flag, value, **loginfo): 914*4882a593Smuzhiyun loginfo['op'] = 'append' 915*4882a593Smuzhiyun loginfo['flag'] = flag 916*4882a593Smuzhiyun self.varhistory.record(**loginfo) 917*4882a593Smuzhiyun newvalue = (self.getVarFlag(var, flag, False) or "") + value 918*4882a593Smuzhiyun self.setVarFlag(var, flag, newvalue, ignore=True) 919*4882a593Smuzhiyun 920*4882a593Smuzhiyun def prependVarFlag(self, var, flag, value, **loginfo): 921*4882a593Smuzhiyun loginfo['op'] = 'prepend' 922*4882a593Smuzhiyun loginfo['flag'] = flag 923*4882a593Smuzhiyun self.varhistory.record(**loginfo) 924*4882a593Smuzhiyun newvalue = value + (self.getVarFlag(var, flag, False) or "") 925*4882a593Smuzhiyun self.setVarFlag(var, flag, newvalue, ignore=True) 926*4882a593Smuzhiyun 927*4882a593Smuzhiyun def setVarFlags(self, var, flags, **loginfo): 928*4882a593Smuzhiyun self.expand_cache = {} 929*4882a593Smuzhiyun infer_caller_details(loginfo) 930*4882a593Smuzhiyun if not var in self.dict: 931*4882a593Smuzhiyun self._makeShadowCopy(var) 932*4882a593Smuzhiyun 933*4882a593Smuzhiyun for i in flags: 934*4882a593Smuzhiyun if i == "_content": 935*4882a593Smuzhiyun continue 936*4882a593Smuzhiyun loginfo['flag'] = i 937*4882a593Smuzhiyun loginfo['detail'] = flags[i] 938*4882a593Smuzhiyun self.varhistory.record(**loginfo) 939*4882a593Smuzhiyun self.dict[var][i] = flags[i] 940*4882a593Smuzhiyun 941*4882a593Smuzhiyun def getVarFlags(self, var, expand = False, internalflags=False): 942*4882a593Smuzhiyun local_var, _ = self._findVar(var) 943*4882a593Smuzhiyun flags = {} 944*4882a593Smuzhiyun 945*4882a593Smuzhiyun if local_var: 946*4882a593Smuzhiyun for i in local_var: 947*4882a593Smuzhiyun if i.startswith(("_", ":")) and not internalflags: 948*4882a593Smuzhiyun continue 949*4882a593Smuzhiyun flags[i] = local_var[i] 950*4882a593Smuzhiyun if expand and i in expand: 951*4882a593Smuzhiyun flags[i] = self.expand(flags[i], var + "[" + i + "]") 952*4882a593Smuzhiyun if len(flags) == 0: 953*4882a593Smuzhiyun return None 954*4882a593Smuzhiyun return flags 955*4882a593Smuzhiyun 956*4882a593Smuzhiyun 957*4882a593Smuzhiyun def delVarFlags(self, var, **loginfo): 958*4882a593Smuzhiyun self.expand_cache = {} 959*4882a593Smuzhiyun if not var in self.dict: 960*4882a593Smuzhiyun self._makeShadowCopy(var) 961*4882a593Smuzhiyun 962*4882a593Smuzhiyun if var in self.dict: 963*4882a593Smuzhiyun content = None 964*4882a593Smuzhiyun 965*4882a593Smuzhiyun loginfo['op'] = 'delete flags' 966*4882a593Smuzhiyun self.varhistory.record(**loginfo) 967*4882a593Smuzhiyun 968*4882a593Smuzhiyun # try to save the content 969*4882a593Smuzhiyun if "_content" in self.dict[var]: 970*4882a593Smuzhiyun content = self.dict[var]["_content"] 971*4882a593Smuzhiyun self.dict[var] = {} 972*4882a593Smuzhiyun self.dict[var]["_content"] = content 973*4882a593Smuzhiyun else: 974*4882a593Smuzhiyun del self.dict[var] 975*4882a593Smuzhiyun 976*4882a593Smuzhiyun def createCopy(self): 977*4882a593Smuzhiyun """ 978*4882a593Smuzhiyun Create a copy of self by setting _data to self 979*4882a593Smuzhiyun """ 980*4882a593Smuzhiyun # we really want this to be a DataSmart... 981*4882a593Smuzhiyun data = DataSmart() 982*4882a593Smuzhiyun data.dict["_data"] = self.dict 983*4882a593Smuzhiyun data.varhistory = self.varhistory.copy() 984*4882a593Smuzhiyun data.varhistory.dataroot = data 985*4882a593Smuzhiyun data.inchistory = self.inchistory.copy() 986*4882a593Smuzhiyun 987*4882a593Smuzhiyun data._tracking = self._tracking 988*4882a593Smuzhiyun data._var_renames = self._var_renames 989*4882a593Smuzhiyun 990*4882a593Smuzhiyun data.overrides = None 991*4882a593Smuzhiyun data.overridevars = copy.copy(self.overridevars) 992*4882a593Smuzhiyun # Should really be a deepcopy but has heavy overhead. 993*4882a593Smuzhiyun # Instead, we're careful with writes. 994*4882a593Smuzhiyun data.overridedata = copy.copy(self.overridedata) 995*4882a593Smuzhiyun 996*4882a593Smuzhiyun return data 997*4882a593Smuzhiyun 998*4882a593Smuzhiyun def expandVarref(self, variable, parents=False): 999*4882a593Smuzhiyun """Find all references to variable in the data and expand it 1000*4882a593Smuzhiyun in place, optionally descending to parent datastores.""" 1001*4882a593Smuzhiyun 1002*4882a593Smuzhiyun if parents: 1003*4882a593Smuzhiyun keys = iter(self) 1004*4882a593Smuzhiyun else: 1005*4882a593Smuzhiyun keys = self.localkeys() 1006*4882a593Smuzhiyun 1007*4882a593Smuzhiyun ref = '${%s}' % variable 1008*4882a593Smuzhiyun value = self.getVar(variable, False) 1009*4882a593Smuzhiyun for key in keys: 1010*4882a593Smuzhiyun referrervalue = self.getVar(key, False) 1011*4882a593Smuzhiyun if referrervalue and isinstance(referrervalue, str) and ref in referrervalue: 1012*4882a593Smuzhiyun self.setVar(key, referrervalue.replace(ref, value)) 1013*4882a593Smuzhiyun 1014*4882a593Smuzhiyun def localkeys(self): 1015*4882a593Smuzhiyun for key in self.dict: 1016*4882a593Smuzhiyun if key not in ['_data']: 1017*4882a593Smuzhiyun yield key 1018*4882a593Smuzhiyun 1019*4882a593Smuzhiyun def __iter__(self): 1020*4882a593Smuzhiyun deleted = set() 1021*4882a593Smuzhiyun overrides = set() 1022*4882a593Smuzhiyun def keylist(d): 1023*4882a593Smuzhiyun klist = set() 1024*4882a593Smuzhiyun for key in d: 1025*4882a593Smuzhiyun if key in ["_data"]: 1026*4882a593Smuzhiyun continue 1027*4882a593Smuzhiyun if key in deleted: 1028*4882a593Smuzhiyun continue 1029*4882a593Smuzhiyun if key in overrides: 1030*4882a593Smuzhiyun continue 1031*4882a593Smuzhiyun if not d[key]: 1032*4882a593Smuzhiyun deleted.add(key) 1033*4882a593Smuzhiyun continue 1034*4882a593Smuzhiyun klist.add(key) 1035*4882a593Smuzhiyun 1036*4882a593Smuzhiyun if "_data" in d: 1037*4882a593Smuzhiyun klist |= keylist(d["_data"]) 1038*4882a593Smuzhiyun 1039*4882a593Smuzhiyun return klist 1040*4882a593Smuzhiyun 1041*4882a593Smuzhiyun self.need_overrides() 1042*4882a593Smuzhiyun for var in self.overridedata: 1043*4882a593Smuzhiyun for (r, o) in self.overridedata[var]: 1044*4882a593Smuzhiyun if o in self.overridesset: 1045*4882a593Smuzhiyun overrides.add(var) 1046*4882a593Smuzhiyun elif ":" in o: 1047*4882a593Smuzhiyun if set(o.split(":")).issubset(self.overridesset): 1048*4882a593Smuzhiyun overrides.add(var) 1049*4882a593Smuzhiyun 1050*4882a593Smuzhiyun for k in keylist(self.dict): 1051*4882a593Smuzhiyun yield k 1052*4882a593Smuzhiyun 1053*4882a593Smuzhiyun for k in overrides: 1054*4882a593Smuzhiyun yield k 1055*4882a593Smuzhiyun 1056*4882a593Smuzhiyun def __len__(self): 1057*4882a593Smuzhiyun return len(frozenset(iter(self))) 1058*4882a593Smuzhiyun 1059*4882a593Smuzhiyun def __getitem__(self, item): 1060*4882a593Smuzhiyun value = self.getVar(item, False) 1061*4882a593Smuzhiyun if value is None: 1062*4882a593Smuzhiyun raise KeyError(item) 1063*4882a593Smuzhiyun else: 1064*4882a593Smuzhiyun return value 1065*4882a593Smuzhiyun 1066*4882a593Smuzhiyun def __setitem__(self, var, value): 1067*4882a593Smuzhiyun self.setVar(var, value) 1068*4882a593Smuzhiyun 1069*4882a593Smuzhiyun def __delitem__(self, var): 1070*4882a593Smuzhiyun self.delVar(var) 1071*4882a593Smuzhiyun 1072*4882a593Smuzhiyun def get_hash(self): 1073*4882a593Smuzhiyun data = {} 1074*4882a593Smuzhiyun d = self.createCopy() 1075*4882a593Smuzhiyun bb.data.expandKeys(d) 1076*4882a593Smuzhiyun 1077*4882a593Smuzhiyun config_ignore_vars = set((d.getVar("BB_HASHCONFIG_IGNORE_VARS") or "").split()) 1078*4882a593Smuzhiyun keys = set(key for key in iter(d) if not key.startswith("__")) 1079*4882a593Smuzhiyun for key in keys: 1080*4882a593Smuzhiyun if key in config_ignore_vars: 1081*4882a593Smuzhiyun continue 1082*4882a593Smuzhiyun 1083*4882a593Smuzhiyun value = d.getVar(key, False) or "" 1084*4882a593Smuzhiyun if type(value) is type(self): 1085*4882a593Smuzhiyun data.update({key:value.get_hash()}) 1086*4882a593Smuzhiyun else: 1087*4882a593Smuzhiyun data.update({key:value}) 1088*4882a593Smuzhiyun 1089*4882a593Smuzhiyun varflags = d.getVarFlags(key, internalflags = True, expand=["vardepvalue"]) 1090*4882a593Smuzhiyun if not varflags: 1091*4882a593Smuzhiyun continue 1092*4882a593Smuzhiyun for f in varflags: 1093*4882a593Smuzhiyun if f == "_content": 1094*4882a593Smuzhiyun continue 1095*4882a593Smuzhiyun data.update({'%s[%s]' % (key, f):varflags[f]}) 1096*4882a593Smuzhiyun 1097*4882a593Smuzhiyun for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]: 1098*4882a593Smuzhiyun bb_list = d.getVar(key, False) or [] 1099*4882a593Smuzhiyun data.update({key:str(bb_list)}) 1100*4882a593Smuzhiyun 1101*4882a593Smuzhiyun if key == "__BBANONFUNCS": 1102*4882a593Smuzhiyun for i in bb_list: 1103*4882a593Smuzhiyun value = d.getVar(i, False) or "" 1104*4882a593Smuzhiyun data.update({i:value}) 1105*4882a593Smuzhiyun 1106*4882a593Smuzhiyun data_str = str([(k, data[k]) for k in sorted(data.keys())]) 1107*4882a593Smuzhiyun return hashlib.sha256(data_str.encode("utf-8")).hexdigest() 1108