data_smart.py 35 KB

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