blob: e3628c6a2439f0a5804df5c942cef28863aa88ac [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
65 def _Options(self, p):
66 def carry(option,
67 opt_str,
68 value,
69 parser):
70 pt = getattr(parser.values, 'cmd_argv', None)
71 if pt is None:
72 pt = []
73 setattr(parser.values, 'cmd_argv', pt)
74
75 if opt_str == '-(':
76 pt.append('(')
77 elif opt_str == '-)':
78 pt.append(')')
79 else:
80 pt.append(opt_str)
81
82 if value is not None:
83 pt.append(value)
84
85 g = p.add_option_group('Sources')
86 g.add_option('--cached',
87 action='callback', callback=carry,
88 help='Search the index, instead of the work tree')
David Pursehouse8f62fb72012-11-14 12:09:38 +090089 g.add_option('-r', '--revision',
Shawn O. Pearceb812a362009-04-10 20:37:47 -070090 dest='revision', action='append', metavar='TREEish',
91 help='Search TREEish, instead of the work tree')
92
93 g = p.add_option_group('Pattern')
94 g.add_option('-e',
95 action='callback', callback=carry,
96 metavar='PATTERN', type='str',
97 help='Pattern to search for')
98 g.add_option('-i', '--ignore-case',
99 action='callback', callback=carry,
100 help='Ignore case differences')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900101 g.add_option('-a', '--text',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700102 action='callback', callback=carry,
103 help="Process binary files as if they were text")
104 g.add_option('-I',
105 action='callback', callback=carry,
106 help="Don't match the pattern in binary files")
107 g.add_option('-w', '--word-regexp',
108 action='callback', callback=carry,
109 help='Match the pattern only at word boundaries')
110 g.add_option('-v', '--invert-match',
111 action='callback', callback=carry,
112 help='Select non-matching lines')
113 g.add_option('-G', '--basic-regexp',
114 action='callback', callback=carry,
115 help='Use POSIX basic regexp for patterns (default)')
116 g.add_option('-E', '--extended-regexp',
117 action='callback', callback=carry,
118 help='Use POSIX extended regexp for patterns')
119 g.add_option('-F', '--fixed-strings',
120 action='callback', callback=carry,
121 help='Use fixed strings (not regexp) for pattern')
122
123 g = p.add_option_group('Pattern Grouping')
124 g.add_option('--all-match',
125 action='callback', callback=carry,
126 help='Limit match to lines that have all patterns')
127 g.add_option('--and', '--or', '--not',
128 action='callback', callback=carry,
129 help='Boolean operators to combine patterns')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900130 g.add_option('-(', '-)',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700131 action='callback', callback=carry,
132 help='Boolean operator grouping')
133
134 g = p.add_option_group('Output')
135 g.add_option('-n',
136 action='callback', callback=carry,
137 help='Prefix the line number to matching lines')
138 g.add_option('-C',
139 action='callback', callback=carry,
140 metavar='CONTEXT', type='str',
141 help='Show CONTEXT lines around match')
142 g.add_option('-B',
143 action='callback', callback=carry,
144 metavar='CONTEXT', type='str',
145 help='Show CONTEXT lines before match')
146 g.add_option('-A',
147 action='callback', callback=carry,
148 metavar='CONTEXT', type='str',
149 help='Show CONTEXT lines after match')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900150 g.add_option('-l', '--name-only', '--files-with-matches',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700151 action='callback', callback=carry,
152 help='Show only file names containing matching lines')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900153 g.add_option('-L', '--files-without-match',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700154 action='callback', callback=carry,
155 help='Show only file names not containing matching lines')
156
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700157 def Execute(self, opt, args):
158 out = GrepColoring(self.manifest.manifestProject.config)
159
160 cmd_argv = ['grep']
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500161 if out.is_on:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700162 cmd_argv.append('--color')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900163 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700164
165 if '-e' not in cmd_argv:
166 if not args:
167 self.Usage()
168 cmd_argv.append('-e')
169 cmd_argv.append(args[0])
170 args = args[1:]
171
172 projects = self.GetProjects(args)
173
174 full_name = False
175 if len(projects) > 1:
176 cmd_argv.append('--full-name')
177 full_name = True
178
179 have_rev = False
180 if opt.revision:
181 if '--cached' in cmd_argv:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700182 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700183 sys.exit(1)
184 have_rev = True
185 cmd_argv.extend(opt.revision)
186 cmd_argv.append('--')
187
Mike Frysinger72ab8522019-10-01 00:18:46 -0400188 git_failed = False
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700189 bad_rev = False
190 have_match = False
191
192 for project in projects:
Mike Frysinger72ab8522019-10-01 00:18:46 -0400193 try:
194 p = GitCommand(project,
195 cmd_argv,
196 bare=False,
197 capture_stdout=True,
198 capture_stderr=True)
199 except GitError as e:
200 git_failed = True
201 out.project('--- project %s ---' % project.relpath)
202 out.nl()
203 out.fail('%s', str(e))
204 out.nl()
205 continue
206
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700207 if p.Wait() != 0:
208 # no results
209 #
210 if p.stderr:
211 if have_rev and 'fatal: ambiguous argument' in p.stderr:
212 bad_rev = True
213 else:
214 out.project('--- project %s ---' % project.relpath)
215 out.nl()
Mike Frysinger72ab8522019-10-01 00:18:46 -0400216 out.fail('%s', p.stderr.strip())
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700217 out.nl()
218 continue
219 have_match = True
220
221 # We cut the last element, to avoid a blank line.
222 #
223 r = p.stdout.split('\n')
224 r = r[0:-1]
225
226 if have_rev and full_name:
227 for line in r:
228 rev, line = line.split(':', 1)
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200229 out.write("%s", rev)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700230 out.write(':')
231 out.project(project.relpath)
232 out.write('/')
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200233 out.write("%s", line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700234 out.nl()
235 elif full_name:
236 for line in r:
237 out.project(project.relpath)
238 out.write('/')
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200239 out.write("%s", line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700240 out.nl()
241 else:
242 for line in r:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700243 print(line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700244
Mike Frysinger72ab8522019-10-01 00:18:46 -0400245 if git_failed:
246 sys.exit(1)
247 elif have_match:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700248 sys.exit(0)
249 elif have_rev and bad_rev:
250 for r in opt.revision:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700251 print("error: can't search revision %s" % r, file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700252 sys.exit(1)
253 else:
254 sys.exit(1)