blob: c16d4185590a15f8b5dfff8bfd842b8e5f6f1a22 [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
15import sys
Mike Frysinger72ab8522019-10-01 00:18:46 -040016
Shawn O. Pearceb812a362009-04-10 20:37:47 -070017from color import Coloring
18from command import PagedCommand
Mike Frysinger72ab8522019-10-01 00:18:46 -040019from error import GitError
Mike Frysinger6f1c6262020-02-04 00:09:23 -050020from git_command import GitCommand
Shawn O. Pearceb812a362009-04-10 20:37:47 -070021
David Pursehouse819827a2020-02-12 15:20:19 +090022
Shawn O. Pearceb812a362009-04-10 20:37:47 -070023class GrepColoring(Coloring):
24 def __init__(self, config):
25 Coloring.__init__(self, config, 'grep')
26 self.project = self.printer('project', attr='bold')
Mike Frysinger72ab8522019-10-01 00:18:46 -040027 self.fail = self.printer('fail', fg='red')
Shawn O. Pearceb812a362009-04-10 20:37:47 -070028
David Pursehouse819827a2020-02-12 15:20:19 +090029
Shawn O. Pearceb812a362009-04-10 20:37:47 -070030class Grep(PagedCommand):
31 common = True
32 helpSummary = "Print lines matching a pattern"
33 helpUsage = """
34%prog {pattern | -e pattern} [<project>...]
35"""
36 helpDescription = """
37Search for the specified patterns in all project files.
38
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040039# Boolean Options
Shawn O. Pearceb812a362009-04-10 20:37:47 -070040
41The following options can appear as often as necessary to express
42the pattern to locate:
43
44 -e PATTERN
45 --and, --or, --not, -(, -)
46
47Further, the -r/--revision option may be specified multiple times
48in order to scan multiple trees. If the same file matches in more
49than one tree, only the first result is reported, prefixed by the
50revision name it was found under.
51
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040052# Examples
Shawn O. Pearceb812a362009-04-10 20:37:47 -070053
54Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX':
55
David Pursehouse1d947b32012-10-25 12:23:11 +090056 repo grep -e '#define' --and -\\( -e MAX_PATH -e PATH_MAX \\)
Shawn O. Pearceb812a362009-04-10 20:37:47 -070057
58Look for a line that has 'NODE' or 'Unexpected' in files that
59contain a line that matches both expressions:
60
61 repo grep --all-match -e NODE -e Unexpected
62
63"""
64
Mike Frysinger45ad1542021-02-24 12:47:01 -050065 @staticmethod
66 def _carry_option(_option, opt_str, value, parser):
67 pt = getattr(parser.values, 'cmd_argv', None)
68 if pt is None:
69 pt = []
70 setattr(parser.values, 'cmd_argv', pt)
71
72 if opt_str == '-(':
73 pt.append('(')
74 elif opt_str == '-)':
75 pt.append(')')
76 else:
77 pt.append(opt_str)
78
79 if value is not None:
80 pt.append(value)
81
Shawn O. Pearceb812a362009-04-10 20:37:47 -070082 def _Options(self, p):
Shawn O. Pearceb812a362009-04-10 20:37:47 -070083 g = p.add_option_group('Sources')
84 g.add_option('--cached',
Mike Frysinger45ad1542021-02-24 12:47:01 -050085 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -070086 help='Search the index, instead of the work tree')
David Pursehouse8f62fb72012-11-14 12:09:38 +090087 g.add_option('-r', '--revision',
Shawn O. Pearceb812a362009-04-10 20:37:47 -070088 dest='revision', action='append', metavar='TREEish',
89 help='Search TREEish, instead of the work tree')
90
91 g = p.add_option_group('Pattern')
92 g.add_option('-e',
Mike Frysinger45ad1542021-02-24 12:47:01 -050093 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -070094 metavar='PATTERN', type='str',
95 help='Pattern to search for')
96 g.add_option('-i', '--ignore-case',
Mike Frysinger45ad1542021-02-24 12:47:01 -050097 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -070098 help='Ignore case differences')
David Pursehouse8f62fb72012-11-14 12:09:38 +090099 g.add_option('-a', '--text',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500100 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700101 help="Process binary files as if they were text")
102 g.add_option('-I',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500103 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700104 help="Don't match the pattern in binary files")
105 g.add_option('-w', '--word-regexp',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500106 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700107 help='Match the pattern only at word boundaries')
108 g.add_option('-v', '--invert-match',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500109 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700110 help='Select non-matching lines')
111 g.add_option('-G', '--basic-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='Use POSIX basic regexp for patterns (default)')
114 g.add_option('-E', '--extended-regexp',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500115 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700116 help='Use POSIX extended regexp for patterns')
117 g.add_option('-F', '--fixed-strings',
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 fixed strings (not regexp) for pattern')
120
121 g = p.add_option_group('Pattern Grouping')
122 g.add_option('--all-match',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500123 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700124 help='Limit match to lines that have all patterns')
125 g.add_option('--and', '--or', '--not',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500126 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700127 help='Boolean operators to combine patterns')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900128 g.add_option('-(', '-)',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500129 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700130 help='Boolean operator grouping')
131
132 g = p.add_option_group('Output')
133 g.add_option('-n',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500134 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700135 help='Prefix the line number to matching lines')
136 g.add_option('-C',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500137 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700138 metavar='CONTEXT', type='str',
139 help='Show CONTEXT lines around match')
140 g.add_option('-B',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500141 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700142 metavar='CONTEXT', type='str',
143 help='Show CONTEXT lines before match')
144 g.add_option('-A',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500145 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700146 metavar='CONTEXT', type='str',
147 help='Show CONTEXT lines after match')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900148 g.add_option('-l', '--name-only', '--files-with-matches',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500149 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700150 help='Show only file names containing matching lines')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900151 g.add_option('-L', '--files-without-match',
Mike Frysinger45ad1542021-02-24 12:47:01 -0500152 action='callback', callback=self._carry_option,
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700153 help='Show only file names not containing matching lines')
154
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700155 def Execute(self, opt, args):
156 out = GrepColoring(self.manifest.manifestProject.config)
157
158 cmd_argv = ['grep']
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500159 if out.is_on:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700160 cmd_argv.append('--color')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900161 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700162
163 if '-e' not in cmd_argv:
164 if not args:
165 self.Usage()
166 cmd_argv.append('-e')
167 cmd_argv.append(args[0])
168 args = args[1:]
169
170 projects = self.GetProjects(args)
171
172 full_name = False
173 if len(projects) > 1:
174 cmd_argv.append('--full-name')
175 full_name = True
176
177 have_rev = False
178 if opt.revision:
179 if '--cached' in cmd_argv:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700180 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700181 sys.exit(1)
182 have_rev = True
183 cmd_argv.extend(opt.revision)
184 cmd_argv.append('--')
185
Mike Frysinger72ab8522019-10-01 00:18:46 -0400186 git_failed = False
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700187 bad_rev = False
188 have_match = False
189
190 for project in projects:
Mike Frysinger72ab8522019-10-01 00:18:46 -0400191 try:
192 p = GitCommand(project,
193 cmd_argv,
194 bare=False,
195 capture_stdout=True,
196 capture_stderr=True)
197 except GitError as e:
198 git_failed = True
199 out.project('--- project %s ---' % project.relpath)
200 out.nl()
201 out.fail('%s', str(e))
202 out.nl()
203 continue
204
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700205 if p.Wait() != 0:
206 # no results
207 #
208 if p.stderr:
209 if have_rev and 'fatal: ambiguous argument' in p.stderr:
210 bad_rev = True
211 else:
212 out.project('--- project %s ---' % project.relpath)
213 out.nl()
Mike Frysinger72ab8522019-10-01 00:18:46 -0400214 out.fail('%s', p.stderr.strip())
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700215 out.nl()
216 continue
217 have_match = True
218
219 # We cut the last element, to avoid a blank line.
220 #
221 r = p.stdout.split('\n')
222 r = r[0:-1]
223
224 if have_rev and full_name:
225 for line in r:
226 rev, line = line.split(':', 1)
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200227 out.write("%s", rev)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700228 out.write(':')
229 out.project(project.relpath)
230 out.write('/')
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200231 out.write("%s", line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700232 out.nl()
233 elif full_name:
234 for line in r:
235 out.project(project.relpath)
236 out.write('/')
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200237 out.write("%s", line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700238 out.nl()
239 else:
240 for line in r:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700241 print(line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700242
Mike Frysinger72ab8522019-10-01 00:18:46 -0400243 if git_failed:
244 sys.exit(1)
245 elif have_match:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700246 sys.exit(0)
247 elif have_rev and bad_rev:
248 for r in opt.revision:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700249 print("error: can't search revision %s" % r, file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700250 sys.exit(1)
251 else:
252 sys.exit(1)