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.py189
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
153class PythonParser(): 153class 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