blob: 6cb1445a30f4707aa454b2e7d96220e035df62d3 [file] [log] [blame]
Shawn O. Pearceb812a362009-04-10 20:37:47 -07001# Copyright (C) 2009 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Mike Frysingerd246d1f2021-02-24 12:50:30 -050015import functools
Shawn O. Pearceb812a362009-04-10 20:37:47 -070016import sys
Mike Frysinger72ab8522019-10-01 00:18:46 -040017
Shawn O. Pearceb812a362009-04-10 20:37:47 -070018from color import Coloring
Mike Frysingerb5d075d2021-03-01 00:56:38 -050019from command import DEFAULT_LOCAL_JOBS, PagedCommand
Mike Frysinger72ab8522019-10-01 00:18:46 -040020from error import GitError
Mike Frysinger6f1c6262020-02-04 00:09:23 -050021from git_command import GitCommand
Shawn O. Pearceb812a362009-04-10 20:37:47 -070022
David Pursehouse819827a2020-02-12 15:20:19 +090023
Shawn O. Pearceb812a362009-04-10 20:37:47 -070024class GrepColoring(Coloring):
25 def __init__(self, config):
26 Coloring.__init__(self, config, 'grep')
27 self.project = self.printer('project', attr='bold')
Mike Frysinger72ab8522019-10-01 00:18:46 -040028 self.fail = self.printer('fail', fg='red')
Shawn O. Pearceb812a362009-04-10 20:37:47 -070029
David Pursehouse819827a2020-02-12 15:20:19 +090030
Shawn O. Pearceb812a362009-04-10 20:37:47 -070031class Grep(PagedCommand):
32 common = True
33 helpSummary = "Print lines matching a pattern"
34 helpUsage = """
35%prog {pattern | -e pattern} [<project>...]
36"""
37 helpDescription = """
38Search for the specified patterns in all project files.
39
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040040# Boolean Options
Shawn O. Pearceb812a362009-04-10 20:37:47 -070041
42The following options can appear as often as necessary to express
43the pattern to locate:
44
45 -e PATTERN
46 --and, --or, --not, -(, -)
47
48Further, the -r/--revision option may be specified multiple times
49in order to scan multiple trees. If the same file matches in more
50than one tree, only the first result is reported, prefixed by the
51revision name it was found under.
52
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040053# Examples
Shawn O. Pearceb812a362009-04-10 20:37:47 -070054
55Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX':
56
David Pursehouse1d947b32012-10-25 12:23:11 +090057 repo grep -e '#define' --and -\\( -e MAX_PATH -e PATH_MAX \\)
Shawn O. Pearceb812a362009-04-10 20:37:47 -070058
59Look for a line that has 'NODE' or 'Unexpected' in files that
60contain a line that matches both expressions:
61
62 repo grep --all-match -e NODE -e Unexpected
63
64"""
Mike Frysingerd246d1f2021-02-24 12:50:30 -050065 PARALLEL_JOBS = DEFAULT_LOCAL_JOBS
Shawn O. Pearceb812a362009-04-10 20:37:47 -070066
Mike Frysinger45ad1542021-02-24 12:47:01 -050067 @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)
80
81 if value is not None:
82 pt.append(value)
83
Mike Frysinger9180a072021-04-13 14:57:40 -040084 def _CommonOptions(self, p):
85 """Override common options slightly."""
86 super()._CommonOptions(p, opt_v=False)
87
Shawn O. Pearceb812a362009-04-10 20:37:47 -070088 def _Options(self, p):
Shawn O. Pearceb812a362009-04-10 20:37:47 -070089 g = p.add_option_group('Sources')
90 g.add_option('--cached',
Mike Frysinger45ad1542021-02-24 12:47:01 -050091 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -070092 help='Search the index, instead of the work tree')
David Pursehouse8f62fb72012-11-14 12:09:38 +090093 g.add_option('-r', '--revision',
Shawn O. Pearceb812a362009-04-10 20:37:47 -070094 dest='revision', action='append', metavar='TREEish',
95 help='Search TREEish, instead of the work tree')
96
97 g = p.add_option_group('Pattern')
98 g.add_option('-e',
Mike Frysinger45ad1542021-02-24 12:47:01 -050099 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700100 metavar='PATTERN', type='str',
101 help='Pattern to search for')
102 g.add_option('-i', '--ignore-case',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500103 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700104 help='Ignore case differences')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900105 g.add_option('-a', '--text',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500106 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700107 help="Process binary files as if they were text")
108 g.add_option('-I',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500109 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700110 help="Don't match the pattern in binary files")
111 g.add_option('-w', '--word-regexp',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500112 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700113 help='Match the pattern only at word boundaries')
114 g.add_option('-v', '--invert-match',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500115 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700116 help='Select non-matching lines')
117 g.add_option('-G', '--basic-regexp',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500118 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700119 help='Use POSIX basic regexp for patterns (default)')
120 g.add_option('-E', '--extended-regexp',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500121 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700122 help='Use POSIX extended regexp for patterns')
123 g.add_option('-F', '--fixed-strings',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500124 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700125 help='Use fixed strings (not regexp) for pattern')
126
127 g = p.add_option_group('Pattern Grouping')
128 g.add_option('--all-match',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500129 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700130 help='Limit match to lines that have all patterns')
131 g.add_option('--and', '--or', '--not',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500132 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700133 help='Boolean operators to combine patterns')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900134 g.add_option('-(', '-)',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500135 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700136 help='Boolean operator grouping')
137
138 g = p.add_option_group('Output')
139 g.add_option('-n',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500140 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700141 help='Prefix the line number to matching lines')
142 g.add_option('-C',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500143 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700144 metavar='CONTEXT', type='str',
145 help='Show CONTEXT lines around match')
146 g.add_option('-B',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500147 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700148 metavar='CONTEXT', type='str',
149 help='Show CONTEXT lines before match')
150 g.add_option('-A',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500151 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700152 metavar='CONTEXT', type='str',
153 help='Show CONTEXT lines after match')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900154 g.add_option('-l', '--name-only', '--files-with-matches',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500155 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700156 help='Show only file names containing matching lines')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900157 g.add_option('-L', '--files-without-match',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500158 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700159 help='Show only file names not containing matching lines')
160
Mike Frysingerd246d1f2021-02-24 12:50:30 -0500161 def _ExecuteOne(self, cmd_argv, project):
162 """Process one project."""
163 try:
164 p = GitCommand(project,
165 cmd_argv,
166 bare=False,
167 capture_stdout=True,
168 capture_stderr=True)
169 except GitError as e:
170 return (project, -1, None, str(e))
171
172 return (project, p.Wait(), p.stdout, p.stderr)
173
174 @staticmethod
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500175 def _ProcessResults(full_name, have_rev, _pool, out, results):
Mike Frysingerd246d1f2021-02-24 12:50:30 -0500176 git_failed = False
177 bad_rev = False
178 have_match = False
179
180 for project, rc, stdout, stderr in results:
181 if rc < 0:
182 git_failed = True
183 out.project('--- project %s ---' % project.relpath)
184 out.nl()
185 out.fail('%s', stderr)
186 out.nl()
187 continue
188
189 if rc:
190 # no results
191 if stderr:
192 if have_rev and 'fatal: ambiguous argument' in stderr:
193 bad_rev = True
194 else:
195 out.project('--- project %s ---' % project.relpath)
196 out.nl()
197 out.fail('%s', stderr.strip())
198 out.nl()
199 continue
200 have_match = True
201
202 # We cut the last element, to avoid a blank line.
203 r = stdout.split('\n')
204 r = r[0:-1]
205
206 if have_rev and full_name:
207 for line in r:
208 rev, line = line.split(':', 1)
209 out.write("%s", rev)
210 out.write(':')
211 out.project(project.relpath)
212 out.write('/')
213 out.write("%s", line)
214 out.nl()
215 elif full_name:
216 for line in r:
217 out.project(project.relpath)
218 out.write('/')
219 out.write("%s", line)
220 out.nl()
221 else:
222 for line in r:
223 print(line)
224
225 return (git_failed, bad_rev, have_match)
226
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700227 def Execute(self, opt, args):
228 out = GrepColoring(self.manifest.manifestProject.config)
229
230 cmd_argv = ['grep']
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500231 if out.is_on:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700232 cmd_argv.append('--color')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900233 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700234
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:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700252 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700253 sys.exit(1)
254 have_rev = True
255 cmd_argv.extend(opt.revision)
256 cmd_argv.append('--')
257
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500258 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)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700265
Mike Frysinger72ab8522019-10-01 00:18:46 -0400266 if git_failed:
267 sys.exit(1)
268 elif have_match:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700269 sys.exit(0)
270 elif have_rev and bad_rev:
271 for r in opt.revision:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700272 print("error: can't search revision %s" % r, file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700273 sys.exit(1)
274 else:
275 sys.exit(1)