data_smart.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. # ex:ts=4:sw=4:sts=4:et
  2. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
  3. """
  4. BitBake Smart Dictionary Implementation
  5. Functions for interacting with the data structure used by the
  6. BitBake build tools.
  7. """
  8. # Copyright (C) 2003, 2004 Chris Larson
  9. # Copyright (C) 2004, 2005 Seb Frankengul
  10. # Copyright (C) 2005, 2006 Holger Hans Peter Freyther
  11. # Copyright (C) 2005 Uli Luckas
  12. # Copyright (C) 2005 ROAD GmbH
  13. #
  14. # This program is free software; you can redistribute it and/or modify
  15. # it under the terms of the GNU General Public License version 2 as
  16. # published by the Free Software Foundation.
  17. #
  18. # This program is distributed in the hope that it will be useful,
  19. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. # GNU General Public License for more details.
  22. #
  23. # You should have received a copy of the GNU General Public License along
  24. # with this program; if not, write to the Free Software Foundation, Inc.,
  25. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  26. # Based on functions from the base bb module, Copyright 2003 Holger Schurig
  27. import copy, re, sys, traceback
  28. from collections import MutableMapping
  29. import logging
  30. import hashlib
  31. import bb, bb.codeparser
  32. from bb import utils
  33. from bb.COW import COWDictBase
  34. logger = logging.getLogger("BitBake.Data")
  35. __setvar_keyword__ = ["_append", "_prepend", "_remove"]
  36. __setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend|_remove)(_(?P<add>.*))?$')
  37. __expand_var_regexp__ = re.compile(r"\${[^{}@\n\t :]+}")
  38. __expand_python_regexp__ = re.compile(r"\${@.+?}")
  39. def infer_caller_details(loginfo, parent = False, varval = True):
  40. """Save the caller the trouble of specifying everything."""
  41. # Save effort.
  42. if 'ignore' in loginfo and loginfo['ignore']:
  43. return
  44. # If nothing was provided, mark this as possibly unneeded.
  45. if not loginfo:
  46. loginfo['ignore'] = True
  47. return
  48. # Infer caller's likely values for variable (var) and value (value),
  49. # to reduce clutter in the rest of the code.
  50. above = None
  51. def set_above():
  52. try:
  53. raise Exception
  54. except Exception:
  55. tb = sys.exc_info()[2]
  56. if parent:
  57. return tb.tb_frame.f_back.f_back.f_back
  58. else:
  59. return tb.tb_frame.f_back.f_back
  60. if varval and ('variable' not in loginfo or 'detail' not in loginfo):
  61. if not above:
  62. above = set_above()
  63. lcls = above.f_locals.items()
  64. for k, v in lcls:
  65. if k == 'value' and 'detail' not in loginfo:
  66. loginfo['detail'] = v
  67. if k == 'var' and 'variable' not in loginfo:
  68. loginfo['variable'] = v
  69. # Infer file/line/function from traceback
  70. # Don't use traceback.extract_stack() since it fills the line contents which
  71. # we don't need and that hits stat syscalls
  72. if 'file' not in loginfo:
  73. if not above:
  74. above = set_above()
  75. f = above.f_back
  76. line = f.f_lineno
  77. file = f.f_code.co_filename
  78. func = f.f_code.co_name
  79. loginfo['file'] = file
  80. loginfo['line'] = line
  81. if func not in loginfo:
  82. loginfo['func'] = func
  83. class VariableParse:
  84. def __init__(self, varname, d, val = None):
  85. self.varname = varname
  86. self.d = d
  87. self.value = val
  88. self.references = set()
  89. self.execs = set()
  90. self.contains = {}
  91. def var_sub(self, match):
  92. key = match.group()[2:-1]
  93. if self.varname and key:
  94. if self.varname == key:
  95. raise Exception("variable %s references itself!" % self.varname)
  96. if key in self.d.expand_cache:
  97. varparse = self.d.expand_cache[key]
  98. var = varparse.value
  99. else:
  100. var = self.d.getVarFlag(key, "_content")
  101. self.references.add(key)
  102. if var is not None:
  103. return var
  104. else:
  105. return match.group()
  106. def python_sub(self, match):
  107. code = match.group()[3:-1]
  108. codeobj = compile(code.strip(), self.varname or "<expansion>", "eval")
  109. parser = bb.codeparser.PythonParser(self.varname, logger)
  110. parser.parse_python(code)
  111. if self.varname:
  112. vardeps = self.d.getVarFlag(self.varname, "vardeps")
  113. if vardeps is None:
  114. parser.log.flush()
  115. else:
  116. parser.log.flush()
  117. self.references |= parser.references
  118. self.execs |= parser.execs
  119. for k in parser.contains:
  120. if k not in self.contains:
  121. self.contains[k] = parser.contains[k].copy()
  122. else:
  123. self.contains[k].update(parser.contains[k])
  124. value = utils.better_eval(codeobj, DataContext(self.d), {'d' : self.d})
  125. return str(value)
  126. class DataContext(dict):
  127. def __init__(self, metadata, **kwargs):
  128. self.metadata = metadata
  129. dict.__init__(self, **kwargs)
  130. self['d'] = metadata
  131. def __missing__(self, key):
  132. value = self.metadata.getVar(key)
  133. if value is None or self.metadata.getVarFlag(key, 'func', False):
  134. raise KeyError(key)
  135. else:
  136. return value
  137. class ExpansionError(Exception):
  138. def __init__(self, varname, expression, exception):
  139. self.expression = expression
  140. self.variablename = varname
  141. self.exception = exception
  142. if varname:
  143. if expression:
  144. self.msg = "Failure expanding variable %s, expression was %s which triggered exception %s: %s" % (varname, expression, type(exception).__name__, exception)
  145. else:
  146. self.msg = "Failure expanding variable %s: %s: %s" % (varname, type(exception).__name__, exception)
  147. else:
  148. self.msg = "Failure expanding expression %s which triggered exception %s: %s" % (expression, type(exception).__name__, exception)
  149. Exception.__init__(self, self.msg)
  150. self.args = (varname, expression, exception)
  151. def __str__(self):
  152. return self.msg
  153. class IncludeHistory(object):
  154. def __init__(self, parent = None, filename = '[TOP LEVEL]'):
  155. self.parent = parent
  156. self.filename = filename
  157. self.children = []
  158. self.current = self
  159. def copy(self):
  160. new = IncludeHistory(self.parent, self.filename)
  161. for c in self.children:
  162. new.children.append(c)
  163. return new
  164. def include(self, filename):
  165. newfile = IncludeHistory(self.current, filename)
  166. self.current.children.append(newfile)
  167. self.current = newfile
  168. return self
  169. def __enter__(self):
  170. pass
  171. def __exit__(self, a, b, c):
  172. if self.current.parent:
  173. self.current = self.current.parent
  174. else:
  175. bb.warn("Include log: Tried to finish '%s' at top level." % filename)
  176. return False
  177. def emit(self, o, level = 0):
  178. """Emit an include history file, and its children."""
  179. if level:
  180. spaces = " " * (level - 1)
  181. o.write("# %s%s" % (spaces, self.filename))
  182. if len(self.children) > 0:
  183. o.write(" includes:")
  184. else:
  185. o.write("#\n# INCLUDE HISTORY:\n#")
  186. level = level + 1
  187. for child in self.children:
  188. o.write("\n")
  189. child.emit(o, level)
  190. class VariableHistory(object):
  191. def __init__(self, dataroot):
  192. self.dataroot = dataroot
  193. self.variables = COWDictBase.copy()
  194. def copy(self):
  195. new = VariableHistory(self.dataroot)
  196. new.variables = self.variables.copy()
  197. return new
  198. def record(self, *kwonly, **loginfo):
  199. if not self.dataroot._tracking:
  200. return
  201. if len(kwonly) > 0:
  202. raise TypeError
  203. infer_caller_details(loginfo, parent = True)
  204. if 'ignore' in loginfo and loginfo['ignore']:
  205. return
  206. if 'op' not in loginfo or not loginfo['op']:
  207. loginfo['op'] = 'set'
  208. if 'detail' in loginfo:
  209. loginfo['detail'] = str(loginfo['detail'])
  210. if 'variable' not in loginfo or 'file' not in loginfo:
  211. raise ValueError("record() missing variable or file.")
  212. var = loginfo['variable']
  213. if var not in self.variables:
  214. self.variables[var] = []
  215. if not isinstance(self.variables[var], list):
  216. return
  217. if 'nodups' in loginfo and loginfo in self.variables[var]:
  218. return
  219. self.variables[var].append(loginfo.copy())
  220. def variable(self, var):
  221. if var in self.variables:
  222. return self.variables[var]
  223. else:
  224. return []
  225. def emit(self, var, oval, val, o, d):
  226. history = self.variable(var)
  227. # Append override history
  228. if var in d.overridedata:
  229. for (r, override) in d.overridedata[var]:
  230. for event in self.variable(r):
  231. loginfo = event.copy()
  232. if 'flag' in loginfo and not loginfo['flag'].startswith("_"):
  233. continue
  234. loginfo['variable'] = var
  235. loginfo['op'] = 'override[%s]:%s' % (override, loginfo['op'])
  236. history.append(loginfo)
  237. commentVal = re.sub('\n', '\n#', str(oval))
  238. if history:
  239. if len(history) == 1:
  240. o.write("#\n# $%s\n" % var)
  241. else:
  242. o.write("#\n# $%s [%d operations]\n" % (var, len(history)))
  243. for event in history:
  244. # o.write("# %s\n" % str(event))
  245. if 'func' in event:
  246. # If we have a function listed, this is internal
  247. # code, not an operation in a config file, and the
  248. # full path is distracting.
  249. event['file'] = re.sub('.*/', '', event['file'])
  250. display_func = ' [%s]' % event['func']
  251. else:
  252. display_func = ''
  253. if 'flag' in event:
  254. flag = '[%s] ' % (event['flag'])
  255. else:
  256. flag = ''
  257. 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'])))
  258. if len(history) > 1:
  259. o.write("# pre-expansion value:\n")
  260. o.write('# "%s"\n' % (commentVal))
  261. else:
  262. o.write("#\n# $%s\n# [no history recorded]\n#\n" % var)
  263. o.write('# "%s"\n' % (commentVal))
  264. def get_variable_files(self, var):
  265. """Get the files where operations are made on a variable"""
  266. var_history = self.variable(var)
  267. files = []
  268. for event in var_history:
  269. files.append(event['file'])
  270. return files
  271. def get_variable_lines(self, var, f):
  272. """Get the line where a operation is made on a variable in file f"""
  273. var_history = self.variable(var)
  274. lines = []
  275. for event in var_history:
  276. if f== event['file']:
  277. line = event['line']
  278. lines.append(line)
  279. return lines
  280. def get_variable_items_files(self, var, d):
  281. """
  282. Use variable history to map items added to a list variable and
  283. the files in which they were added.
  284. """
  285. history = self.variable(var)
  286. finalitems = (d.getVar(var) or '').split()
  287. filemap = {}
  288. isset = False
  289. for event in history:
  290. if 'flag' in event:
  291. continue
  292. if event['op'] == '_remove':
  293. continue
  294. if isset and event['op'] == 'set?':
  295. continue
  296. isset = True
  297. items = d.expand(event['detail']).split()
  298. for item in items:
  299. # This is a little crude but is belt-and-braces to avoid us
  300. # having to handle every possible operation type specifically
  301. if item in finalitems and not item in filemap:
  302. filemap[item] = event['file']
  303. return filemap
  304. def del_var_history(self, var, f=None, line=None):
  305. """If file f and line are not given, the entire history of var is deleted"""
  306. if var in self.variables:
  307. if f and line:
  308. self.variables[var] = [ x for x in self.variables[var] if x['file']!=f and x['line']!=line]
  309. else:
  310. self.variables[var] = []
  311. class DataSmart(MutableMapping):
  312. def __init__(self):
  313. self.dict = {}
  314. self.inchistory = IncludeHistory()
  315. self.varhistory = VariableHistory(self)
  316. self._tracking = False
  317. self.expand_cache = {}
  318. # cookie monster tribute
  319. # Need to be careful about writes to overridedata as
  320. # its only a shallow copy, could influence other data store
  321. # copies!
  322. self.overridedata = {}
  323. self.overrides = None
  324. self.overridevars = set(["OVERRIDES", "FILE"])
  325. self.inoverride = False
  326. def enableTracking(self):
  327. self._tracking = True
  328. def disableTracking(self):
  329. self._tracking = False
  330. def expandWithRefs(self, s, varname):
  331. if not isinstance(s, str): # sanity check
  332. return VariableParse(varname, self, s)
  333. if varname and varname in self.expand_cache:
  334. return self.expand_cache[varname]
  335. varparse = VariableParse(varname, self)
  336. while s.find('${') != -1:
  337. olds = s
  338. try:
  339. s = __expand_var_regexp__.sub(varparse.var_sub, s)
  340. try:
  341. s = __expand_python_regexp__.sub(varparse.python_sub, s)
  342. except SyntaxError as e:
  343. # Likely unmatched brackets, just don't expand the expression
  344. if e.msg != "EOL while scanning string literal":
  345. raise
  346. if s == olds:
  347. break
  348. except ExpansionError:
  349. raise
  350. except bb.parse.SkipRecipe:
  351. raise
  352. except Exception as exc:
  353. raise ExpansionError(varname, s, exc) from exc
  354. varparse.value = s
  355. if varname:
  356. self.expand_cache[varname] = varparse
  357. return varparse
  358. def expand(self, s, varname = None):
  359. return self.expandWithRefs(s, varname).value
  360. def finalize(self, parent = False):
  361. return
  362. def internal_finalize(self, parent = False):
  363. """Performs final steps upon the datastore, including application of overrides"""
  364. self.overrides = None
  365. def need_overrides(self):
  366. if self.overrides is not None:
  367. return
  368. if self.inoverride:
  369. return
  370. for count in range(5):
  371. self.inoverride = True
  372. # Can end up here recursively so setup dummy values
  373. self.overrides = []
  374. self.overridesset = set()
  375. self.overrides = (self.getVar("OVERRIDES") or "").split(":") or []
  376. self.overridesset = set(self.overrides)
  377. self.inoverride = False
  378. self.expand_cache = {}
  379. newoverrides = (self.getVar("OVERRIDES") or "").split(":") or []
  380. if newoverrides == self.overrides:
  381. break
  382. self.overrides = newoverrides
  383. self.overridesset = set(self.overrides)
  384. else:
  385. 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.")
  386. def initVar(self, var):
  387. self.expand_cache = {}
  388. if not var in self.dict:
  389. self.dict[var] = {}
  390. def _findVar(self, var):
  391. dest = self.dict
  392. while dest:
  393. if var in dest:
  394. return dest[var]
  395. if "_data" not in dest:
  396. break
  397. dest = dest["_data"]
  398. def _makeShadowCopy(self, var):
  399. if var in self.dict:
  400. return
  401. local_var = self._findVar(var)
  402. if local_var:
  403. self.dict[var] = copy.copy(local_var)
  404. else:
  405. self.initVar(var)
  406. def setVar(self, var, value, **loginfo):
  407. #print("var=" + str(var) + " val=" + str(value))
  408. parsing=False
  409. if 'parsing' in loginfo:
  410. parsing=True
  411. if 'op' not in loginfo:
  412. loginfo['op'] = "set"
  413. self.expand_cache = {}
  414. match = __setvar_regexp__.match(var)
  415. if match and match.group("keyword") in __setvar_keyword__:
  416. base = match.group('base')
  417. keyword = match.group("keyword")
  418. override = match.group('add')
  419. l = self.getVarFlag(base, keyword, False) or []
  420. l.append([value, override])
  421. self.setVarFlag(base, keyword, l, ignore=True)
  422. # And cause that to be recorded:
  423. loginfo['detail'] = value
  424. loginfo['variable'] = base
  425. if override:
  426. loginfo['op'] = '%s[%s]' % (keyword, override)
  427. else:
  428. loginfo['op'] = keyword
  429. self.varhistory.record(**loginfo)
  430. # todo make sure keyword is not __doc__ or __module__
  431. # pay the cookie monster
  432. # more cookies for the cookie monster
  433. if '_' in var:
  434. self._setvar_update_overrides(base, **loginfo)
  435. if base in self.overridevars:
  436. self._setvar_update_overridevars(var, value)
  437. return
  438. if not var in self.dict:
  439. self._makeShadowCopy(var)
  440. if not parsing:
  441. if "_append" in self.dict[var]:
  442. del self.dict[var]["_append"]
  443. if "_prepend" in self.dict[var]:
  444. del self.dict[var]["_prepend"]
  445. if var in self.overridedata:
  446. active = []
  447. self.need_overrides()
  448. for (r, o) in self.overridedata[var]:
  449. if o in self.overridesset:
  450. active.append(r)
  451. elif "_" in o:
  452. if set(o.split("_")).issubset(self.overridesset):
  453. active.append(r)
  454. for a in active:
  455. self.delVar(a)
  456. del self.overridedata[var]
  457. # more cookies for the cookie monster
  458. if '_' in var:
  459. self._setvar_update_overrides(var, **loginfo)
  460. # setting var
  461. self.dict[var]["_content"] = value
  462. self.varhistory.record(**loginfo)
  463. if var in self.overridevars:
  464. self._setvar_update_overridevars(var, value)
  465. def _setvar_update_overridevars(self, var, value):
  466. vardata = self.expandWithRefs(value, var)
  467. new = vardata.references
  468. new.update(vardata.contains.keys())
  469. while not new.issubset(self.overridevars):
  470. nextnew = set()
  471. self.overridevars.update(new)
  472. for i in new:
  473. vardata = self.expandWithRefs(self.getVar(i), i)
  474. nextnew.update(vardata.references)
  475. nextnew.update(vardata.contains.keys())
  476. new = nextnew
  477. self.internal_finalize(True)
  478. def _setvar_update_overrides(self, var, **loginfo):
  479. # aka pay the cookie monster
  480. override = var[var.rfind('_')+1:]
  481. shortvar = var[:var.rfind('_')]
  482. while override and override.islower():
  483. if shortvar not in self.overridedata:
  484. self.overridedata[shortvar] = []
  485. if [var, override] not in self.overridedata[shortvar]:
  486. # Force CoW by recreating the list first
  487. self.overridedata[shortvar] = list(self.overridedata[shortvar])
  488. self.overridedata[shortvar].append([var, override])
  489. override = None
  490. if "_" in shortvar:
  491. override = var[shortvar.rfind('_')+1:]
  492. shortvar = var[:shortvar.rfind('_')]
  493. if len(shortvar) == 0:
  494. override = None
  495. def getVar(self, var, expand=True, noweakdefault=False, parsing=False):
  496. return self.getVarFlag(var, "_content", expand, noweakdefault, parsing)
  497. def renameVar(self, key, newkey, **loginfo):
  498. """
  499. Rename the variable key to newkey
  500. """
  501. val = self.getVar(key, 0, parsing=True)
  502. if val is not None:
  503. loginfo['variable'] = newkey
  504. loginfo['op'] = 'rename from %s' % key
  505. loginfo['detail'] = val
  506. self.varhistory.record(**loginfo)
  507. self.setVar(newkey, val, ignore=True, parsing=True)
  508. for i in (__setvar_keyword__):
  509. src = self.getVarFlag(key, i, False)
  510. if src is None:
  511. continue
  512. dest = self.getVarFlag(newkey, i, False) or []
  513. dest.extend(src)
  514. self.setVarFlag(newkey, i, dest, ignore=True)
  515. if key in self.overridedata:
  516. self.overridedata[newkey] = []
  517. for (v, o) in self.overridedata[key]:
  518. self.overridedata[newkey].append([v.replace(key, newkey), o])
  519. self.renameVar(v, v.replace(key, newkey))
  520. if '_' in newkey and val is None:
  521. self._setvar_update_overrides(newkey, **loginfo)
  522. loginfo['variable'] = key
  523. loginfo['op'] = 'rename (to)'
  524. loginfo['detail'] = newkey
  525. self.varhistory.record(**loginfo)
  526. self.delVar(key, ignore=True)
  527. def appendVar(self, var, value, **loginfo):
  528. loginfo['op'] = 'append'
  529. self.varhistory.record(**loginfo)
  530. self.setVar(var + "_append", value, ignore=True, parsing=True)
  531. def prependVar(self, var, value, **loginfo):
  532. loginfo['op'] = 'prepend'
  533. self.varhistory.record(**loginfo)
  534. self.setVar(var + "_prepend", value, ignore=True, parsing=True)
  535. def delVar(self, var, **loginfo):
  536. loginfo['detail'] = ""
  537. loginfo['op'] = 'del'
  538. self.varhistory.record(**loginfo)
  539. self.expand_cache = {}
  540. self.dict[var] = {}
  541. if var in self.overridedata:
  542. del self.overridedata[var]
  543. if '_' in var:
  544. override = var[var.rfind('_')+1:]
  545. shortvar = var[:var.rfind('_')]
  546. while override and override.islower():
  547. try:
  548. if shortvar in self.overridedata:
  549. # Force CoW by recreating the list first
  550. self.overridedata[shortvar] = list(self.overridedata[shortvar])
  551. self.overridedata[shortvar].remove([var, override])
  552. except ValueError as e:
  553. pass
  554. override = None
  555. if "_" in shortvar:
  556. override = var[shortvar.rfind('_')+1:]
  557. shortvar = var[:shortvar.rfind('_')]
  558. if len(shortvar) == 0:
  559. override = None
  560. def setVarFlag(self, var, flag, value, **loginfo):
  561. self.expand_cache = {}
  562. if 'op' not in loginfo:
  563. loginfo['op'] = "set"
  564. loginfo['flag'] = flag
  565. self.varhistory.record(**loginfo)
  566. if not var in self.dict:
  567. self._makeShadowCopy(var)
  568. self.dict[var][flag] = value
  569. if flag == "_defaultval" and '_' in var:
  570. self._setvar_update_overrides(var, **loginfo)
  571. if flag == "_defaultval" and var in self.overridevars:
  572. self._setvar_update_overridevars(var, value)
  573. if flag == "unexport" or flag == "export":
  574. if not "__exportlist" in self.dict:
  575. self._makeShadowCopy("__exportlist")
  576. if not "_content" in self.dict["__exportlist"]:
  577. self.dict["__exportlist"]["_content"] = set()
  578. self.dict["__exportlist"]["_content"].add(var)
  579. def getVarFlag(self, var, flag, expand=True, noweakdefault=False, parsing=False):
  580. local_var = self._findVar(var)
  581. value = None
  582. if flag == "_content" and var in self.overridedata and not parsing:
  583. match = False
  584. active = {}
  585. self.need_overrides()
  586. for (r, o) in self.overridedata[var]:
  587. # What about double overrides both with "_" in the name?
  588. if o in self.overridesset:
  589. active[o] = r
  590. elif "_" in o:
  591. if set(o.split("_")).issubset(self.overridesset):
  592. active[o] = r
  593. mod = True
  594. while mod:
  595. mod = False
  596. for o in self.overrides:
  597. for a in active.copy():
  598. if a.endswith("_" + o):
  599. t = active[a]
  600. del active[a]
  601. active[a.replace("_" + o, "")] = t
  602. mod = True
  603. elif a == o:
  604. match = active[a]
  605. del active[a]
  606. if match:
  607. value = self.getVar(match, False)
  608. if local_var is not None and value is None:
  609. if flag in local_var:
  610. value = copy.copy(local_var[flag])
  611. elif flag == "_content" and "_defaultval" in local_var and not noweakdefault:
  612. value = copy.copy(local_var["_defaultval"])
  613. if flag == "_content" and local_var is not None and "_append" in local_var and not parsing:
  614. if not value:
  615. value = ""
  616. self.need_overrides()
  617. for (r, o) in local_var["_append"]:
  618. match = True
  619. if o:
  620. for o2 in o.split("_"):
  621. if not o2 in self.overrides:
  622. match = False
  623. if match:
  624. value = value + r
  625. if flag == "_content" and local_var is not None and "_prepend" in local_var and not parsing:
  626. if not value:
  627. value = ""
  628. self.need_overrides()
  629. for (r, o) in local_var["_prepend"]:
  630. match = True
  631. if o:
  632. for o2 in o.split("_"):
  633. if not o2 in self.overrides:
  634. match = False
  635. if match:
  636. value = r + value
  637. if expand and value:
  638. # Only getvar (flag == _content) hits the expand cache
  639. cachename = None
  640. if flag == "_content":
  641. cachename = var
  642. else:
  643. cachename = var + "[" + flag + "]"
  644. value = self.expand(value, cachename)
  645. if value and flag == "_content" and local_var is not None and "_remove" in local_var:
  646. removes = []
  647. self.need_overrides()
  648. for (r, o) in local_var["_remove"]:
  649. match = True
  650. if o:
  651. for o2 in o.split("_"):
  652. if not o2 in self.overrides:
  653. match = False
  654. if match:
  655. removes.extend(self.expand(r).split())
  656. filtered = filter(lambda v: v not in removes,
  657. value.split())
  658. value = " ".join(filtered)
  659. if expand and var in self.expand_cache:
  660. # We need to ensure the expand cache has the correct value
  661. # flag == "_content" here
  662. self.expand_cache[var].value = value
  663. return value
  664. def delVarFlag(self, var, flag, **loginfo):
  665. self.expand_cache = {}
  666. local_var = self._findVar(var)
  667. if not local_var:
  668. return
  669. if not var in self.dict:
  670. self._makeShadowCopy(var)
  671. if var in self.dict and flag in self.dict[var]:
  672. loginfo['detail'] = ""
  673. loginfo['op'] = 'delFlag'
  674. loginfo['flag'] = flag
  675. self.varhistory.record(**loginfo)
  676. del self.dict[var][flag]
  677. def appendVarFlag(self, var, flag, value, **loginfo):
  678. loginfo['op'] = 'append'
  679. loginfo['flag'] = flag
  680. self.varhistory.record(**loginfo)
  681. newvalue = (self.getVarFlag(var, flag, False) or "") + value
  682. self.setVarFlag(var, flag, newvalue, ignore=True)
  683. def prependVarFlag(self, var, flag, value, **loginfo):
  684. loginfo['op'] = 'prepend'
  685. loginfo['flag'] = flag
  686. self.varhistory.record(**loginfo)
  687. newvalue = value + (self.getVarFlag(var, flag, False) or "")
  688. self.setVarFlag(var, flag, newvalue, ignore=True)
  689. def setVarFlags(self, var, flags, **loginfo):
  690. self.expand_cache = {}
  691. infer_caller_details(loginfo)
  692. if not var in self.dict:
  693. self._makeShadowCopy(var)
  694. for i in flags:
  695. if i == "_content":
  696. continue
  697. loginfo['flag'] = i
  698. loginfo['detail'] = flags[i]
  699. self.varhistory.record(**loginfo)
  700. self.dict[var][i] = flags[i]
  701. def getVarFlags(self, var, expand = False, internalflags=False):
  702. local_var = self._findVar(var)
  703. flags = {}
  704. if local_var:
  705. for i in local_var:
  706. if i.startswith("_") and not internalflags:
  707. continue
  708. flags[i] = local_var[i]
  709. if expand and i in expand:
  710. flags[i] = self.expand(flags[i], var + "[" + i + "]")
  711. if len(flags) == 0:
  712. return None
  713. return flags
  714. def delVarFlags(self, var, **loginfo):
  715. self.expand_cache = {}
  716. if not var in self.dict:
  717. self._makeShadowCopy(var)
  718. if var in self.dict:
  719. content = None
  720. loginfo['op'] = 'delete flags'
  721. self.varhistory.record(**loginfo)
  722. # try to save the content
  723. if "_content" in self.dict[var]:
  724. content = self.dict[var]["_content"]
  725. self.dict[var] = {}
  726. self.dict[var]["_content"] = content
  727. else:
  728. del self.dict[var]
  729. def createCopy(self):
  730. """
  731. Create a copy of self by setting _data to self
  732. """
  733. # we really want this to be a DataSmart...
  734. data = DataSmart()
  735. data.dict["_data"] = self.dict
  736. data.varhistory = self.varhistory.copy()
  737. data.varhistory.datasmart = data
  738. data.inchistory = self.inchistory.copy()
  739. data._tracking = self._tracking
  740. data.overrides = None
  741. data.overridevars = copy.copy(self.overridevars)
  742. # Should really be a deepcopy but has heavy overhead.
  743. # Instead, we're careful with writes.
  744. data.overridedata = copy.copy(self.overridedata)
  745. return data
  746. def expandVarref(self, variable, parents=False):
  747. """Find all references to variable in the data and expand it
  748. in place, optionally descending to parent datastores."""
  749. if parents:
  750. keys = iter(self)
  751. else:
  752. keys = self.localkeys()
  753. ref = '${%s}' % variable
  754. value = self.getVar(variable, False)
  755. for key in keys:
  756. referrervalue = self.getVar(key, False)
  757. if referrervalue and ref in referrervalue:
  758. self.setVar(key, referrervalue.replace(ref, value))
  759. def localkeys(self):
  760. for key in self.dict:
  761. if key != '_data':
  762. yield key
  763. def __iter__(self):
  764. deleted = set()
  765. overrides = set()
  766. def keylist(d):
  767. klist = set()
  768. for key in d:
  769. if key == "_data":
  770. continue
  771. if key in deleted:
  772. continue
  773. if key in overrides:
  774. continue
  775. if not d[key]:
  776. deleted.add(key)
  777. continue
  778. klist.add(key)
  779. if "_data" in d:
  780. klist |= keylist(d["_data"])
  781. return klist
  782. self.need_overrides()
  783. for var in self.overridedata:
  784. for (r, o) in self.overridedata[var]:
  785. if o in self.overridesset:
  786. overrides.add(var)
  787. elif "_" in o:
  788. if set(o.split("_")).issubset(self.overridesset):
  789. overrides.add(var)
  790. for k in keylist(self.dict):
  791. yield k
  792. for k in overrides:
  793. yield k
  794. def __len__(self):
  795. return len(frozenset(iter(self)))
  796. def __getitem__(self, item):
  797. value = self.getVar(item, False)
  798. if value is None:
  799. raise KeyError(item)
  800. else:
  801. return value
  802. def __setitem__(self, var, value):
  803. self.setVar(var, value)
  804. def __delitem__(self, var):
  805. self.delVar(var)
  806. def get_hash(self):
  807. data = {}
  808. d = self.createCopy()
  809. bb.data.expandKeys(d)
  810. bb.data.update_data(d)
  811. config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST") or "").split())
  812. keys = set(key for key in iter(d) if not key.startswith("__"))
  813. for key in keys:
  814. if key in config_whitelist:
  815. continue
  816. value = d.getVar(key, False) or ""
  817. data.update({key:value})
  818. varflags = d.getVarFlags(key, internalflags = True)
  819. if not varflags:
  820. continue
  821. for f in varflags:
  822. if f == "_content":
  823. continue
  824. data.update({'%s[%s]' % (key, f):varflags[f]})
  825. for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]:
  826. bb_list = d.getVar(key, False) or []
  827. data.update({key:str(bb_list)})
  828. if key == "__BBANONFUNCS":
  829. for i in bb_list:
  830. value = d.getVar(i, False) or ""
  831. data.update({i:value})
  832. data_str = str([(k, data[k]) for k in sorted(data.keys())])
  833. return hashlib.md5(data_str.encode("utf-8")).hexdigest()