blob: 93c9ae5111fefe9eb1abe5dc7dd57b7041757538 [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):
Mike Frysinger4f210542021-06-14 16:05:19 -040032 COMMON = True
Shawn O. Pearceb812a362009-04-10 20:37:47 -070033 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
LaMont Jonescc879a92021-11-18 22:40:18 +0000175 def _ProcessResults(full_name, have_rev, opt, _pool, out, results):
Mike Frysingerd246d1f2021-02-24 12:50:30 -0500176 git_failed = False
177 bad_rev = False
178 have_match = False
LaMont Jonescc879a92021-11-18 22:40:18 +0000179 _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only)
Mike Frysingerd246d1f2021-02-24 12:50:30 -0500180
181 for project, rc, stdout, stderr in results:
182 if rc < 0:
183 git_failed = True
LaMont Jonescc879a92021-11-18 22:40:18 +0000184 out.project('--- project %s ---' % _RelPath(project))
Mike Frysingerd246d1f2021-02-24 12:50:30 -0500185 out.nl()
186 out.fail('%s', stderr)
187 out.nl()
188 continue
189
190 if rc:
191 # no results
192 if stderr:
193 if have_rev and 'fatal: ambiguous argument' in stderr:
194 bad_rev = True
195 else:
LaMont Jonescc879a92021-11-18 22:40:18 +0000196 out.project('--- project %s ---' % _RelPath(project))
Mike Frysingerd246d1f2021-02-24 12:50:30 -0500197 out.nl()
198 out.fail('%s', stderr.strip())
199 out.nl()
200 continue
201 have_match = True
202
203 # We cut the last element, to avoid a blank line.
204 r = stdout.split('\n')
205 r = r[0:-1]
206
207 if have_rev and full_name:
208 for line in r:
209 rev, line = line.split(':', 1)
210 out.write("%s", rev)
211 out.write(':')
LaMont Jonescc879a92021-11-18 22:40:18 +0000212 out.project(_RelPath(project))
Mike Frysingerd246d1f2021-02-24 12:50:30 -0500213 out.write('/')
214 out.write("%s", line)
215 out.nl()
216 elif full_name:
217 for line in r:
LaMont Jonescc879a92021-11-18 22:40:18 +0000218 out.project(_RelPath(project))
Mike Frysingerd246d1f2021-02-24 12:50:30 -0500219 out.write('/')
220 out.write("%s", line)
221 out.nl()
222 else:
223 for line in r:
224 print(line)
225
226 return (git_failed, bad_rev, have_match)
227
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700228 def Execute(self, opt, args):
229 out = GrepColoring(self.manifest.manifestProject.config)
230
231 cmd_argv = ['grep']
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500232 if out.is_on:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700233 cmd_argv.append('--color')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900234 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700235
236 if '-e' not in cmd_argv:
237 if not args:
238 self.Usage()
239 cmd_argv.append('-e')
240 cmd_argv.append(args[0])
241 args = args[1:]
242
LaMont Jonescc879a92021-11-18 22:40:18 +0000243 projects = self.GetProjects(args, all_manifests=not opt.this_manifest_only)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700244
245 full_name = False
246 if len(projects) > 1:
247 cmd_argv.append('--full-name')
248 full_name = True
249
250 have_rev = False
251 if opt.revision:
252 if '--cached' in cmd_argv:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700253 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700254 sys.exit(1)
255 have_rev = True
256 cmd_argv.extend(opt.revision)
257 cmd_argv.append('--')
258
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500259 git_failed, bad_rev, have_match = self.ExecuteInParallel(
260 opt.jobs,
261 functools.partial(self._ExecuteOne, cmd_argv),
262 projects,
LaMont Jonescc879a92021-11-18 22:40:18 +0000263 callback=functools.partial(self._ProcessResults, full_name, have_rev, opt),
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500264 output=out,
265 ordered=True)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700266
Mike Frysinger72ab8522019-10-01 00:18:46 -0400267 if git_failed:
268 sys.exit(1)
269 elif have_match:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700270 sys.exit(0)
271 elif have_rev and bad_rev:
272 for r in opt.revision:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700273 print("error: can't search revision %s" % r, file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700274 sys.exit(1)
275 else:
276 sys.exit(1)