summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/codeparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/codeparser.py')
-rw-r--r--bitbake/lib/bb/codeparser.py97
1 files changed, 73 insertions, 24 deletions
diff --git a/bitbake/lib/bb/codeparser.py b/bitbake/lib/bb/codeparser.py
index 2e8b7ced3c..4f70cf7fe7 100644
--- a/bitbake/lib/bb/codeparser.py
+++ b/bitbake/lib/bb/codeparser.py
@@ -69,12 +69,25 @@ def add_module_functions(fn, functions, namespace):
69 name = "%s.%s" % (namespace, f) 69 name = "%s.%s" % (namespace, f)
70 parser = PythonParser(name, logger) 70 parser = PythonParser(name, logger)
71 try: 71 try:
72 parser.parse_python(None, filename=fn, lineno=1, fixedhash=fixedhash+f) 72 parser.parse_python(None, filename=fn, lineno=1, fixedhash=fixedhash+f, func=functions[f])
73 #bb.warn("Cached %s" % f) 73 #bb.warn("Cached %s" % f)
74 except KeyError: 74 except KeyError:
75 lines, lineno = inspect.getsourcelines(functions[f]) 75 try:
76 targetfn = inspect.getsourcefile(functions[f])
77 except TypeError:
78 # Builtin
79 continue
80 if fn != targetfn:
81 # Skip references to other modules outside this file
82 #bb.warn("Skipping %s" % name)
83 continue
84 try:
85 lines, lineno = inspect.getsourcelines(functions[f])
86 except TypeError:
87 # Builtin
88 continue
76 src = "".join(lines) 89 src = "".join(lines)
77 parser.parse_python(src, filename=fn, lineno=lineno, fixedhash=fixedhash+f) 90 parser.parse_python(src, filename=fn, lineno=lineno, fixedhash=fixedhash+f, func=functions[f])
78 #bb.warn("Not cached %s" % f) 91 #bb.warn("Not cached %s" % f)
79 execs = parser.execs.copy() 92 execs = parser.execs.copy()
80 # Expand internal module exec references 93 # Expand internal module exec references
@@ -82,14 +95,17 @@ def add_module_functions(fn, functions, namespace):
82 if e in functions: 95 if e in functions:
83 execs.remove(e) 96 execs.remove(e)
84 execs.add(namespace + "." + e) 97 execs.add(namespace + "." + e)
85 modulecode_deps[name] = [parser.references.copy(), execs, parser.var_execs.copy(), parser.contains.copy()] 98 visitorcode = None
99 if hasattr(functions[f], 'visitorcode'):
100 visitorcode = getattr(functions[f], "visitorcode")
101 modulecode_deps[name] = [parser.references.copy(), execs, parser.var_execs.copy(), parser.contains.copy(), parser.extra, visitorcode]
86 #bb.warn("%s: %s\nRefs:%s Execs: %s %s %s" % (name, fn, parser.references, parser.execs, parser.var_execs, parser.contains)) 102 #bb.warn("%s: %s\nRefs:%s Execs: %s %s %s" % (name, fn, parser.references, parser.execs, parser.var_execs, parser.contains))
87 103
88def update_module_dependencies(d): 104def update_module_dependencies(d):
89 for mod in modulecode_deps: 105 for mod in modulecode_deps:
90 excludes = set((d.getVarFlag(mod, "vardepsexclude") or "").split()) 106 excludes = set((d.getVarFlag(mod, "vardepsexclude") or "").split())
91 if excludes: 107 if excludes:
92 modulecode_deps[mod] = [modulecode_deps[mod][0] - excludes, modulecode_deps[mod][1] - excludes, modulecode_deps[mod][2] - excludes, modulecode_deps[mod][3]] 108 modulecode_deps[mod] = [modulecode_deps[mod][0] - excludes, modulecode_deps[mod][1] - excludes, modulecode_deps[mod][2] - excludes, modulecode_deps[mod][3], modulecode_deps[mod][4], modulecode_deps[mod][5]]
93 109
94# A custom getstate/setstate using tuples is actually worth 15% cachesize by 110# A custom getstate/setstate using tuples is actually worth 15% cachesize by
95# avoiding duplication of the attribute names! 111# avoiding duplication of the attribute names!
@@ -112,21 +128,22 @@ class SetCache(object):
112codecache = SetCache() 128codecache = SetCache()
113 129
114class pythonCacheLine(object): 130class pythonCacheLine(object):
115 def __init__(self, refs, execs, contains): 131 def __init__(self, refs, execs, contains, extra):
116 self.refs = codecache.internSet(refs) 132 self.refs = codecache.internSet(refs)
117 self.execs = codecache.internSet(execs) 133 self.execs = codecache.internSet(execs)
118 self.contains = {} 134 self.contains = {}
119 for c in contains: 135 for c in contains:
120 self.contains[c] = codecache.internSet(contains[c]) 136 self.contains[c] = codecache.internSet(contains[c])
137 self.extra = extra
121 138
122 def __getstate__(self): 139 def __getstate__(self):
123 return (self.refs, self.execs, self.contains) 140 return (self.refs, self.execs, self.contains, self.extra)
124 141
125 def __setstate__(self, state): 142 def __setstate__(self, state):
126 (refs, execs, contains) = state 143 (refs, execs, contains, extra) = state
127 self.__init__(refs, execs, contains) 144 self.__init__(refs, execs, contains, extra)
128 def __hash__(self): 145 def __hash__(self):
129 l = (hash(self.refs), hash(self.execs)) 146 l = (hash(self.refs), hash(self.execs), hash(self.extra))
130 for c in sorted(self.contains.keys()): 147 for c in sorted(self.contains.keys()):
131 l = l + (c, hash(self.contains[c])) 148 l = l + (c, hash(self.contains[c]))
132 return hash(l) 149 return hash(l)
@@ -155,7 +172,7 @@ class CodeParserCache(MultiProcessCache):
155 # so that an existing cache gets invalidated. Additionally you'll need 172 # so that an existing cache gets invalidated. Additionally you'll need
156 # to increment __cache_version__ in cache.py in order to ensure that old 173 # to increment __cache_version__ in cache.py in order to ensure that old
157 # recipe caches don't trigger "Taskhash mismatch" errors. 174 # recipe caches don't trigger "Taskhash mismatch" errors.
158 CACHE_VERSION = 11 175 CACHE_VERSION = 14
159 176
160 def __init__(self): 177 def __init__(self):
161 MultiProcessCache.__init__(self) 178 MultiProcessCache.__init__(self)
@@ -169,8 +186,8 @@ class CodeParserCache(MultiProcessCache):
169 self.pythoncachelines = {} 186 self.pythoncachelines = {}
170 self.shellcachelines = {} 187 self.shellcachelines = {}
171 188
172 def newPythonCacheLine(self, refs, execs, contains): 189 def newPythonCacheLine(self, refs, execs, contains, extra):
173 cacheline = pythonCacheLine(refs, execs, contains) 190 cacheline = pythonCacheLine(refs, execs, contains, extra)
174 h = hash(cacheline) 191 h = hash(cacheline)
175 if h in self.pythoncachelines: 192 if h in self.pythoncachelines:
176 return self.pythoncachelines[h] 193 return self.pythoncachelines[h]
@@ -255,7 +272,15 @@ class PythonParser():
255 272
256 def visit_Call(self, node): 273 def visit_Call(self, node):
257 name = self.called_node_name(node.func) 274 name = self.called_node_name(node.func)
258 if name and (name.endswith(self.getvars) or name.endswith(self.getvarflags) or name in self.containsfuncs or name in self.containsanyfuncs): 275 if name and name in modulecode_deps and modulecode_deps[name][5]:
276 visitorcode = modulecode_deps[name][5]
277 contains, execs, warn = visitorcode(name, node.args)
278 for i in contains:
279 self.contains[i] = contains[i]
280 self.execs |= execs
281 if warn:
282 self.warn(node.func, warn)
283 elif name and (name.endswith(self.getvars) or name.endswith(self.getvarflags) or name in self.containsfuncs or name in self.containsanyfuncs):
259 if isinstance(node.args[0], ast.Constant) and isinstance(node.args[0].value, str): 284 if isinstance(node.args[0], ast.Constant) and isinstance(node.args[0].value, str):
260 varname = node.args[0].value 285 varname = node.args[0].value
261 if name in self.containsfuncs and isinstance(node.args[1], ast.Constant): 286 if name in self.containsfuncs and isinstance(node.args[1], ast.Constant):
@@ -323,7 +348,7 @@ class PythonParser():
323 # For the python module code it is expensive to have the function text so it is 348 # For the python module code it is expensive to have the function text so it is
324 # uses a different fixedhash to cache against. We can take the hit on obtaining the 349 # uses a different fixedhash to cache against. We can take the hit on obtaining the
325 # text if it isn't in the cache. 350 # text if it isn't in the cache.
326 def parse_python(self, node, lineno=0, filename="<string>", fixedhash=None): 351 def parse_python(self, node, lineno=0, filename="<string>", fixedhash=None, func=None):
327 if not fixedhash and (not node or not node.strip()): 352 if not fixedhash and (not node or not node.strip()):
328 return 353 return
329 354
@@ -338,6 +363,7 @@ class PythonParser():
338 self.contains = {} 363 self.contains = {}
339 for i in codeparsercache.pythoncache[h].contains: 364 for i in codeparsercache.pythoncache[h].contains:
340 self.contains[i] = set(codeparsercache.pythoncache[h].contains[i]) 365 self.contains[i] = set(codeparsercache.pythoncache[h].contains[i])
366 self.extra = codeparsercache.pythoncache[h].extra
341 return 367 return
342 368
343 if h in codeparsercache.pythoncacheextras: 369 if h in codeparsercache.pythoncacheextras:
@@ -346,6 +372,7 @@ class PythonParser():
346 self.contains = {} 372 self.contains = {}
347 for i in codeparsercache.pythoncacheextras[h].contains: 373 for i in codeparsercache.pythoncacheextras[h].contains:
348 self.contains[i] = set(codeparsercache.pythoncacheextras[h].contains[i]) 374 self.contains[i] = set(codeparsercache.pythoncacheextras[h].contains[i])
375 self.extra = codeparsercache.pythoncacheextras[h].extra
349 return 376 return
350 377
351 if fixedhash and not node: 378 if fixedhash and not node:
@@ -363,9 +390,16 @@ class PythonParser():
363 if n.__class__.__name__ == "Call": 390 if n.__class__.__name__ == "Call":
364 self.visit_Call(n) 391 self.visit_Call(n)
365 392
393 if func is not None:
394 self.references |= getattr(func, "bb_vardeps", set())
395 self.references -= getattr(func, "bb_vardepsexclude", set())
396
366 self.execs.update(self.var_execs) 397 self.execs.update(self.var_execs)
398 self.extra = None
399 if fixedhash:
400 self.extra = bbhash(str(node))
367 401
368 codeparsercache.pythoncacheextras[h] = codeparsercache.newPythonCacheLine(self.references, self.execs, self.contains) 402 codeparsercache.pythoncacheextras[h] = codeparsercache.newPythonCacheLine(self.references, self.execs, self.contains, self.extra)
369 403
370class ShellParser(): 404class ShellParser():
371 def __init__(self, name, log): 405 def __init__(self, name, log):
@@ -484,19 +518,34 @@ class ShellParser():
484 """ 518 """
485 519
486 words = list(words) 520 words = list(words)
487 for word in list(words): 521 for word in words:
488 wtree = pyshlex.make_wordtree(word[1]) 522 wtree = pyshlex.make_wordtree(word[1])
489 for part in wtree: 523 for part in wtree:
490 if not isinstance(part, list): 524 if not isinstance(part, list):
491 continue 525 continue
492 526
493 if part[0] in ('`', '$('): 527 candidates = [part]
494 command = pyshlex.wordtree_as_string(part[1:-1]) 528
495 self._parse_shell(command) 529 # If command is of type:
496 530 #
497 if word[0] in ("cmd_name", "cmd_word"): 531 # var="... $(cmd [...]) ..."
498 if word in words: 532 #
499 words.remove(word) 533 # Then iterate on what's between the quotes and if we find a
534 # list, make that what we check for below.
535 if len(part) >= 3 and part[0] == '"':
536 for p in part[1:-1]:
537 if isinstance(p, list):
538 candidates.append(p)
539
540 for candidate in candidates:
541 if len(candidate) >= 2:
542 if candidate[0] in ('`', '$('):
543 command = pyshlex.wordtree_as_string(candidate[1:-1])
544 self._parse_shell(command)
545
546 if word[0] in ("cmd_name", "cmd_word"):
547 if word in words:
548 words.remove(word)
500 549
501 usetoken = False 550 usetoken = False
502 for word in words: 551 for word in words: