diff options
Diffstat (limited to 'bitbake/lib/bb/codeparser.py')
-rw-r--r-- | bitbake/lib/bb/codeparser.py | 97 |
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 | ||
88 | def update_module_dependencies(d): | 104 | def 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): | |||
112 | codecache = SetCache() | 128 | codecache = SetCache() |
113 | 129 | ||
114 | class pythonCacheLine(object): | 130 | class 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 | ||
370 | class ShellParser(): | 404 | class 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: |