diff options
Diffstat (limited to 'bitbake/lib/bb/codeparser.py')
-rw-r--r-- | bitbake/lib/bb/codeparser.py | 189 |
1 files changed, 87 insertions, 102 deletions
diff --git a/bitbake/lib/bb/codeparser.py b/bitbake/lib/bb/codeparser.py index 0dd9a365ee..0f3d646cc4 100644 --- a/bitbake/lib/bb/codeparser.py +++ b/bitbake/lib/bb/codeparser.py | |||
@@ -151,116 +151,103 @@ def parser_cache_savemerge(d): | |||
151 | 151 | ||
152 | 152 | ||
153 | class PythonParser(): | 153 | class PythonParser(): |
154 | class ValueVisitor(): | 154 | getvars = ("d.getVar", "bb.data.getVar", "data.getVar") |
155 | """Visitor to traverse a python abstract syntax tree and obtain | 155 | expands = ("d.expand", "bb.data.expand", "data.expand") |
156 | the variables referenced via bitbake metadata APIs, and the external | 156 | execfuncs = ("bb.build.exec_func", "bb.build.exec_task") |
157 | functions called. | 157 | |
158 | @classmethod | ||
159 | def _compare_name(cls, strparts, node): | ||
160 | """Given a sequence of strings representing a python name, | ||
161 | where the last component is the actual Name and the prior | ||
162 | elements are Attribute nodes, determine if the supplied node | ||
163 | matches. | ||
158 | """ | 164 | """ |
159 | 165 | ||
160 | getvars = ("d.getVar", "bb.data.getVar", "data.getVar") | 166 | if not strparts: |
161 | expands = ("d.expand", "bb.data.expand", "data.expand") | 167 | return True |
162 | execs = ("bb.build.exec_func", "bb.build.exec_task") | ||
163 | 168 | ||
164 | @classmethod | 169 | current, rest = strparts[0], strparts[1:] |
165 | def _compare_name(cls, strparts, node): | 170 | if isinstance(node, ast.Attribute): |
166 | """Given a sequence of strings representing a python name, | 171 | if current == node.attr: |
167 | where the last component is the actual Name and the prior | 172 | return cls._compare_name(rest, node.value) |
168 | elements are Attribute nodes, determine if the supplied node | 173 | elif isinstance(node, ast.Name): |
169 | matches. | 174 | if current == node.id: |
170 | """ | ||
171 | |||
172 | if not strparts: | ||
173 | return True | 175 | return True |
176 | return False | ||
177 | |||
178 | @classmethod | ||
179 | def compare_name(cls, value, node): | ||
180 | """Convenience function for the _compare_node method, which | ||
181 | can accept a string (which is split by '.' for you), or an | ||
182 | iterable of strings, in which case it checks to see if any of | ||
183 | them match, similar to isinstance. | ||
184 | """ | ||
174 | 185 | ||
175 | current, rest = strparts[0], strparts[1:] | 186 | if isinstance(value, basestring): |
176 | if isinstance(node, ast.Attribute): | 187 | return cls._compare_name(tuple(reversed(value.split("."))), |
177 | if current == node.attr: | 188 | node) |
178 | return cls._compare_name(rest, node.value) | 189 | else: |
179 | elif isinstance(node, ast.Name): | 190 | return any(cls.compare_name(item, node) for item in value) |
180 | if current == node.id: | ||
181 | return True | ||
182 | return False | ||
183 | |||
184 | @classmethod | ||
185 | def compare_name(cls, value, node): | ||
186 | """Convenience function for the _compare_node method, which | ||
187 | can accept a string (which is split by '.' for you), or an | ||
188 | iterable of strings, in which case it checks to see if any of | ||
189 | them match, similar to isinstance. | ||
190 | """ | ||
191 | |||
192 | if isinstance(value, basestring): | ||
193 | return cls._compare_name(tuple(reversed(value.split("."))), | ||
194 | node) | ||
195 | else: | ||
196 | return any(cls.compare_name(item, node) for item in value) | ||
197 | |||
198 | def __init__(self, value): | ||
199 | self.var_references = set() | ||
200 | self.var_execs = set() | ||
201 | self.direct_func_calls = set() | ||
202 | self.var_expands = set() | ||
203 | self.value = value | ||
204 | |||
205 | @classmethod | ||
206 | def warn(cls, func, arg): | ||
207 | """Warn about calls of bitbake APIs which pass a non-literal | ||
208 | argument for the variable name, as we're not able to track such | ||
209 | a reference. | ||
210 | """ | ||
211 | 191 | ||
212 | try: | 192 | @classmethod |
213 | funcstr = codegen.to_source(func) | 193 | def warn(cls, func, arg): |
214 | argstr = codegen.to_source(arg) | 194 | """Warn about calls of bitbake APIs which pass a non-literal |
215 | except TypeError: | 195 | argument for the variable name, as we're not able to track such |
216 | logger.debug(2, 'Failed to convert function and argument to source form') | 196 | a reference. |
217 | else: | 197 | """ |
218 | logger.debug(1, "Warning: in call to '%s', argument '%s' is " | ||
219 | "not a literal", funcstr, argstr) | ||
220 | 198 | ||
221 | def visit_Call(self, node): | 199 | try: |
222 | if self.compare_name(self.getvars, node.func): | 200 | funcstr = codegen.to_source(func) |
223 | if isinstance(node.args[0], ast.Str): | 201 | argstr = codegen.to_source(arg) |
224 | self.var_references.add(node.args[0].s) | 202 | except TypeError: |
225 | else: | 203 | logger.debug(2, 'Failed to convert function and argument to source form') |
226 | self.warn(node.func, node.args[0]) | 204 | else: |
227 | elif self.compare_name(self.expands, node.func): | 205 | logger.debug(1, "Warning: in call to '%s', argument '%s' is " |
228 | if isinstance(node.args[0], ast.Str): | 206 | "not a literal", funcstr, argstr) |
229 | self.warn(node.func, node.args[0]) | 207 | |
230 | self.var_expands.add(node.args[0].s) | 208 | def visit_Call(self, node): |
231 | elif isinstance(node.args[0], ast.Call) and \ | 209 | if self.compare_name(self.getvars, node.func): |
232 | self.compare_name(self.getvars, node.args[0].func): | 210 | if isinstance(node.args[0], ast.Str): |
233 | pass | 211 | self.var_references.add(node.args[0].s) |
234 | else: | 212 | else: |
235 | self.warn(node.func, node.args[0]) | 213 | self.warn(node.func, node.args[0]) |
236 | elif self.compare_name(self.execs, node.func): | 214 | elif self.compare_name(self.expands, node.func): |
237 | if isinstance(node.args[0], ast.Str): | 215 | if isinstance(node.args[0], ast.Str): |
238 | self.var_execs.add(node.args[0].s) | 216 | self.warn(node.func, node.args[0]) |
239 | else: | 217 | self.var_expands.add(node.args[0].s) |
240 | self.warn(node.func, node.args[0]) | 218 | elif isinstance(node.args[0], ast.Call) and \ |
241 | elif isinstance(node.func, ast.Name): | 219 | self.compare_name(self.getvars, node.args[0].func): |
242 | self.direct_func_calls.add(node.func.id) | 220 | pass |
243 | elif isinstance(node.func, ast.Attribute): | 221 | else: |
244 | # We must have a qualified name. Therefore we need | 222 | self.warn(node.func, node.args[0]) |
245 | # to walk the chain of 'Attribute' nodes to determine | 223 | elif self.compare_name(self.execfuncs, node.func): |
246 | # the qualification. | 224 | if isinstance(node.args[0], ast.Str): |
247 | attr_node = node.func.value | 225 | self.var_execs.add(node.args[0].s) |
248 | identifier = node.func.attr | 226 | else: |
249 | while isinstance(attr_node, ast.Attribute): | 227 | self.warn(node.func, node.args[0]) |
250 | identifier = attr_node.attr + "." + identifier | 228 | elif isinstance(node.func, ast.Name): |
251 | attr_node = attr_node.value | 229 | self.execs.add(node.func.id) |
252 | if isinstance(attr_node, ast.Name): | 230 | elif isinstance(node.func, ast.Attribute): |
253 | identifier = attr_node.id + "." + identifier | 231 | # We must have a qualified name. Therefore we need |
254 | self.direct_func_calls.add(identifier) | 232 | # to walk the chain of 'Attribute' nodes to determine |
233 | # the qualification. | ||
234 | attr_node = node.func.value | ||
235 | identifier = node.func.attr | ||
236 | while isinstance(attr_node, ast.Attribute): | ||
237 | identifier = attr_node.attr + "." + identifier | ||
238 | attr_node = attr_node.value | ||
239 | if isinstance(attr_node, ast.Name): | ||
240 | identifier = attr_node.id + "." + identifier | ||
241 | self.execs.add(identifier) | ||
255 | 242 | ||
256 | def __init__(self): | 243 | def __init__(self): |
257 | #self.funcdefs = set() | 244 | self.var_references = set() |
245 | self.var_execs = set() | ||
258 | self.execs = set() | 246 | self.execs = set() |
259 | #self.external_cmds = set() | 247 | self.var_expands = set() |
260 | self.references = set() | 248 | self.references = set() |
261 | 249 | ||
262 | def parse_python(self, node): | 250 | def parse_python(self, node): |
263 | |||
264 | h = hash(str(node)) | 251 | h = hash(str(node)) |
265 | 252 | ||
266 | if h in pythonparsecache: | 253 | if h in pythonparsecache: |
@@ -271,14 +258,12 @@ class PythonParser(): | |||
271 | code = compile(check_indent(str(node)), "<string>", "exec", | 258 | code = compile(check_indent(str(node)), "<string>", "exec", |
272 | ast.PyCF_ONLY_AST) | 259 | ast.PyCF_ONLY_AST) |
273 | 260 | ||
274 | visitor = self.ValueVisitor(code) | ||
275 | for n in ast.walk(code): | 261 | for n in ast.walk(code): |
276 | if n.__class__.__name__ == "Call": | 262 | if n.__class__.__name__ == "Call": |
277 | visitor.visit_Call(n) | 263 | self.visit_Call(n) |
278 | 264 | ||
279 | self.references.update(visitor.var_references) | 265 | self.references.update(self.var_references) |
280 | self.references.update(visitor.var_execs) | 266 | self.references.update(self.var_execs) |
281 | self.execs = visitor.direct_func_calls | ||
282 | 267 | ||
283 | pythonparsecache[h] = {} | 268 | pythonparsecache[h] = {} |
284 | pythonparsecache[h]["refs"] = self.references | 269 | pythonparsecache[h]["refs"] = self.references |