summaryrefslogtreecommitdiffstats
path: root/subcmds/grep.py
diff options
context:
space:
mode:
Diffstat (limited to 'subcmds/grep.py')
-rw-r--r--subcmds/grep.py202
1 files changed, 110 insertions, 92 deletions
diff --git a/subcmds/grep.py b/subcmds/grep.py
index 4dd85d57..8ac4ba14 100644
--- a/subcmds/grep.py
+++ b/subcmds/grep.py
@@ -1,5 +1,3 @@
1# -*- coding:utf-8 -*-
2#
3# Copyright (C) 2009 The Android Open Source Project 1# Copyright (C) 2009 The Android Open Source Project
4# 2#
5# Licensed under the Apache License, Version 2.0 (the "License"); 3# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,14 +12,14 @@
14# See the License for the specific language governing permissions and 12# See the License for the specific language governing permissions and
15# limitations under the License. 13# limitations under the License.
16 14
17from __future__ import print_function 15import functools
18
19import sys 16import sys
20 17
21from color import Coloring 18from color import Coloring
22from command import PagedCommand 19from command import DEFAULT_LOCAL_JOBS, PagedCommand
23from error import GitError 20from error import GitError
24from git_command import git_require, GitCommand 21from git_command import GitCommand
22
25 23
26class GrepColoring(Coloring): 24class GrepColoring(Coloring):
27 def __init__(self, config): 25 def __init__(self, config):
@@ -29,8 +27,9 @@ class GrepColoring(Coloring):
29 self.project = self.printer('project', attr='bold') 27 self.project = self.printer('project', attr='bold')
30 self.fail = self.printer('fail', fg='red') 28 self.fail = self.printer('fail', fg='red')
31 29
30
32class Grep(PagedCommand): 31class Grep(PagedCommand):
33 common = True 32 COMMON = True
34 helpSummary = "Print lines matching a pattern" 33 helpSummary = "Print lines matching a pattern"
35 helpUsage = """ 34 helpUsage = """
36%prog {pattern | -e pattern} [<project>...] 35%prog {pattern | -e pattern} [<project>...]
@@ -63,30 +62,33 @@ contain a line that matches both expressions:
63 repo grep --all-match -e NODE -e Unexpected 62 repo grep --all-match -e NODE -e Unexpected
64 63
65""" 64"""
65 PARALLEL_JOBS = DEFAULT_LOCAL_JOBS
66
67 @staticmethod
68 def _carry_option(_option, opt_str, value, parser):
69 pt = getattr(parser.values, 'cmd_argv', None)
70 if pt is None:
71 pt = []
72 setattr(parser.values, 'cmd_argv', pt)
73
74 if opt_str == '-(':
75 pt.append('(')
76 elif opt_str == '-)':
77 pt.append(')')
78 else:
79 pt.append(opt_str)
66 80
67 def _Options(self, p): 81 if value is not None:
68 def carry(option, 82 pt.append(value)
69 opt_str,
70 value,
71 parser):
72 pt = getattr(parser.values, 'cmd_argv', None)
73 if pt is None:
74 pt = []
75 setattr(parser.values, 'cmd_argv', pt)
76
77 if opt_str == '-(':
78 pt.append('(')
79 elif opt_str == '-)':
80 pt.append(')')
81 else:
82 pt.append(opt_str)
83 83
84 if value is not None: 84 def _CommonOptions(self, p):
85 pt.append(value) 85 """Override common options slightly."""
86 super()._CommonOptions(p, opt_v=False)
86 87
88 def _Options(self, p):
87 g = p.add_option_group('Sources') 89 g = p.add_option_group('Sources')
88 g.add_option('--cached', 90 g.add_option('--cached',
89 action='callback', callback=carry, 91 action='callback', callback=self._carry_option,
90 help='Search the index, instead of the work tree') 92 help='Search the index, instead of the work tree')
91 g.add_option('-r', '--revision', 93 g.add_option('-r', '--revision',
92 dest='revision', action='append', metavar='TREEish', 94 dest='revision', action='append', metavar='TREEish',
@@ -94,136 +96,111 @@ contain a line that matches both expressions:
94 96
95 g = p.add_option_group('Pattern') 97 g = p.add_option_group('Pattern')
96 g.add_option('-e', 98 g.add_option('-e',
97 action='callback', callback=carry, 99 action='callback', callback=self._carry_option,
98 metavar='PATTERN', type='str', 100 metavar='PATTERN', type='str',
99 help='Pattern to search for') 101 help='Pattern to search for')
100 g.add_option('-i', '--ignore-case', 102 g.add_option('-i', '--ignore-case',
101 action='callback', callback=carry, 103 action='callback', callback=self._carry_option,
102 help='Ignore case differences') 104 help='Ignore case differences')
103 g.add_option('-a', '--text', 105 g.add_option('-a', '--text',
104 action='callback', callback=carry, 106 action='callback', callback=self._carry_option,
105 help="Process binary files as if they were text") 107 help="Process binary files as if they were text")
106 g.add_option('-I', 108 g.add_option('-I',
107 action='callback', callback=carry, 109 action='callback', callback=self._carry_option,
108 help="Don't match the pattern in binary files") 110 help="Don't match the pattern in binary files")
109 g.add_option('-w', '--word-regexp', 111 g.add_option('-w', '--word-regexp',
110 action='callback', callback=carry, 112 action='callback', callback=self._carry_option,
111 help='Match the pattern only at word boundaries') 113 help='Match the pattern only at word boundaries')
112 g.add_option('-v', '--invert-match', 114 g.add_option('-v', '--invert-match',
113 action='callback', callback=carry, 115 action='callback', callback=self._carry_option,
114 help='Select non-matching lines') 116 help='Select non-matching lines')
115 g.add_option('-G', '--basic-regexp', 117 g.add_option('-G', '--basic-regexp',
116 action='callback', callback=carry, 118 action='callback', callback=self._carry_option,
117 help='Use POSIX basic regexp for patterns (default)') 119 help='Use POSIX basic regexp for patterns (default)')
118 g.add_option('-E', '--extended-regexp', 120 g.add_option('-E', '--extended-regexp',
119 action='callback', callback=carry, 121 action='callback', callback=self._carry_option,
120 help='Use POSIX extended regexp for patterns') 122 help='Use POSIX extended regexp for patterns')
121 g.add_option('-F', '--fixed-strings', 123 g.add_option('-F', '--fixed-strings',
122 action='callback', callback=carry, 124 action='callback', callback=self._carry_option,
123 help='Use fixed strings (not regexp) for pattern') 125 help='Use fixed strings (not regexp) for pattern')
124 126
125 g = p.add_option_group('Pattern Grouping') 127 g = p.add_option_group('Pattern Grouping')
126 g.add_option('--all-match', 128 g.add_option('--all-match',
127 action='callback', callback=carry, 129 action='callback', callback=self._carry_option,
128 help='Limit match to lines that have all patterns') 130 help='Limit match to lines that have all patterns')
129 g.add_option('--and', '--or', '--not', 131 g.add_option('--and', '--or', '--not',
130 action='callback', callback=carry, 132 action='callback', callback=self._carry_option,
131 help='Boolean operators to combine patterns') 133 help='Boolean operators to combine patterns')
132 g.add_option('-(', '-)', 134 g.add_option('-(', '-)',
133 action='callback', callback=carry, 135 action='callback', callback=self._carry_option,
134 help='Boolean operator grouping') 136 help='Boolean operator grouping')
135 137
136 g = p.add_option_group('Output') 138 g = p.add_option_group('Output')
137 g.add_option('-n', 139 g.add_option('-n',
138 action='callback', callback=carry, 140 action='callback', callback=self._carry_option,
139 help='Prefix the line number to matching lines') 141 help='Prefix the line number to matching lines')
140 g.add_option('-C', 142 g.add_option('-C',
141 action='callback', callback=carry, 143 action='callback', callback=self._carry_option,
142 metavar='CONTEXT', type='str', 144 metavar='CONTEXT', type='str',
143 help='Show CONTEXT lines around match') 145 help='Show CONTEXT lines around match')
144 g.add_option('-B', 146 g.add_option('-B',
145 action='callback', callback=carry, 147 action='callback', callback=self._carry_option,
146 metavar='CONTEXT', type='str', 148 metavar='CONTEXT', type='str',
147 help='Show CONTEXT lines before match') 149 help='Show CONTEXT lines before match')
148 g.add_option('-A', 150 g.add_option('-A',
149 action='callback', callback=carry, 151 action='callback', callback=self._carry_option,
150 metavar='CONTEXT', type='str', 152 metavar='CONTEXT', type='str',
151 help='Show CONTEXT lines after match') 153 help='Show CONTEXT lines after match')
152 g.add_option('-l', '--name-only', '--files-with-matches', 154 g.add_option('-l', '--name-only', '--files-with-matches',
153 action='callback', callback=carry, 155 action='callback', callback=self._carry_option,
154 help='Show only file names containing matching lines') 156 help='Show only file names containing matching lines')
155 g.add_option('-L', '--files-without-match', 157 g.add_option('-L', '--files-without-match',
156 action='callback', callback=carry, 158 action='callback', callback=self._carry_option,
157 help='Show only file names not containing matching lines') 159 help='Show only file names not containing matching lines')
158 160
159 161 def _ExecuteOne(self, cmd_argv, project):
160 def Execute(self, opt, args): 162 """Process one project."""
161 out = GrepColoring(self.manifest.manifestProject.config) 163 try:
162 164 p = GitCommand(project,
163 cmd_argv = ['grep'] 165 cmd_argv,
164 if out.is_on and git_require((1, 6, 3)): 166 bare=False,
165 cmd_argv.append('--color') 167 capture_stdout=True,
166 cmd_argv.extend(getattr(opt, 'cmd_argv', [])) 168 capture_stderr=True)
167 169 except GitError as e:
168 if '-e' not in cmd_argv: 170 return (project, -1, None, str(e))
169 if not args: 171
170 self.Usage() 172 return (project, p.Wait(), p.stdout, p.stderr)
171 cmd_argv.append('-e') 173
172 cmd_argv.append(args[0]) 174 @staticmethod
173 args = args[1:] 175 def _ProcessResults(full_name, have_rev, _pool, out, results):
174
175 projects = self.GetProjects(args)
176
177 full_name = False
178 if len(projects) > 1:
179 cmd_argv.append('--full-name')
180 full_name = True
181
182 have_rev = False
183 if opt.revision:
184 if '--cached' in cmd_argv:
185 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
186 sys.exit(1)
187 have_rev = True
188 cmd_argv.extend(opt.revision)
189 cmd_argv.append('--')
190
191 git_failed = False 176 git_failed = False
192 bad_rev = False 177 bad_rev = False
193 have_match = False 178 have_match = False
194 179
195 for project in projects: 180 for project, rc, stdout, stderr in results:
196 try: 181 if rc < 0:
197 p = GitCommand(project,
198 cmd_argv,
199 bare=False,
200 capture_stdout=True,
201 capture_stderr=True)
202 except GitError as e:
203 git_failed = True 182 git_failed = True
204 out.project('--- project %s ---' % project.relpath) 183 out.project('--- project %s ---' % project.relpath)
205 out.nl() 184 out.nl()
206 out.fail('%s', str(e)) 185 out.fail('%s', stderr)
207 out.nl() 186 out.nl()
208 continue 187 continue
209 188
210 if p.Wait() != 0: 189 if rc:
211 # no results 190 # no results
212 # 191 if stderr:
213 if p.stderr: 192 if have_rev and 'fatal: ambiguous argument' in stderr:
214 if have_rev and 'fatal: ambiguous argument' in p.stderr:
215 bad_rev = True 193 bad_rev = True
216 else: 194 else:
217 out.project('--- project %s ---' % project.relpath) 195 out.project('--- project %s ---' % project.relpath)
218 out.nl() 196 out.nl()
219 out.fail('%s', p.stderr.strip()) 197 out.fail('%s', stderr.strip())
220 out.nl() 198 out.nl()
221 continue 199 continue
222 have_match = True 200 have_match = True
223 201
224 # We cut the last element, to avoid a blank line. 202 # We cut the last element, to avoid a blank line.
225 # 203 r = stdout.split('\n')
226 r = p.stdout.split('\n')
227 r = r[0:-1] 204 r = r[0:-1]
228 205
229 if have_rev and full_name: 206 if have_rev and full_name:
@@ -245,6 +222,47 @@ contain a line that matches both expressions:
245 for line in r: 222 for line in r:
246 print(line) 223 print(line)
247 224
225 return (git_failed, bad_rev, have_match)
226
227 def Execute(self, opt, args):
228 out = GrepColoring(self.manifest.manifestProject.config)
229
230 cmd_argv = ['grep']
231 if out.is_on:
232 cmd_argv.append('--color')
233 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
234
235 if '-e' not in cmd_argv:
236 if not args:
237 self.Usage()
238 cmd_argv.append('-e')
239 cmd_argv.append(args[0])
240 args = args[1:]
241
242 projects = self.GetProjects(args)
243
244 full_name = False
245 if len(projects) > 1:
246 cmd_argv.append('--full-name')
247 full_name = True
248
249 have_rev = False
250 if opt.revision:
251 if '--cached' in cmd_argv:
252 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
253 sys.exit(1)
254 have_rev = True
255 cmd_argv.extend(opt.revision)
256 cmd_argv.append('--')
257
258 git_failed, bad_rev, have_match = self.ExecuteInParallel(
259 opt.jobs,
260 functools.partial(self._ExecuteOne, cmd_argv),
261 projects,
262 callback=functools.partial(self._ProcessResults, full_name, have_rev),
263 output=out,
264 ordered=True)
265
248 if git_failed: 266 if git_failed:
249 sys.exit(1) 267 sys.exit(1)
250 elif have_match: 268 elif have_match: