blob: 89611cd675f9c2dc8acdcd069f83c113f3981481 [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
Shawn O. Pearceb812a362009-04-10 20:37:47 -07002#
3# Copyright (C) 2009 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Mike Frysinger72ab8522019-10-01 00:18:46 -040018
Shawn O. Pearceb812a362009-04-10 20:37:47 -070019import sys
Mike Frysinger72ab8522019-10-01 00:18:46 -040020
Shawn O. Pearceb812a362009-04-10 20:37:47 -070021from color import Coloring
22from command import PagedCommand
Mike Frysinger72ab8522019-10-01 00:18:46 -040023from error import GitError
Mike Frysinger6f1c6262020-02-04 00:09:23 -050024from git_command import GitCommand
Shawn O. Pearceb812a362009-04-10 20:37:47 -070025
David Pursehouse819827a2020-02-12 15:20:19 +090026
Shawn O. Pearceb812a362009-04-10 20:37:47 -070027class GrepColoring(Coloring):
28 def __init__(self, config):
29 Coloring.__init__(self, config, 'grep')
30 self.project = self.printer('project', attr='bold')
Mike Frysinger72ab8522019-10-01 00:18:46 -040031 self.fail = self.printer('fail', fg='red')
Shawn O. Pearceb812a362009-04-10 20:37:47 -070032
David Pursehouse819827a2020-02-12 15:20:19 +090033
Shawn O. Pearceb812a362009-04-10 20:37:47 -070034class Grep(PagedCommand):
35 common = True
36 helpSummary = "Print lines matching a pattern"
37 helpUsage = """
38%prog {pattern | -e pattern} [<project>...]
39"""
40 helpDescription = """
41Search for the specified patterns in all project files.
42
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040043# Boolean Options
Shawn O. Pearceb812a362009-04-10 20:37:47 -070044
45The following options can appear as often as necessary to express
46the pattern to locate:
47
48 -e PATTERN
49 --and, --or, --not, -(, -)
50
51Further, the -r/--revision option may be specified multiple times
52in order to scan multiple trees. If the same file matches in more
53than one tree, only the first result is reported, prefixed by the
54revision name it was found under.
55
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040056# Examples
Shawn O. Pearceb812a362009-04-10 20:37:47 -070057
58Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX':
59
David Pursehouse1d947b32012-10-25 12:23:11 +090060 repo grep -e '#define' --and -\\( -e MAX_PATH -e PATH_MAX \\)
Shawn O. Pearceb812a362009-04-10 20:37:47 -070061
62Look for a line that has 'NODE' or 'Unexpected' in files that
63contain a line that matches both expressions:
64
65 repo grep --all-match -e NODE -e Unexpected
66
67"""
68
69 def _Options(self, p):
70 def carry(option,
71 opt_str,
72 value,
73 parser):
74 pt = getattr(parser.values, 'cmd_argv', None)
75 if pt is None:
76 pt = []
77 setattr(parser.values, 'cmd_argv', pt)
78
79 if opt_str == '-(':
80 pt.append('(')
81 elif opt_str == '-)':
82 pt.append(')')
83 else:
84 pt.append(opt_str)
85
86 if value is not None:
87 pt.append(value)
88
89 g = p.add_option_group('Sources')
90 g.add_option('--cached',
91 action='callback', callback=carry,
92 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',
99 action='callback', callback=carry,
100 metavar='PATTERN', type='str',
101 help='Pattern to search for')
102 g.add_option('-i', '--ignore-case',
103 action='callback', callback=carry,
104 help='Ignore case differences')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900105 g.add_option('-a', '--text',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700106 action='callback', callback=carry,
107 help="Process binary files as if they were text")
108 g.add_option('-I',
109 action='callback', callback=carry,
110 help="Don't match the pattern in binary files")
111 g.add_option('-w', '--word-regexp',
112 action='callback', callback=carry,
113 help='Match the pattern only at word boundaries')
114 g.add_option('-v', '--invert-match',
115 action='callback', callback=carry,
116 help='Select non-matching lines')
117 g.add_option('-G', '--basic-regexp',
118 action='callback', callback=carry,
119 help='Use POSIX basic regexp for patterns (default)')
120 g.add_option('-E', '--extended-regexp',
121 action='callback', callback=carry,
122 help='Use POSIX extended regexp for patterns')
123 g.add_option('-F', '--fixed-strings',
124 action='callback', callback=carry,
125 help='Use fixed strings (not regexp) for pattern')
126
127 g = p.add_option_group('Pattern Grouping')
128 g.add_option('--all-match',
129 action='callback', callback=carry,
130 help='Limit match to lines that have all patterns')
131 g.add_option('--and', '--or', '--not',
132 action='callback', callback=carry,
133 help='Boolean operators to combine patterns')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900134 g.add_option('-(', '-)',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700135 action='callback', callback=carry,
136 help='Boolean operator grouping')
137
138 g = p.add_option_group('Output')
139 g.add_option('-n',
140 action='callback', callback=carry,
141 help='Prefix the line number to matching lines')
142 g.add_option('-C',
143 action='callback', callback=carry,
144 metavar='CONTEXT', type='str',
145 help='Show CONTEXT lines around match')
146 g.add_option('-B',
147 action='callback', callback=carry,
148 metavar='CONTEXT', type='str',
149 help='Show CONTEXT lines before match')
150 g.add_option('-A',
151 action='callback', callback=carry,
152 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',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700155 action='callback', callback=carry,
156 help='Show only file names containing matching lines')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900157 g.add_option('-L', '--files-without-match',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700158 action='callback', callback=carry,
159 help='Show only file names not containing matching lines')
160
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700161 def Execute(self, opt, args):
162 out = GrepColoring(self.manifest.manifestProject.config)
163
164 cmd_argv = ['grep']
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500165 if out.is_on:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700166 cmd_argv.append('--color')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900167 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700168
169 if '-e' not in cmd_argv:
170 if not args:
171 self.Usage()
172 cmd_argv.append('-e')
173 cmd_argv.append(args[0])
174 args = args[1:]
175
176 projects = self.GetProjects(args)
177
178 full_name = False
179 if len(projects) > 1:
180 cmd_argv.append('--full-name')
181 full_name = True
182
183 have_rev = False
184 if opt.revision:
185 if '--cached' in cmd_argv:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700186 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700187 sys.exit(1)
188 have_rev = True
189 cmd_argv.extend(opt.revision)
190 cmd_argv.append('--')
191
Mike Frysinger72ab8522019-10-01 00:18:46 -0400192 git_failed = False
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700193 bad_rev = False
194 have_match = False
195
196 for project in projects:
Mike Frysinger72ab8522019-10-01 00:18:46 -0400197 try:
198 p = GitCommand(project,
199 cmd_argv,
200 bare=False,
201 capture_stdout=True,
202 capture_stderr=True)
203 except GitError as e:
204 git_failed = True
205 out.project('--- project %s ---' % project.relpath)
206 out.nl()
207 out.fail('%s', str(e))
208 out.nl()
209 continue
210
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700211 if p.Wait() != 0:
212 # no results
213 #
214 if p.stderr:
215 if have_rev and 'fatal: ambiguous argument' in p.stderr:
216 bad_rev = True
217 else:
218 out.project('--- project %s ---' % project.relpath)
219 out.nl()
Mike Frysinger72ab8522019-10-01 00:18:46 -0400220 out.fail('%s', p.stderr.strip())
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700221 out.nl()
222 continue
223 have_match = True
224
225 # We cut the last element, to avoid a blank line.
226 #
227 r = p.stdout.split('\n')
228 r = r[0:-1]
229
230 if have_rev and full_name:
231 for line in r:
232 rev, line = line.split(':', 1)
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200233 out.write("%s", rev)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700234 out.write(':')
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 elif full_name:
240 for line in r:
241 out.project(project.relpath)
242 out.write('/')
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200243 out.write("%s", line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700244 out.nl()
245 else:
246 for line in r:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700247 print(line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700248
Mike Frysinger72ab8522019-10-01 00:18:46 -0400249 if git_failed:
250 sys.exit(1)
251 elif have_match:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700252 sys.exit(0)
253 elif have_rev and bad_rev:
254 for r in opt.revision:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700255 print("error: can't search revision %s" % r, file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700256 sys.exit(1)
257 else:
258 sys.exit(1)