blob: 4dd85d575d0fe81e175bf0c75a84607f77259ec3 [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
Shawn O. Pearcef0d4c362009-06-12 09:33:48 -070024from git_command import git_require, GitCommand
Shawn O. Pearceb812a362009-04-10 20:37:47 -070025
26class GrepColoring(Coloring):
27 def __init__(self, config):
28 Coloring.__init__(self, config, 'grep')
29 self.project = self.printer('project', attr='bold')
Mike Frysinger72ab8522019-10-01 00:18:46 -040030 self.fail = self.printer('fail', fg='red')
Shawn O. Pearceb812a362009-04-10 20:37:47 -070031
32class Grep(PagedCommand):
33 common = True
34 helpSummary = "Print lines matching a pattern"
35 helpUsage = """
36%prog {pattern | -e pattern} [<project>...]
37"""
38 helpDescription = """
39Search for the specified patterns in all project files.
40
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040041# Boolean Options
Shawn O. Pearceb812a362009-04-10 20:37:47 -070042
43The following options can appear as often as necessary to express
44the pattern to locate:
45
46 -e PATTERN
47 --and, --or, --not, -(, -)
48
49Further, the -r/--revision option may be specified multiple times
50in order to scan multiple trees. If the same file matches in more
51than one tree, only the first result is reported, prefixed by the
52revision name it was found under.
53
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040054# Examples
Shawn O. Pearceb812a362009-04-10 20:37:47 -070055
56Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX':
57
David Pursehouse1d947b32012-10-25 12:23:11 +090058 repo grep -e '#define' --and -\\( -e MAX_PATH -e PATH_MAX \\)
Shawn O. Pearceb812a362009-04-10 20:37:47 -070059
60Look for a line that has 'NODE' or 'Unexpected' in files that
61contain a line that matches both expressions:
62
63 repo grep --all-match -e NODE -e Unexpected
64
65"""
66
67 def _Options(self, p):
68 def carry(option,
69 opt_str,
70 value,
71 parser):
72 pt = getattr(parser.values, 'cmd_argv', None)
73 if pt is None:
74 pt = []
75 setattr(parser.values, 'cmd_argv', pt)
76
77 if opt_str == '-(':
78 pt.append('(')
79 elif opt_str == '-)':
80 pt.append(')')
81 else:
82 pt.append(opt_str)
83
84 if value is not None:
85 pt.append(value)
86
87 g = p.add_option_group('Sources')
88 g.add_option('--cached',
89 action='callback', callback=carry,
90 help='Search the index, instead of the work tree')
David Pursehouse8f62fb72012-11-14 12:09:38 +090091 g.add_option('-r', '--revision',
Shawn O. Pearceb812a362009-04-10 20:37:47 -070092 dest='revision', action='append', metavar='TREEish',
93 help='Search TREEish, instead of the work tree')
94
95 g = p.add_option_group('Pattern')
96 g.add_option('-e',
97 action='callback', callback=carry,
98 metavar='PATTERN', type='str',
99 help='Pattern to search for')
100 g.add_option('-i', '--ignore-case',
101 action='callback', callback=carry,
102 help='Ignore case differences')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900103 g.add_option('-a', '--text',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700104 action='callback', callback=carry,
105 help="Process binary files as if they were text")
106 g.add_option('-I',
107 action='callback', callback=carry,
108 help="Don't match the pattern in binary files")
109 g.add_option('-w', '--word-regexp',
110 action='callback', callback=carry,
111 help='Match the pattern only at word boundaries')
112 g.add_option('-v', '--invert-match',
113 action='callback', callback=carry,
114 help='Select non-matching lines')
115 g.add_option('-G', '--basic-regexp',
116 action='callback', callback=carry,
117 help='Use POSIX basic regexp for patterns (default)')
118 g.add_option('-E', '--extended-regexp',
119 action='callback', callback=carry,
120 help='Use POSIX extended regexp for patterns')
121 g.add_option('-F', '--fixed-strings',
122 action='callback', callback=carry,
123 help='Use fixed strings (not regexp) for pattern')
124
125 g = p.add_option_group('Pattern Grouping')
126 g.add_option('--all-match',
127 action='callback', callback=carry,
128 help='Limit match to lines that have all patterns')
129 g.add_option('--and', '--or', '--not',
130 action='callback', callback=carry,
131 help='Boolean operators to combine patterns')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900132 g.add_option('-(', '-)',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700133 action='callback', callback=carry,
134 help='Boolean operator grouping')
135
136 g = p.add_option_group('Output')
137 g.add_option('-n',
138 action='callback', callback=carry,
139 help='Prefix the line number to matching lines')
140 g.add_option('-C',
141 action='callback', callback=carry,
142 metavar='CONTEXT', type='str',
143 help='Show CONTEXT lines around match')
144 g.add_option('-B',
145 action='callback', callback=carry,
146 metavar='CONTEXT', type='str',
147 help='Show CONTEXT lines before match')
148 g.add_option('-A',
149 action='callback', callback=carry,
150 metavar='CONTEXT', type='str',
151 help='Show CONTEXT lines after match')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900152 g.add_option('-l', '--name-only', '--files-with-matches',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700153 action='callback', callback=carry,
154 help='Show only file names containing matching lines')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900155 g.add_option('-L', '--files-without-match',
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700156 action='callback', callback=carry,
157 help='Show only file names not containing matching lines')
158
159
160 def Execute(self, opt, args):
161 out = GrepColoring(self.manifest.manifestProject.config)
162
163 cmd_argv = ['grep']
David Pursehouse8f62fb72012-11-14 12:09:38 +0900164 if out.is_on and git_require((1, 6, 3)):
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700165 cmd_argv.append('--color')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900166 cmd_argv.extend(getattr(opt, 'cmd_argv', []))
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700167
168 if '-e' not in cmd_argv:
169 if not args:
170 self.Usage()
171 cmd_argv.append('-e')
172 cmd_argv.append(args[0])
173 args = args[1:]
174
175 projects = self.GetProjects(args)
176
177 full_name = False
178 if len(projects) > 1:
179 cmd_argv.append('--full-name')
180 full_name = True
181
182 have_rev = False
183 if opt.revision:
184 if '--cached' in cmd_argv:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700185 print('fatal: cannot combine --cached and --revision', file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700186 sys.exit(1)
187 have_rev = True
188 cmd_argv.extend(opt.revision)
189 cmd_argv.append('--')
190
Mike Frysinger72ab8522019-10-01 00:18:46 -0400191 git_failed = False
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700192 bad_rev = False
193 have_match = False
194
195 for project in projects:
Mike Frysinger72ab8522019-10-01 00:18:46 -0400196 try:
197 p = GitCommand(project,
198 cmd_argv,
199 bare=False,
200 capture_stdout=True,
201 capture_stderr=True)
202 except GitError as e:
203 git_failed = True
204 out.project('--- project %s ---' % project.relpath)
205 out.nl()
206 out.fail('%s', str(e))
207 out.nl()
208 continue
209
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700210 if p.Wait() != 0:
211 # no results
212 #
213 if p.stderr:
214 if have_rev and 'fatal: ambiguous argument' in p.stderr:
215 bad_rev = True
216 else:
217 out.project('--- project %s ---' % project.relpath)
218 out.nl()
Mike Frysinger72ab8522019-10-01 00:18:46 -0400219 out.fail('%s', p.stderr.strip())
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700220 out.nl()
221 continue
222 have_match = True
223
224 # We cut the last element, to avoid a blank line.
225 #
226 r = p.stdout.split('\n')
227 r = r[0:-1]
228
229 if have_rev and full_name:
230 for line in r:
231 rev, line = line.split(':', 1)
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200232 out.write("%s", rev)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700233 out.write(':')
234 out.project(project.relpath)
235 out.write('/')
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200236 out.write("%s", line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700237 out.nl()
238 elif full_name:
239 for line in r:
240 out.project(project.relpath)
241 out.write('/')
Sebastian Schmidtfeb39d62010-06-02 17:18:13 +0200242 out.write("%s", line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700243 out.nl()
244 else:
245 for line in r:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700246 print(line)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700247
Mike Frysinger72ab8522019-10-01 00:18:46 -0400248 if git_failed:
249 sys.exit(1)
250 elif have_match:
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700251 sys.exit(0)
252 elif have_rev and bad_rev:
253 for r in opt.revision:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700254 print("error: can't search revision %s" % r, file=sys.stderr)
Shawn O. Pearceb812a362009-04-10 20:37:47 -0700255 sys.exit(1)
256 else:
257 sys.exit(1)