| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- # -*- coding:utf-8 -*-
- #
- # Copyright (C) 2009 The Android Open Source Project
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- from __future__ import print_function
- import sys
- from color import Coloring
- from command import PagedCommand
- from error import GitError
- from git_command import git_require, GitCommand
- class GrepColoring(Coloring):
- def __init__(self, config):
- Coloring.__init__(self, config, 'grep')
- self.project = self.printer('project', attr='bold')
- self.fail = self.printer('fail', fg='red')
- class Grep(PagedCommand):
- common = True
- helpSummary = "Print lines matching a pattern"
- helpUsage = """
- %prog {pattern | -e pattern} [<project>...]
- """
- helpDescription = """
- Search for the specified patterns in all project files.
- # Boolean Options
- The following options can appear as often as necessary to express
- the pattern to locate:
- -e PATTERN
- --and, --or, --not, -(, -)
- Further, the -r/--revision option may be specified multiple times
- in order to scan multiple trees. If the same file matches in more
- than one tree, only the first result is reported, prefixed by the
- revision name it was found under.
- # Examples
- Look for a line that has '#define' and either 'MAX_PATH or 'PATH_MAX':
- repo grep -e '#define' --and -\\( -e MAX_PATH -e PATH_MAX \\)
- Look for a line that has 'NODE' or 'Unexpected' in files that
- contain a line that matches both expressions:
- repo grep --all-match -e NODE -e Unexpected
- """
- def _Options(self, p):
- def carry(option,
- opt_str,
- value,
- parser):
- pt = getattr(parser.values, 'cmd_argv', None)
- if pt is None:
- pt = []
- setattr(parser.values, 'cmd_argv', pt)
- if opt_str == '-(':
- pt.append('(')
- elif opt_str == '-)':
- pt.append(')')
- else:
- pt.append(opt_str)
- if value is not None:
- pt.append(value)
- g = p.add_option_group('Sources')
- g.add_option('--cached',
- action='callback', callback=carry,
- help='Search the index, instead of the work tree')
- g.add_option('-r', '--revision',
- dest='revision', action='append', metavar='TREEish',
- help='Search TREEish, instead of the work tree')
- g = p.add_option_group('Pattern')
- g.add_option('-e',
- action='callback', callback=carry,
- metavar='PATTERN', type='str',
- help='Pattern to search for')
- g.add_option('-i', '--ignore-case',
- action='callback', callback=carry,
- help='Ignore case differences')
- g.add_option('-a', '--text',
- action='callback', callback=carry,
- help="Process binary files as if they were text")
- g.add_option('-I',
- action='callback', callback=carry,
- help="Don't match the pattern in binary files")
- g.add_option('-w', '--word-regexp',
- action='callback', callback=carry,
- help='Match the pattern only at word boundaries')
- g.add_option('-v', '--invert-match',
- action='callback', callback=carry,
- help='Select non-matching lines')
- g.add_option('-G', '--basic-regexp',
- action='callback', callback=carry,
- help='Use POSIX basic regexp for patterns (default)')
- g.add_option('-E', '--extended-regexp',
- action='callback', callback=carry,
- help='Use POSIX extended regexp for patterns')
- g.add_option('-F', '--fixed-strings',
- action='callback', callback=carry,
- help='Use fixed strings (not regexp) for pattern')
- g = p.add_option_group('Pattern Grouping')
- g.add_option('--all-match',
- action='callback', callback=carry,
- help='Limit match to lines that have all patterns')
- g.add_option('--and', '--or', '--not',
- action='callback', callback=carry,
- help='Boolean operators to combine patterns')
- g.add_option('-(', '-)',
- action='callback', callback=carry,
- help='Boolean operator grouping')
- g = p.add_option_group('Output')
- g.add_option('-n',
- action='callback', callback=carry,
- help='Prefix the line number to matching lines')
- g.add_option('-C',
- action='callback', callback=carry,
- metavar='CONTEXT', type='str',
- help='Show CONTEXT lines around match')
- g.add_option('-B',
- action='callback', callback=carry,
- metavar='CONTEXT', type='str',
- help='Show CONTEXT lines before match')
- g.add_option('-A',
- action='callback', callback=carry,
- metavar='CONTEXT', type='str',
- help='Show CONTEXT lines after match')
- g.add_option('-l', '--name-only', '--files-with-matches',
- action='callback', callback=carry,
- help='Show only file names containing matching lines')
- g.add_option('-L', '--files-without-match',
- action='callback', callback=carry,
- help='Show only file names not containing matching lines')
- def Execute(self, opt, args):
- out = GrepColoring(self.manifest.manifestProject.config)
- cmd_argv = ['grep']
- if out.is_on and git_require((1, 6, 3)):
- cmd_argv.append('--color')
- cmd_argv.extend(getattr(opt, 'cmd_argv', []))
- if '-e' not in cmd_argv:
- if not args:
- self.Usage()
- cmd_argv.append('-e')
- cmd_argv.append(args[0])
- args = args[1:]
- projects = self.GetProjects(args)
- full_name = False
- if len(projects) > 1:
- cmd_argv.append('--full-name')
- full_name = True
- have_rev = False
- if opt.revision:
- if '--cached' in cmd_argv:
- print('fatal: cannot combine --cached and --revision', file=sys.stderr)
- sys.exit(1)
- have_rev = True
- cmd_argv.extend(opt.revision)
- cmd_argv.append('--')
- git_failed = False
- bad_rev = False
- have_match = False
- for project in projects:
- try:
- p = GitCommand(project,
- cmd_argv,
- bare=False,
- capture_stdout=True,
- capture_stderr=True)
- except GitError as e:
- git_failed = True
- out.project('--- project %s ---' % project.relpath)
- out.nl()
- out.fail('%s', str(e))
- out.nl()
- continue
- if p.Wait() != 0:
- # no results
- #
- if p.stderr:
- if have_rev and 'fatal: ambiguous argument' in p.stderr:
- bad_rev = True
- else:
- out.project('--- project %s ---' % project.relpath)
- out.nl()
- out.fail('%s', p.stderr.strip())
- out.nl()
- continue
- have_match = True
- # We cut the last element, to avoid a blank line.
- #
- r = p.stdout.split('\n')
- r = r[0:-1]
- if have_rev and full_name:
- for line in r:
- rev, line = line.split(':', 1)
- out.write("%s", rev)
- out.write(':')
- out.project(project.relpath)
- out.write('/')
- out.write("%s", line)
- out.nl()
- elif full_name:
- for line in r:
- out.project(project.relpath)
- out.write('/')
- out.write("%s", line)
- out.nl()
- else:
- for line in r:
- print(line)
- if git_failed:
- sys.exit(1)
- elif have_match:
- sys.exit(0)
- elif have_rev and bad_rev:
- for r in opt.revision:
- print("error: can't search revision %s" % r, file=sys.stderr)
- sys.exit(1)
- else:
- sys.exit(1)
|