data_smart.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  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", True)
  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", True)
  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))
  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, True)
  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, True) 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. exc_class, exc, tb = sys.exc_info()
  354. raise ExpansionError, ExpansionError(varname, s, exc), tb
  355. varparse.value = s
  356. if varname:
  357. self.expand_cache[varname] = varparse
  358. return varparse
  359. def expand(self, s, varname = None):
  360. return self.expandWithRefs(s, varname).value
  361. def finalize(self, parent = False):
  362. return
  363. def internal_finalize(self, parent = False):
  364. """Performs final steps upon the datastore, including application of overrides"""
  365. self.overrides = None
  366. def need_overrides(self):
  367. if self.overrides is not None:
  368. return
  369. if self.inoverride:
  370. return
  371. for count in range(5):
  372. self.inoverride = True
  373. # Can end up here recursively so setup dummy values
  374. self.overrides = []
  375. self.overridesset = set()
  376. self.overrides = (self.getVar("OVERRIDES", True) or "").split(":") or []
  377. self.overridesset = set(self.overrides)
  378. self.inoverride = False
  379. self.expand_cache = {}
  380. newoverrides = (self.getVar("OVERRIDES", True) or "").split(":") or []
  381. if newoverrides == self.overrides:
  382. break
  383. self.overrides = newoverrides
  384. self.overridesset = set(self.overrides)
  385. else:
  386. 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.")
  387. def initVar(self, var):
  388. self.expand_cache = {}
  389. if not var in self.dict:
  390. self.dict[var] = {}
  391. def _findVar(self, var):
  392. dest = self.dict
  393. while dest:
  394. if var in dest:
  395. return dest[var]
  396. if "_data" not in dest:
  397. break
  398. dest = dest["_data"]
  399. def _makeShadowCopy(self, var):
  400. if var in self.dict:
  401. return
  402. local_var = self._findVar(var)
  403. if local_var:
  404. self.dict[var] = copy.copy(local_var)
  405. else:
  406. self.initVar(var)
  407. def setVar(self, var, value, **loginfo):
  408. #print("var=" + str(var) + " val=" + str(value))
  409. parsing=False
  410. if 'parsing' in loginfo:
  411. parsing=True
  412. if 'op' not in loginfo:
  413. loginfo['op'] = "set"
  414. self.expand_cache = {}
  415. match = __setvar_regexp__.match(var)
  416. if match and match.group("keyword") in __setvar_keyword__:
  417. base = match.group('base')
  418. keyword = match.group("keyword")
  419. override = match.group('add')
  420. l = self.getVarFlag(base, keyword, False) or []
  421. l.append([value, override])
  422. self.setVarFlag(base, keyword, l, ignore=True)
  423. # And cause that to be recorded:
  424. loginfo['detail'] = value
  425. loginfo['variable'] = base
  426. if override:
  427. loginfo['op'] = '%s[%s]' % (keyword, override)
  428. else:
  429. loginfo['op'] = keyword
  430. self.varhistory.record(**loginfo)
  431. # todo make sure keyword is not __doc__ or __module__
  432. # pay the cookie monster
  433. # more cookies for the cookie monster
  434. if '_' in var:
  435. self._setvar_update_overrides(base, **loginfo)
  436. if base in self.overridevars:
  437. self._setvar_update_overridevars(var, value)
  438. return
  439. if not var in self.dict:
  440. self._makeShadowCopy(var)
  441. if not parsing:
  442. if "_append" in self.dict[var]:
  443. del self.dict[var]["_append"]
  444. if "_prepend" in self.dict[var]:
  445. del self.dict[var]["_prepend"]
  446. if var in self.overridedata:
  447. active = []
  448. self.need_overrides()
  449. for (r, o) in self.overridedata[var]:
  450. if o in self.overridesset:
  451. active.append(r)
  452. elif "_" in o:
  453. if set(o.split("_")).issubset(self.overridesset):
  454. active.append(r)
  455. for a in active:
  456. self.delVar(a)
  457. del self.overridedata[var]
  458. # more cookies for the cookie monster
  459. if '_' in var:
  460. self._setvar_update_overrides(var, **loginfo)
  461. # setting var
  462. self.dict[var]["_content"] = value
  463. self.varhistory.record(**loginfo)
  464. if var in self.overridevars:
  465. self._setvar_update_overridevars(var, value)
  466. def _setvar_update_overridevars(self, var, value):
  467. vardata = self.expandWithRefs(value, var)
  468. new = vardata.references
  469. new.update(vardata.contains.keys())
  470. while not new.issubset(self.overridevars):
  471. nextnew = set()
  472. self.overridevars.update(new)
  473. for i in new:
  474. vardata = self.expandWithRefs(self.getVar(i, True), i)
  475. nextnew.update(vardata.references)
  476. nextnew.update(vardata.contains.keys())
  477. new = nextnew
  478. self.internal_finalize(True)
  479. def _setvar_update_overrides(self, var, **loginfo):
  480. # aka pay the cookie monster
  481. override = var[var.rfind('_')+1:]
  482. shortvar = var[:var.rfind('_')]
  483. while override and override.islower():
  484. if shortvar not in self.overridedata:
  485. self.overridedata[shortvar] = []
  486. if [var, override] not in self.overridedata[shortvar]:
  487. # Force CoW by recreating the list first
  488. self.overridedata[shortvar] = list(self.overridedata[shortvar])
  489. self.overridedata[shortvar].append([var, override])
  490. override = None
  491. if "_" in shortvar:
  492. override = var[shortvar.rfind('_')+1:]
  493. shortvar = var[:shortvar.rfind('_')]
  494. if len(shortvar) == 0:
  495. override = None
  496. def getVar(self, var, expand, noweakdefault=False, parsing=False):
  497. return self.getVarFlag(var, "_content", expand, noweakdefault, parsing)
  498. def renameVar(self, key, newkey, **loginfo):
  499. """
  500. Rename the variable key to newkey
  501. """
  502. val = self.getVar(key, 0, parsing=True)
  503. if val is not None:
  504. loginfo['variable'] = newkey
  505. loginfo['op'] = 'rename from %s' % key
  506. loginfo['detail'] = val
  507. self.varhistory.record(**loginfo)
  508. self.setVar(newkey, val, ignore=True, parsing=True)
  509. for i in (__setvar_keyword__):
  510. src = self.getVarFlag(key, i, False)
  511. if src is None:
  512. continue
  513. dest = self.getVarFlag(newkey, i, False) or []
  514. dest.extend(src)
  515. self.setVarFlag(newkey, i, dest, ignore=True)
  516. if key in self.overridedata:
  517. self.overridedata[newkey] = []
  518. for (v, o) in self.overridedata[key]:
  519. self.overridedata[newkey].append([v.replace(key, newkey), o])
  520. self.renameVar(v, v.replace(key, newkey))
  521. if '_' in newkey and val is None:
  522. self._setvar_update_overrides(newkey, **loginfo)
  523. loginfo['variable'] = key
  524. loginfo['op'] = 'rename (to)'
  525. loginfo['detail'] = newkey
  526. self.varhistory.record(**loginfo)
  527. self.delVar(key, ignore=True)
  528. def appendVar(self, var, value, **loginfo):
  529. loginfo['op'] = 'append'
  530. self.varhistory.record(**loginfo)
  531. self.setVar(var + "_append", value, ignore=True, parsing=True)
  532. def prependVar(self, var, value, **loginfo):
  533. loginfo['op'] = 'prepend'
  534. self.varhistory.record(**loginfo)
  535. self.setVar(var + "_prepend", value, ignore=True, parsing=True)
  536. def delVar(self, var, **loginfo):
  537. loginfo['detail'] = ""
  538. loginfo['op'] = 'del'
  539. self.varhistory.record(**loginfo)
  540. self.expand_cache = {}
  541. self.dict[var] = {}
  542. if var in self.overridedata:
  543. del self.overridedata[var]
  544. if '_' in var:
  545. override = var[var.rfind('_')+1:]
  546. shortvar = var[:var.rfind('_')]
  547. while override and override.islower():
  548. try:
  549. if shortvar in self.overridedata:
  550. # Force CoW by recreating the list first
  551. self.overridedata[shortvar] = list(self.overridedata[shortvar])
  552. self.overridedata[shortvar].remove([var, override])
  553. except ValueError as e:
  554. pass
  555. override = None
  556. if "_" in shortvar:
  557. override = var[shortvar.rfind('_')+1:]
  558. shortvar = var[:shortvar.rfind('_')]
  559. if len(shortvar) == 0:
  560. override = None
  561. def setVarFlag(self, var, flag, value, **loginfo):
  562. self.expand_cache = {}
  563. if 'op' not in loginfo:
  564. loginfo['op'] = "set"
  565. loginfo['flag'] = flag
  566. self.varhistory.record(**loginfo)
  567. if not var in self.dict:
  568. self._makeShadowCopy(var)
  569. self.dict[var][flag] = value
  570. if flag == "_defaultval" and '_' in var:
  571. self._setvar_update_overrides(var, **loginfo)
  572. if flag == "_defaultval" and var in self.overridevars:
  573. self._setvar_update_overridevars(var, value)
  574. if flag == "unexport" or flag == "export":
  575. if not "__exportlist" in self.dict:
  576. self._makeShadowCopy("__exportlist")
  577. if not "_content" in self.dict["__exportlist"]:
  578. self.dict["__exportlist"]["_content"] = set()
  579. self.dict["__exportlist"]["_content"].add(var)
  580. def getVarFlag(self, var, flag, expand, noweakdefault=False, parsing=False):
  581. local_var = self._findVar(var)
  582. value = None
  583. if flag == "_content" and var in self.overridedata and not parsing:
  584. match = False
  585. active = {}
  586. self.need_overrides()
  587. for (r, o) in self.overridedata[var]:
  588. # What about double overrides both with "_" in the name?
  589. if o in self.overridesset:
  590. active[o] = r
  591. elif "_" in o:
  592. if set(o.split("_")).issubset(self.overridesset):
  593. active[o] = r
  594. mod = True
  595. while mod:
  596. mod = False
  597. for o in self.overrides:
  598. for a in active.copy():
  599. if a.endswith("_" + o):
  600. t = active[a]
  601. del active[a]
  602. active[a.replace("_" + o, "")] = t
  603. mod = True
  604. elif a == o:
  605. match = active[a]
  606. del active[a]
  607. if match:
  608. value = self.getVar(match, False)
  609. if local_var is not None and value is None:
  610. if flag in local_var:
  611. value = copy.copy(local_var[flag])
  612. elif flag == "_content" and "_defaultval" in local_var and not noweakdefault:
  613. value = copy.copy(local_var["_defaultval"])
  614. if flag == "_content" and local_var is not None and "_append" in local_var and not parsing:
  615. if not value:
  616. value = ""
  617. self.need_overrides()
  618. for (r, o) in local_var["_append"]:
  619. match = True
  620. if o:
  621. for o2 in o.split("_"):
  622. if not o2 in self.overrides:
  623. match = False
  624. if match:
  625. value = value + r
  626. if flag == "_content" and local_var is not None and "_prepend" in local_var and not parsing:
  627. if not value:
  628. value = ""
  629. self.need_overrides()
  630. for (r, o) in local_var["_prepend"]:
  631. match = True
  632. if o:
  633. for o2 in o.split("_"):
  634. if not o2 in self.overrides:
  635. match = False
  636. if match:
  637. value = r + value
  638. if expand and value:
  639. # Only getvar (flag == _content) hits the expand cache
  640. cachename = None
  641. if flag == "_content":
  642. cachename = var
  643. else:
  644. cachename = var + "[" + flag + "]"
  645. value = self.expand(value, cachename)
  646. if value and flag == "_content" and local_var is not None and "_remove" in local_var:
  647. removes = []
  648. self.need_overrides()
  649. for (r, o) in local_var["_remove"]:
  650. match = True
  651. if o:
  652. for o2 in o.split("_"):
  653. if not o2 in self.overrides:
  654. match = False
  655. if match:
  656. removes.extend(self.expand(r).split())
  657. filtered = filter(lambda v: v not in removes,
  658. value.split())
  659. value = " ".join(filtered)
  660. if expand and var in self.expand_cache:
  661. # We need to ensure the expand cache has the correct value
  662. # flag == "_content" here
  663. self.expand_cache[var].value = value
  664. return value
  665. def delVarFlag(self, var, flag, **loginfo):
  666. self.expand_cache = {}
  667. local_var = self._findVar(var)
  668. if not local_var:
  669. return
  670. if not var in self.dict:
  671. self._makeShadowCopy(var)
  672. if var in self.dict and flag in self.dict[var]:
  673. loginfo['detail'] = ""
  674. loginfo['op'] = 'delFlag'
  675. loginfo['flag'] = flag
  676. self.varhistory.record(**loginfo)
  677. del self.dict[var][flag]
  678. def appendVarFlag(self, var, flag, value, **loginfo):
  679. loginfo['op'] = 'append'
  680. loginfo['flag'] = flag
  681. self.varhistory.record(**loginfo)
  682. newvalue = (self.getVarFlag(var, flag, False) or "") + value
  683. self.setVarFlag(var, flag, newvalue, ignore=True)
  684. def prependVarFlag(self, var, flag, value, **loginfo):
  685. loginfo['op'] = 'prepend'
  686. loginfo['flag'] = flag
  687. self.varhistory.record(**loginfo)
  688. newvalue = value + (self.getVarFlag(var, flag, False) or "")
  689. self.setVarFlag(var, flag, newvalue, ignore=True)
  690. def setVarFlags(self, var, flags, **loginfo):
  691. self.expand_cache = {}
  692. infer_caller_details(loginfo)
  693. if not var in self.dict:
  694. self._makeShadowCopy(var)
  695. for i in flags:
  696. if i == "_content":
  697. continue
  698. loginfo['flag'] = i
  699. loginfo['detail'] = flags[i]
  700. self.varhistory.record(**loginfo)
  701. self.dict[var][i] = flags[i]
  702. def getVarFlags(self, var, expand = False, internalflags=False):
  703. local_var = self._findVar(var)
  704. flags = {}
  705. if local_var:
  706. for i in local_var:
  707. if i.startswith("_") and not internalflags:
  708. continue
  709. flags[i] = local_var[i]
  710. if expand and i in expand:
  711. flags[i] = self.expand(flags[i], var + "[" + i + "]")
  712. if len(flags) == 0:
  713. return None
  714. return flags
  715. def delVarFlags(self, var, **loginfo):
  716. self.expand_cache = {}
  717. if not var in self.dict:
  718. self._makeShadowCopy(var)
  719. if var in self.dict:
  720. content = None
  721. loginfo['op'] = 'delete flags'
  722. self.varhistory.record(**loginfo)
  723. # try to save the content
  724. if "_content" in self.dict[var]:
  725. content = self.dict[var]["_content"]
  726. self.dict[var] = {}
  727. self.dict[var]["_content"] = content
  728. else:
  729. del self.dict[var]
  730. def createCopy(self):
  731. """
  732. Create a copy of self by setting _data to self
  733. """
  734. # we really want this to be a DataSmart...
  735. data = DataSmart()
  736. data.dict["_data"] = self.dict
  737. data.varhistory = self.varhistory.copy()
  738. data.varhistory.datasmart = data
  739. data.inchistory = self.inchistory.copy()
  740. data._tracking = self._tracking
  741. data.overrides = None
  742. data.overridevars = copy.copy(self.overridevars)
  743. # Should really be a deepcopy but has heavy overhead.
  744. # Instead, we're careful with writes.
  745. data.overridedata = copy.copy(self.overridedata)
  746. return data
  747. def expandVarref(self, variable, parents=False):
  748. """Find all references to variable in the data and expand it
  749. in place, optionally descending to parent datastores."""
  750. if parents:
  751. keys = iter(self)
  752. else:
  753. keys = self.localkeys()
  754. ref = '${%s}' % variable
  755. value = self.getVar(variable, False)
  756. for key in keys:
  757. referrervalue = self.getVar(key, False)
  758. if referrervalue and ref in referrervalue:
  759. self.setVar(key, referrervalue.replace(ref, value))
  760. def localkeys(self):
  761. for key in self.dict:
  762. if key != '_data':
  763. yield key
  764. def __iter__(self):
  765. deleted = set()
  766. overrides = set()
  767. def keylist(d):
  768. klist = set()
  769. for key in d:
  770. if key == "_data":
  771. continue
  772. if key in deleted:
  773. continue
  774. if key in overrides:
  775. continue
  776. if not d[key]:
  777. deleted.add(key)
  778. continue
  779. klist.add(key)
  780. if "_data" in d:
  781. klist |= keylist(d["_data"])
  782. return klist
  783. self.need_overrides()
  784. for var in self.overridedata:
  785. for (r, o) in self.overridedata[var]:
  786. if o in self.overridesset:
  787. overrides.add(var)
  788. elif "_" in o:
  789. if set(o.split("_")).issubset(self.overridesset):
  790. overrides.add(var)
  791. for k in keylist(self.dict):
  792. yield k
  793. for k in overrides:
  794. yield k
  795. def __len__(self):
  796. return len(frozenset(iter(self)))
  797. def __getitem__(self, item):
  798. value = self.getVar(item, False)
  799. if value is None:
  800. raise KeyError(item)
  801. else:
  802. return value
  803. def __setitem__(self, var, value):
  804. self.setVar(var, value)
  805. def __delitem__(self, var):
  806. self.delVar(var)
  807. def get_hash(self):
  808. data = {}
  809. d = self.createCopy()
  810. bb.data.expandKeys(d)
  811. bb.data.update_data(d)
  812. config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST", True) or "").split())
  813. keys = set(key for key in iter(d) if not key.startswith("__"))
  814. for key in keys:
  815. if key in config_whitelist:
  816. continue
  817. value = d.getVar(key, False) or ""
  818. data.update({key:value})
  819. varflags = d.getVarFlags(key, internalflags = True)
  820. if not varflags:
  821. continue
  822. for f in varflags:
  823. if f == "_content":
  824. continue
  825. data.update({'%s[%s]' % (key, f):varflags[f]})
  826. for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]:
  827. bb_list = d.getVar(key, False) or []
  828. bb_list.sort()
  829. data.update({key:str(bb_list)})
  830. if key == "__BBANONFUNCS":
  831. for i in bb_list:
  832. value = d.getVar(i, False) or ""
  833. data.update({i:value})
  834. data_str = str([(k, data[k]) for k in sorted(data.keys())])
  835. return hashlib.md5(data_str.encode("utf-8")).hexdigest()