OILS / opy / _regtest / src / tools / deps.py View on Github | oilshell.org

157 lines, 70 significant
1#!/usr/bin/python
2from __future__ import print_function
3"""
4deps.py
5"""
6
7import sys
8
9from asdl import asdl_ as asdl
10from asdl import py_meta
11from core import builtin
12from core import util
13from core import word
14from osh.meta import ast, runtime
15from osh import ast_lib
16
17command_e = ast.command_e
18builtin_e = runtime.builtin_e
19log = util.log
20
21# TODO: Move to asdl/visitor.py?
22class Visitor(object):
23 # Python does introspection on method names:
24 # method = 'visit_' + node.__class__.__name__
25 # I'm using ASDL metaprogramming instead.
26
27 def Visit(self, node):
28 raise NotImplementedError
29
30 # Like ast.NodeVisitor().generic_visit!
31 def VisitChildren(self, node):
32 """
33 Args:
34 node: an ASDL node.
35 """
36 #print 'CHILD', node.ASDL_TYPE
37
38 for name, _ in node.ASDL_TYPE.GetFields():
39 child = getattr(node, name)
40 #log('Considering child %s', name)
41
42 if isinstance(child, list):
43 #log('Visiting child array %s', name)
44 for item in child:
45 # We have to check for compound objects on an INSTANCE basis, not a
46 # type basis, because sums can look like this:
47 # iterable = IterArgv | IterArray(word* words)
48 # We visit the latter but not the former.
49 if isinstance(item, py_meta.CompoundObj):
50 self.Visit(item)
51 continue
52
53 if isinstance(child, py_meta.CompoundObj):
54 #log('Visiting child %s', name)
55 self.Visit(child)
56 continue
57
58
59class DepsVisitor(Visitor):
60 """
61 Output:
62
63 type name resolved_name source_path line_num
64 bin cp /usr/bin/cp prog.sh 22
65 lib functions.sh /home/andy/src/functions prog.sh 22
66
67 TODO:
68 - Make this TSV2
69 - handle source and .
70 - flags like --path and --special exec
71 - need some knowledge of function scope.
72 f; f() { true; } -- f is an exeternal binary!
73 g() { f; }; f() { true; } -- f is a function!
74
75 """
76 def __init__(self, f):
77 Visitor.__init__(self)
78 self.funcs_defined = {}
79 self.progs_used = {}
80 self.f = f
81
82 def _Visit(self, node):
83 """
84 """
85 #log('VISIT %s', node.__class__.__name__)
86
87 # NOTE: The tags are not unique!!! We would need this:
88 # if isinstance(node, ast.command) and node.tag == command_e.SimpleCommand:
89 # But it's easier to check the __class__ attribute.
90
91 cls = node.__class__
92 if cls is ast.SimpleCommand:
93 #log('SimpleCommand %s', node.words)
94 #log('--')
95 #ast_lib.PrettyPrint(node)
96
97 # Things to consider:
98 # - source and .
99 # - DONE builtins: get a list from builtin.py
100 # - DONE functions: have to enter function definitions into a dictionary
101 # - Commands that call others: sudo, su, find, xargs, etc.
102 # - builtins that call others: exec, command
103 # - except not command -v!
104
105 if not node.words:
106 return
107
108 w = node.words[0]
109 ok, argv0, _ = word.StaticEval(w)
110 if not ok:
111 log("Couldn't statically evaluate %r", w)
112 return
113
114 if (builtin.ResolveSpecial(argv0) == builtin_e.NONE and
115 builtin.Resolve(argv0) == builtin_e.NONE):
116 self.progs_used[argv0] = True
117
118 # NOTE: If argv1 is $0, then we do NOT print a warning!
119 if argv0 == 'sudo':
120 if len(node.words) < 2:
121 return
122 w1 = node.words[1]
123 ok, argv1, _ = word.StaticEval(w1)
124 if not ok:
125 log("Couldn't statically evaluate %r", w)
126 return
127
128 # Should we mark them behind 'sudo'? e.g. "sudo apt install"?
129 self.progs_used[argv1] = True
130
131 elif cls is ast.FuncDef:
132 self.funcs_defined[node.name] = True
133
134 def Visit(self, node):
135 self._Visit(node)
136
137 # We always need to visit children, even for SimpleCommand, etc. There
138 # could be command sub, e.g. even in redirect. echo hi > $(cat out)
139 self.VisitChildren(node)
140
141 def Emit(self, row):
142 # TSV-like format
143 self.f.write('\t'.join(row))
144 self.f.write('\n')
145
146 def Done(self):
147 """Write a report."""
148 # TODO: Use self.Emit(), make it TSV.
149 for name in self.progs_used:
150 if name not in self.funcs_defined:
151 print(name)
152
153
154def Deps(node):
155 v = DepsVisitor(sys.stdout)
156 v.Visit(node)
157 v.Done()