| @@ -0,0 +1,132 @@ | ||
| #!/usr/bin/python | ||
| """ | ||
| deps.py | ||
| """ | ||
| import sys | ||
| from asdl import asdl_ as asdl | ||
| from asdl import py_meta | ||
| from core import builtin | ||
| from core import util | ||
| from core import word | ||
| from osh.meta import ast, runtime | ||
| from osh import ast_lib | ||
| command_e = ast.command_e | ||
| builtin_e = runtime.builtin_e | ||
| log = util.log | ||
| # TODO: Move to asdl/visitor.py? | ||
| class Visitor(object): | ||
| # In Python, they do introspection on method names. | ||
| # method = 'visit_' + node.__class__.__name__ | ||
| # I'm not going to bother, because I have ASDL! I want the generic visitor. | ||
| def Visit(self, node): | ||
| raise NotImplementedError | ||
| # Like ast.NodeVisitor().generic_visit! | ||
| def VisitChildren(self, node): | ||
| """ | ||
| Args: | ||
| node: an ASDL node. | ||
| """ | ||
| #print 'CHILD', node.ASDL_TYPE | ||
| for name, _ in node.ASDL_TYPE.GetFields(): | ||
| child = getattr(node, name) | ||
| #log('Considering child %s', name) | ||
| if isinstance(child, list): | ||
| #log('Visiting child array %s', name) | ||
| for item in child: | ||
| # We have to do this on an INSTANCE basis, not a type basis, because | ||
| # sums can be like: | ||
| # iterable = IterArgv | IterArray(word* words) | ||
| # We visit the latter but not the foramer. | ||
| if isinstance(item, py_meta.CompoundObj): | ||
| self.Visit(item) | ||
| continue | ||
| if isinstance(child, py_meta.CompoundObj): | ||
| #log('Visiting child %s', name) | ||
| self.Visit(child) | ||
| continue | ||
| class DepsVisitor(Visitor): | ||
| """ | ||
| Output: | ||
| type name resolved_name source_path line_num | ||
| bin cp /usr/bin/cp prog.sh 22 | ||
| lib functions.sh /home/andy/src/functions prog.sh 22 | ||
| TODO: Make this TSV2 | ||
| """ | ||
| def __init__(self, f): | ||
| Visitor.__init__(self) | ||
| self.funcs_defined = {} | ||
| self.progs_used = {} | ||
| self.f = f | ||
| def Visit(self, node): | ||
| """ | ||
| """ | ||
| #log('VISIT %s', node.__class__.__name__) | ||
| # PROBLEM: The tags are not unique!!! Crap. This is picking up some other | ||
| # stuff. Need the isinstance() check. | ||
| if isinstance(node, ast.command) and node.tag == command_e.SimpleCommand: | ||
| #log('SimpleCommand %s', node.words) | ||
| #log('--') | ||
| #ast_lib.PrettyPrint(node) | ||
| # Things to consider: | ||
| # - source and . | ||
| # - builtins: get a list from builtin.py | ||
| # - functions: have to enter function definitions into a dictionary | ||
| # - Commands that call others: sudo, su, find, xargs, etc. | ||
| # TODO: We need two passes! test/wild.sh make-report, etc. | ||
| if node.words: | ||
| w = node.words[0] | ||
| ok, prog, _ = word.StaticEval(w) | ||
| if ok: | ||
| # TODO: Also consider builtins | ||
| if (prog not in self.funcs_defined and | ||
| builtin.ResolveSpecial(prog) == builtin_e.NONE and | ||
| builtin.Resolve(prog) == builtin_e.NONE): | ||
| self.progs_used[prog] = True | ||
| else: | ||
| log("Couldn't statically evaluate %r", w) | ||
| # There could be command sub, e.g. even in redirect: | ||
| self.VisitChildren(node) | ||
| elif isinstance(node, ast.command) and node.tag == command_e.FuncDef: | ||
| self.funcs_defined[node.name] = True | ||
| self.VisitChildren(node) | ||
| else: | ||
| self.VisitChildren(node) | ||
| def Emit(self, row): | ||
| # TSV-like format | ||
| self.f.write('\t'.join(row)) | ||
| self.f.write('\n') | ||
| def Done(self): | ||
| for name in self.progs_used: | ||
| print name | ||
| def Deps(node): | ||
| v = DepsVisitor(sys.stdout) | ||
| v.Visit(node) | ||
| v.Done() | ||
| #print(node) |