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