summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
authorPeter Seebach <peter.seebach@windriver.com>2013-01-18 11:47:34 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2013-01-18 12:35:19 +0000
commitfe30cbc2cb0719092f19cba3b06a37e93260994c (patch)
tree715c87a0c77aa75255395b3e70796811ddce3859 /bitbake
parent4dd6d9139cb77f2b0ff7ab9482e853108efad5aa (diff)
downloadpoky-fe30cbc2cb0719092f19cba3b06a37e93260994c.tar.gz
bitbake: bitbake: data_smart.py and friends: Track variable history
This patch adds tracking of the history of variable assignments. The changes are predominantly localized to data_smart.py and parse/ast.py. cooker.py and data.py are altered to display the recorded data, and turn tracking on for the bitbake -e case. The data.py update_data() function warns DataSmart.finalize() to report the caller one further back up the tree. In general, d.setVar() does what it used to do. Optionally, arguments describing an operation may be appended; if none are present, the operation is implicitly ignored. If it's not ignored, it will attempt to infer missing information (name of variable, value assigned, file and line) by examining the traceback. This slightly elaborate process eliminates a category of problems in which the 'var' member of the keyword arguments dict is set, and a positional argument corresponding to 'var' is also set. It also makes calling much simpler for the common cases. The resulting output gives you a pretty good picture of what values got set, and how they got set. RP Modifications: a) Split from IncludeHistory to separate VariableHistory b) Add dedicated copy function instead of deepcopy c) Use COW for variables dict d) Remove 'value' loginfo value and just use 'details' e) Desensitise code for calling order (set 'op' before/after infer_caller_details was error prone) f) Fix bug where ?= "" wasn't shown correctly g) Log more set operations as some variables mysteriously acquired values previously h) Standardise infer_caller_details to be triggered from .record() where at all possible to reduce overhead in non-enabled cases i) Rename variable parameter names to match inference code j) Add VariableHistory emit() function to match IncludeHistory k) Fix handling of appendVar, prependVar and matching flag ops l) Use ignored=True to stop logging further events where appropriate (Bitbake rev: f00524a3729000cbcb3317fee933ac448fae5e2d) Signed-off-by: Peter Seebach <peter.seebach@windriver.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-rw-r--r--bitbake/lib/bb/cooker.py10
-rw-r--r--bitbake/lib/bb/data.py9
-rw-r--r--bitbake/lib/bb/data_smart.py256
-rw-r--r--bitbake/lib/bb/parse/ast.py30
4 files changed, 251 insertions, 54 deletions
diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py
index f06b71c42c..caf1123496 100644
--- a/bitbake/lib/bb/cooker.py
+++ b/bitbake/lib/bb/cooker.py
@@ -177,6 +177,8 @@ class BBCooker:
177 177
178 def initConfigurationData(self): 178 def initConfigurationData(self):
179 self.configuration.data = bb.data.init() 179 self.configuration.data = bb.data.init()
180 if self.configuration.show_environment:
181 self.configuration.data.enableTracking()
180 182
181 if not self.server_registration_cb: 183 if not self.server_registration_cb:
182 self.configuration.data.setVar("BB_WORKERCONTEXT", "1") 184 self.configuration.data.setVar("BB_WORKERCONTEXT", "1")
@@ -185,13 +187,7 @@ class BBCooker:
185 bb.data.inheritFromOS(self.configuration.data, self.savedenv, filtered_keys) 187 bb.data.inheritFromOS(self.configuration.data, self.savedenv, filtered_keys)
186 188
187 def loadConfigurationData(self): 189 def loadConfigurationData(self):
188 self.configuration.data = bb.data.init() 190 self.initConfigurationData()
189
190 if not self.server_registration_cb:
191 self.configuration.data.setVar("BB_WORKERCONTEXT", "1")
192
193 filtered_keys = bb.utils.approved_variables()
194 bb.data.inheritFromOS(self.configuration.data, self.savedenv, filtered_keys)
195 191
196 try: 192 try:
197 self.parseConfigurationFiles(self.configuration.prefile, 193 self.parseConfigurationFiles(self.configuration.prefile,
diff --git a/bitbake/lib/bb/data.py b/bitbake/lib/bb/data.py
index 2c3e905a77..7047f48934 100644
--- a/bitbake/lib/bb/data.py
+++ b/bitbake/lib/bb/data.py
@@ -166,9 +166,9 @@ def inheritFromOS(d, savedenv, permitted):
166 for s in savedenv.keys(): 166 for s in savedenv.keys():
167 if s in permitted: 167 if s in permitted:
168 try: 168 try:
169 d.setVar(s, getVar(s, savedenv, True)) 169 d.setVar(s, getVar(s, savedenv, True), op = 'from env')
170 if s in exportlist: 170 if s in exportlist:
171 d.setVarFlag(s, "export", True) 171 d.setVarFlag(s, "export", True, op = 'auto env export')
172 except TypeError: 172 except TypeError:
173 pass 173 pass
174 174
@@ -194,8 +194,7 @@ def emit_var(var, o=sys.__stdout__, d = init(), all=False):
194 return 0 194 return 0
195 195
196 if all: 196 if all:
197 commentVal = re.sub('\n', '\n#', str(oval)) 197 d.varhistory.emit(var, oval, val, o)
198 o.write('# %s=%s\n' % (var, commentVal))
199 198
200 if (var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1) and not all: 199 if (var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1) and not all:
201 return 0 200 return 0
@@ -274,7 +273,7 @@ def emit_func(func, o=sys.__stdout__, d = init()):
274 273
275def update_data(d): 274def update_data(d):
276 """Performs final steps upon the datastore, including application of overrides""" 275 """Performs final steps upon the datastore, including application of overrides"""
277 d.finalize() 276 d.finalize(parent = True)
278 277
279def build_dependencies(key, keys, shelldeps, vardepvals, d): 278def build_dependencies(key, keys, shelldeps, vardepvals, d):
280 deps = set() 279 deps = set()
diff --git a/bitbake/lib/bb/data_smart.py b/bitbake/lib/bb/data_smart.py
index 5fdfeee2c7..ddf98e6a2e 100644
--- a/bitbake/lib/bb/data_smart.py
+++ b/bitbake/lib/bb/data_smart.py
@@ -28,7 +28,7 @@ BitBake build tools.
28# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 28# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29# Based on functions from the base bb module, Copyright 2003 Holger Schurig 29# Based on functions from the base bb module, Copyright 2003 Holger Schurig
30 30
31import copy, re 31import copy, re, sys, traceback
32from collections import MutableMapping 32from collections import MutableMapping
33import logging 33import logging
34import hashlib 34import hashlib
@@ -43,6 +43,42 @@ __setvar_regexp__ = re.compile('(?P<base>.*?)(?P<keyword>_append|_prepend)(_(?P<
43__expand_var_regexp__ = re.compile(r"\${[^{}]+}") 43__expand_var_regexp__ = re.compile(r"\${[^{}]+}")
44__expand_python_regexp__ = re.compile(r"\${@.+?}") 44__expand_python_regexp__ = re.compile(r"\${@.+?}")
45 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
46 82
47class VariableParse: 83class VariableParse:
48 def __init__(self, varname, d, val = None): 84 def __init__(self, varname, d, val = None):
@@ -157,11 +193,80 @@ class IncludeHistory(object):
157 o.write("\n") 193 o.write("\n")
158 child.emit(o, level) 194 child.emit(o, level)
159 195
196class VariableHistory(object):
197 def __init__(self, dataroot):
198 self.dataroot = dataroot
199 self.variables = COWDictBase.copy()
200
201 def copy(self):
202 new = VariableHistory(self.dataroot)
203 new.variables = self.variables.copy()
204 return new
205
206 def record(self, *kwonly, **loginfo):
207 if not self.dataroot._tracking:
208 return
209 if len(kwonly) > 0:
210 raise TypeError
211 infer_caller_details(loginfo, parent = True)
212 if 'ignore' in loginfo and loginfo['ignore']:
213 return
214 if 'op' not in loginfo or not loginfo['op']:
215 loginfo['op'] = 'set'
216 if 'detail' in loginfo:
217 loginfo['detail'] = str(loginfo['detail'])
218 if 'variable' not in loginfo or 'file' not in loginfo:
219 raise ValueError("record() missing variable or file.")
220 var = loginfo['variable']
221
222 if var not in self.variables:
223 self.variables[var] = []
224 self.variables[var].append(loginfo.copy())
225
226 def variable(self, var):
227 if var in self.variables:
228 return self.variables[var]
229 else:
230 return []
231
232 def emit(self, var, oval, val, o):
233 history = self.variable(var)
234 commentVal = re.sub('\n', '\n#', str(oval))
235 if history:
236 if len(history) == 1:
237 o.write("#\n# $%s\n" % var)
238 else:
239 o.write("#\n# $%s [%d operations]\n" % (var, len(history)))
240 for event in history:
241 # o.write("# %s\n" % str(event))
242 if 'func' in event:
243 # If we have a function listed, this is internal
244 # code, not an operation in a config file, and the
245 # full path is distracting.
246 event['file'] = re.sub('.*/', '', event['file'])
247 display_func = ' [%s]' % event['func']
248 else:
249 display_func = ''
250 if 'flag' in event:
251 flag = '[%s] ' % (event['flag'])
252 else:
253 flag = ''
254 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'])))
255 if len(history) > 1:
256 o.write("# computed:\n")
257 o.write('# "%s"\n' % (commentVal))
258 else:
259 o.write("#\n# $%s\n# [no history recorded]\n#\n" % var)
260 o.write('# "%s"\n' % (commentVal))
261
262
160class DataSmart(MutableMapping): 263class DataSmart(MutableMapping):
161 def __init__(self, special = COWDictBase.copy(), seen = COWDictBase.copy() ): 264 def __init__(self, special = COWDictBase.copy(), seen = COWDictBase.copy() ):
162 self.dict = {} 265 self.dict = {}
163 266
164 self.inchistory = IncludeHistory() 267 self.inchistory = IncludeHistory()
268 self.varhistory = VariableHistory(self)
269 self._tracking = False
165 270
166 # cookie monster tribute 271 # cookie monster tribute
167 self._special_values = special 272 self._special_values = special
@@ -169,6 +274,12 @@ class DataSmart(MutableMapping):
169 274
170 self.expand_cache = {} 275 self.expand_cache = {}
171 276
277 def enableTracking(self):
278 self._tracking = True
279
280 def disableTracking(self):
281 self._tracking = False
282
172 def expandWithRefs(self, s, varname): 283 def expandWithRefs(self, s, varname):
173 284
174 if not isinstance(s, basestring): # sanity check 285 if not isinstance(s, basestring): # sanity check
@@ -204,10 +315,14 @@ class DataSmart(MutableMapping):
204 return self.expandWithRefs(s, varname).value 315 return self.expandWithRefs(s, varname).value
205 316
206 317
207 def finalize(self): 318 def finalize(self, parent = False):
208 """Performs final steps upon the datastore, including application of overrides""" 319 """Performs final steps upon the datastore, including application of overrides"""
209 320
210 overrides = (self.getVar("OVERRIDES", True) or "").split(":") or [] 321 overrides = (self.getVar("OVERRIDES", True) or "").split(":") or []
322 finalize_caller = {
323 'op': 'finalize',
324 }
325 infer_caller_details(finalize_caller, parent = parent, varval = False)
211 326
212 # 327 #
213 # Well let us see what breaks here. We used to iterate 328 # Well let us see what breaks here. We used to iterate
@@ -224,6 +339,9 @@ class DataSmart(MutableMapping):
224 # Then we will handle _append and _prepend 339 # Then we will handle _append and _prepend
225 # 340 #
226 341
342 # We only want to report finalization once per variable overridden.
343 finalizes_reported = {}
344
227 for o in overrides: 345 for o in overrides:
228 # calculate '_'+override 346 # calculate '_'+override
229 l = len(o) + 1 347 l = len(o) + 1
@@ -236,7 +354,19 @@ class DataSmart(MutableMapping):
236 for var in vars: 354 for var in vars:
237 name = var[:-l] 355 name = var[:-l]
238 try: 356 try:
239 self.setVar(name, self.getVar(var, False)) 357 # Report only once, even if multiple changes.
358 if name not in finalizes_reported:
359 finalizes_reported[name] = True
360 finalize_caller['variable'] = name
361 finalize_caller['detail'] = 'was: ' + str(self.getVar(name, False))
362 self.varhistory.record(**finalize_caller)
363 # Copy history of the override over.
364 for event in self.varhistory.variable(var):
365 loginfo = event.copy()
366 loginfo['variable'] = name
367 loginfo['op'] = 'override[%s]:%s' % (o, loginfo['op'])
368 self.varhistory.record(**loginfo)
369 self.setVar(name, self.getVar(var, False), op = 'finalize', file = 'override[%s]' % o, line = '')
240 self.delVar(var) 370 self.delVar(var)
241 except Exception: 371 except Exception:
242 logger.info("Untracked delVar") 372 logger.info("Untracked delVar")
@@ -267,9 +397,9 @@ class DataSmart(MutableMapping):
267 397
268 # We save overrides that may be applied at some later stage 398 # We save overrides that may be applied at some later stage
269 if keep: 399 if keep:
270 self.setVarFlag(append, op, keep) 400 self.setVarFlag(append, op, keep, ignore=True)
271 else: 401 else:
272 self.delVarFlag(append, op) 402 self.delVarFlag(append, op, ignore=True)
273 403
274 def initVar(self, var): 404 def initVar(self, var):
275 self.expand_cache = {} 405 self.expand_cache = {}
@@ -297,7 +427,10 @@ class DataSmart(MutableMapping):
297 else: 427 else:
298 self.initVar(var) 428 self.initVar(var)
299 429
300 def setVar(self, var, value): 430
431 def setVar(self, var, value, **loginfo):
432 if 'op' not in loginfo:
433 loginfo['op'] = "set"
301 self.expand_cache = {} 434 self.expand_cache = {}
302 match = __setvar_regexp__.match(var) 435 match = __setvar_regexp__.match(var)
303 if match and match.group("keyword") in __setvar_keyword__: 436 if match and match.group("keyword") in __setvar_keyword__:
@@ -306,15 +439,22 @@ class DataSmart(MutableMapping):
306 override = match.group('add') 439 override = match.group('add')
307 l = self.getVarFlag(base, keyword) or [] 440 l = self.getVarFlag(base, keyword) or []
308 l.append([value, override]) 441 l.append([value, override])
309 self.setVarFlag(base, keyword, l) 442 self.setVarFlag(base, keyword, l, ignore=True)
310 443 # And cause that to be recorded:
444 loginfo['detail'] = value
445 loginfo['variable'] = base
446 if override:
447 loginfo['op'] = '%s[%s]' % (keyword, override)
448 else:
449 loginfo['op'] = keyword
450 self.varhistory.record(**loginfo)
311 # todo make sure keyword is not __doc__ or __module__ 451 # todo make sure keyword is not __doc__ or __module__
312 # pay the cookie monster 452 # pay the cookie monster
313 try: 453 try:
314 self._special_values[keyword].add( base ) 454 self._special_values[keyword].add(base)
315 except KeyError: 455 except KeyError:
316 self._special_values[keyword] = set() 456 self._special_values[keyword] = set()
317 self._special_values[keyword].add( base ) 457 self._special_values[keyword].add(base)
318 458
319 return 459 return
320 460
@@ -331,6 +471,7 @@ class DataSmart(MutableMapping):
331 471
332 # setting var 472 # setting var
333 self.dict[var]["_content"] = value 473 self.dict[var]["_content"] = value
474 self.varhistory.record(**loginfo)
334 475
335 def getVar(self, var, expand=False, noweakdefault=False): 476 def getVar(self, var, expand=False, noweakdefault=False):
336 value = self.getVarFlag(var, "_content", False, noweakdefault) 477 value = self.getVarFlag(var, "_content", False, noweakdefault)
@@ -340,13 +481,17 @@ class DataSmart(MutableMapping):
340 return self.expand(value, var) 481 return self.expand(value, var)
341 return value 482 return value
342 483
343 def renameVar(self, key, newkey): 484 def renameVar(self, key, newkey, **loginfo):
344 """ 485 """
345 Rename the variable key to newkey 486 Rename the variable key to newkey
346 """ 487 """
347 val = self.getVar(key, 0) 488 val = self.getVar(key, 0)
348 if val is not None: 489 if val is not None:
349 self.setVar(newkey, val) 490 loginfo['variable'] = newkey
491 loginfo['op'] = 'rename from %s' % key
492 loginfo['detail'] = val
493 self.varhistory.record(**loginfo)
494 self.setVar(newkey, val, ignore=True)
350 495
351 for i in ('_append', '_prepend'): 496 for i in ('_append', '_prepend'):
352 src = self.getVarFlag(key, i) 497 src = self.getVarFlag(key, i)
@@ -355,23 +500,34 @@ class DataSmart(MutableMapping):
355 500
356 dest = self.getVarFlag(newkey, i) or [] 501 dest = self.getVarFlag(newkey, i) or []
357 dest.extend(src) 502 dest.extend(src)
358 self.setVarFlag(newkey, i, dest) 503 self.setVarFlag(newkey, i, dest, ignore=True)
359 504
360 if i in self._special_values and key in self._special_values[i]: 505 if i in self._special_values and key in self._special_values[i]:
361 self._special_values[i].remove(key) 506 self._special_values[i].remove(key)
362 self._special_values[i].add(newkey) 507 self._special_values[i].add(newkey)
363 508
364 self.delVar(key) 509 loginfo['variable'] = key
365 510 loginfo['op'] = 'rename (to)'
366 def appendVar(self, key, value): 511 loginfo['detail'] = newkey
367 value = (self.getVar(key, False) or "") + value 512 self.varhistory.record(**loginfo)
368 self.setVar(key, value) 513 self.delVar(key, ignore=True)
369 514
370 def prependVar(self, key, value): 515 def appendVar(self, var, value, **loginfo):
371 value = value + (self.getVar(key, False) or "") 516 loginfo['op'] = 'append'
372 self.setVar(key, value) 517 self.varhistory.record(**loginfo)
373 518 newvalue = (self.getVar(var, False) or "") + value
374 def delVar(self, var): 519 self.setVar(var, newvalue, ignore=True)
520
521 def prependVar(self, var, value, **loginfo):
522 loginfo['op'] = 'prepend'
523 self.varhistory.record(**loginfo)
524 newvalue = value + (self.getVar(var, False) or "")
525 self.setVar(var, newvalue, ignore=True)
526
527 def delVar(self, var, **loginfo):
528 loginfo['detail'] = ""
529 loginfo['op'] = 'del'
530 self.varhistory.record(**loginfo)
375 self.expand_cache = {} 531 self.expand_cache = {}
376 self.dict[var] = {} 532 self.dict[var] = {}
377 if '_' in var: 533 if '_' in var:
@@ -379,10 +535,14 @@ class DataSmart(MutableMapping):
379 if override and override in self._seen_overrides and var in self._seen_overrides[override]: 535 if override and override in self._seen_overrides and var in self._seen_overrides[override]:
380 self._seen_overrides[override].remove(var) 536 self._seen_overrides[override].remove(var)
381 537
382 def setVarFlag(self, var, flag, flagvalue): 538 def setVarFlag(self, var, flag, value, **loginfo):
539 if 'op' not in loginfo:
540 loginfo['op'] = "set"
541 loginfo['flag'] = flag
542 self.varhistory.record(**loginfo)
383 if not var in self.dict: 543 if not var in self.dict:
384 self._makeShadowCopy(var) 544 self._makeShadowCopy(var)
385 self.dict[var][flag] = flagvalue 545 self.dict[var][flag] = value
386 546
387 def getVarFlag(self, var, flag, expand=False, noweakdefault=False): 547 def getVarFlag(self, var, flag, expand=False, noweakdefault=False):
388 local_var = self._findVar(var) 548 local_var = self._findVar(var)
@@ -396,7 +556,7 @@ class DataSmart(MutableMapping):
396 value = self.expand(value, None) 556 value = self.expand(value, None)
397 return value 557 return value
398 558
399 def delVarFlag(self, var, flag): 559 def delVarFlag(self, var, flag, **loginfo):
400 local_var = self._findVar(var) 560 local_var = self._findVar(var)
401 if not local_var: 561 if not local_var:
402 return 562 return
@@ -404,23 +564,38 @@ class DataSmart(MutableMapping):
404 self._makeShadowCopy(var) 564 self._makeShadowCopy(var)
405 565
406 if var in self.dict and flag in self.dict[var]: 566 if var in self.dict and flag in self.dict[var]:
407 del self.dict[var][flag] 567 loginfo['detail'] = ""
408 568 loginfo['op'] = 'delFlag'
409 def appendVarFlag(self, key, flag, value): 569 loginfo['flag'] = flag
410 value = (self.getVarFlag(key, flag, False) or "") + value 570 self.varhistory.record(**loginfo)
411 self.setVarFlag(key, flag, value)
412 571
413 def prependVarFlag(self, key, flag, value): 572 del self.dict[var][flag]
414 value = value + (self.getVarFlag(key, flag, False) or "")
415 self.setVarFlag(key, flag, value)
416 573
417 def setVarFlags(self, var, flags): 574 def appendVarFlag(self, var, flag, value, **loginfo):
575 loginfo['op'] = 'append'
576 loginfo['flag'] = flag
577 self.varhistory.record(**loginfo)
578 newvalue = (self.getVarFlag(var, flag, False) or "") + value
579 self.setVarFlag(var, flag, newvalue, ignore=True)
580
581 def prependVarFlag(self, var, flag, value, **loginfo):
582 loginfo['op'] = 'prepend'
583 loginfo['flag'] = flag
584 self.varhistory.record(**loginfo)
585 newvalue = value + (self.getVarFlag(var, flag, False) or "")
586 self.setVarFlag(var, flag, newvalue, ignore=True)
587
588 def setVarFlags(self, var, flags, **loginfo):
589 infer_caller_details(loginfo)
418 if not var in self.dict: 590 if not var in self.dict:
419 self._makeShadowCopy(var) 591 self._makeShadowCopy(var)
420 592
421 for i in flags: 593 for i in flags:
422 if i == "_content": 594 if i == "_content":
423 continue 595 continue
596 loginfo['flag'] = i
597 loginfo['detail'] = flags[i]
598 self.varhistory.record(**loginfo)
424 self.dict[var][i] = flags[i] 599 self.dict[var][i] = flags[i]
425 600
426 def getVarFlags(self, var): 601 def getVarFlags(self, var):
@@ -438,13 +613,16 @@ class DataSmart(MutableMapping):
438 return flags 613 return flags
439 614
440 615
441 def delVarFlags(self, var): 616 def delVarFlags(self, var, **loginfo):
442 if not var in self.dict: 617 if not var in self.dict:
443 self._makeShadowCopy(var) 618 self._makeShadowCopy(var)
444 619
445 if var in self.dict: 620 if var in self.dict:
446 content = None 621 content = None
447 622
623 loginfo['op'] = 'delete flags'
624 self.varhistory.record(**loginfo)
625
448 # try to save the content 626 # try to save the content
449 if "_content" in self.dict[var]: 627 if "_content" in self.dict[var]:
450 content = self.dict[var]["_content"] 628 content = self.dict[var]["_content"]
@@ -461,8 +639,12 @@ class DataSmart(MutableMapping):
461 # we really want this to be a DataSmart... 639 # we really want this to be a DataSmart...
462 data = DataSmart(seen=self._seen_overrides.copy(), special=self._special_values.copy()) 640 data = DataSmart(seen=self._seen_overrides.copy(), special=self._special_values.copy())
463 data.dict["_data"] = self.dict 641 data.dict["_data"] = self.dict
642 data.varhistory = self.varhistory.copy()
643 data.varhistory.datasmart = data
464 data.inchistory = self.inchistory.copy() 644 data.inchistory = self.inchistory.copy()
465 645
646 data._tracking = self._tracking
647
466 return data 648 return data
467 649
468 def expandVarref(self, variable, parents=False): 650 def expandVarref(self, variable, parents=False):
diff --git a/bitbake/lib/bb/parse/ast.py b/bitbake/lib/bb/parse/ast.py
index 79e9f7e170..b2657f8044 100644
--- a/bitbake/lib/bb/parse/ast.py
+++ b/bitbake/lib/bb/parse/ast.py
@@ -68,7 +68,7 @@ class ExportNode(AstNode):
68 self.var = var 68 self.var = var
69 69
70 def eval(self, data): 70 def eval(self, data):
71 data.setVarFlag(self.var, "export", 1) 71 data.setVarFlag(self.var, "export", 1, op = 'exported')
72 72
73class DataNode(AstNode): 73class DataNode(AstNode):
74 """ 74 """
@@ -90,33 +90,53 @@ class DataNode(AstNode):
90 def eval(self, data): 90 def eval(self, data):
91 groupd = self.groupd 91 groupd = self.groupd
92 key = groupd["var"] 92 key = groupd["var"]
93 loginfo = {
94 'variable': key,
95 'file': self.filename,
96 'line': self.lineno,
97 }
93 if "exp" in groupd and groupd["exp"] != None: 98 if "exp" in groupd and groupd["exp"] != None:
94 data.setVarFlag(key, "export", 1) 99 data.setVarFlag(key, "export", 1, op = 'exported', **loginfo)
100
101 op = "set"
95 if "ques" in groupd and groupd["ques"] != None: 102 if "ques" in groupd and groupd["ques"] != None:
96 val = self.getFunc(key, data) 103 val = self.getFunc(key, data)
104 op = "set?"
97 if val == None: 105 if val == None:
98 val = groupd["value"] 106 val = groupd["value"]
99 elif "colon" in groupd and groupd["colon"] != None: 107 elif "colon" in groupd and groupd["colon"] != None:
100 e = data.createCopy() 108 e = data.createCopy()
101 bb.data.update_data(e) 109 bb.data.update_data(e)
110 op = "immediate"
102 val = e.expand(groupd["value"], key + "[:=]") 111 val = e.expand(groupd["value"], key + "[:=]")
103 elif "append" in groupd and groupd["append"] != None: 112 elif "append" in groupd and groupd["append"] != None:
113 op = "append"
104 val = "%s %s" % ((self.getFunc(key, data) or ""), groupd["value"]) 114 val = "%s %s" % ((self.getFunc(key, data) or ""), groupd["value"])
105 elif "prepend" in groupd and groupd["prepend"] != None: 115 elif "prepend" in groupd and groupd["prepend"] != None:
116 op = "prepend"
106 val = "%s %s" % (groupd["value"], (self.getFunc(key, data) or "")) 117 val = "%s %s" % (groupd["value"], (self.getFunc(key, data) or ""))
107 elif "postdot" in groupd and groupd["postdot"] != None: 118 elif "postdot" in groupd and groupd["postdot"] != None:
119 op = "postdot"
108 val = "%s%s" % ((self.getFunc(key, data) or ""), groupd["value"]) 120 val = "%s%s" % ((self.getFunc(key, data) or ""), groupd["value"])
109 elif "predot" in groupd and groupd["predot"] != None: 121 elif "predot" in groupd and groupd["predot"] != None:
122 op = "predot"
110 val = "%s%s" % (groupd["value"], (self.getFunc(key, data) or "")) 123 val = "%s%s" % (groupd["value"], (self.getFunc(key, data) or ""))
111 else: 124 else:
112 val = groupd["value"] 125 val = groupd["value"]
113 126
127 flag = None
114 if 'flag' in groupd and groupd['flag'] != None: 128 if 'flag' in groupd and groupd['flag'] != None:
115 data.setVarFlag(key, groupd['flag'], val) 129 flag = groupd['flag']
116 elif groupd["lazyques"]: 130 elif groupd["lazyques"]:
117 data.setVarFlag(key, "defaultval", val) 131 flag = "defaultval"
132
133 loginfo['op'] = op
134 loginfo['detail'] = groupd["value"]
135
136 if flag:
137 data.setVarFlag(key, flag, val, **loginfo)
118 else: 138 else:
119 data.setVar(key, val) 139 data.setVar(key, val, **loginfo)
120 140
121class MethodNode(AstNode): 141class MethodNode(AstNode):
122 def __init__(self, filename, lineno, func_name, body): 142 def __init__(self, filename, lineno, func_name, body):