xref: /aosp_15_r20/external/bcc/scripts/git-clang-format (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python
2*387f9dfdSAndroid Build Coastguard Worker#
3*387f9dfdSAndroid Build Coastguard Worker#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
4*387f9dfdSAndroid Build Coastguard Worker#
5*387f9dfdSAndroid Build Coastguard Worker# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6*387f9dfdSAndroid Build Coastguard Worker# See https://llvm.org/LICENSE.txt for license information.
7*387f9dfdSAndroid Build Coastguard Worker# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8*387f9dfdSAndroid Build Coastguard Worker#
9*387f9dfdSAndroid Build Coastguard Worker#===------------------------------------------------------------------------===#
10*387f9dfdSAndroid Build Coastguard Worker
11*387f9dfdSAndroid Build Coastguard Workerr"""
12*387f9dfdSAndroid Build Coastguard Workerclang-format git integration
13*387f9dfdSAndroid Build Coastguard Worker============================
14*387f9dfdSAndroid Build Coastguard Worker
15*387f9dfdSAndroid Build Coastguard WorkerThis file provides a clang-format integration for git. Put it somewhere in your
16*387f9dfdSAndroid Build Coastguard Workerpath and ensure that it is executable. Then, "git clang-format" will invoke
17*387f9dfdSAndroid Build Coastguard Workerclang-format on the changes in current files or a specific commit.
18*387f9dfdSAndroid Build Coastguard Worker
19*387f9dfdSAndroid Build Coastguard WorkerFor further details, run:
20*387f9dfdSAndroid Build Coastguard Workergit clang-format -h
21*387f9dfdSAndroid Build Coastguard Worker
22*387f9dfdSAndroid Build Coastguard WorkerRequires Python 2.7 or Python 3
23*387f9dfdSAndroid Build Coastguard Worker"""
24*387f9dfdSAndroid Build Coastguard Worker
25*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import absolute_import, division, print_function
26*387f9dfdSAndroid Build Coastguard Workerimport argparse
27*387f9dfdSAndroid Build Coastguard Workerimport collections
28*387f9dfdSAndroid Build Coastguard Workerimport contextlib
29*387f9dfdSAndroid Build Coastguard Workerimport errno
30*387f9dfdSAndroid Build Coastguard Workerimport os
31*387f9dfdSAndroid Build Coastguard Workerimport re
32*387f9dfdSAndroid Build Coastguard Workerimport subprocess
33*387f9dfdSAndroid Build Coastguard Workerimport sys
34*387f9dfdSAndroid Build Coastguard Worker
35*387f9dfdSAndroid Build Coastguard Workerusage = 'git clang-format [OPTIONS] [<commit>] [<commit>] [--] [<file>...]'
36*387f9dfdSAndroid Build Coastguard Worker
37*387f9dfdSAndroid Build Coastguard Workerdesc = '''
38*387f9dfdSAndroid Build Coastguard WorkerIf zero or one commits are given, run clang-format on all lines that differ
39*387f9dfdSAndroid Build Coastguard Workerbetween the working directory and <commit>, which defaults to HEAD.  Changes are
40*387f9dfdSAndroid Build Coastguard Workeronly applied to the working directory.
41*387f9dfdSAndroid Build Coastguard Worker
42*387f9dfdSAndroid Build Coastguard WorkerIf two commits are given (requires --diff), run clang-format on all lines in the
43*387f9dfdSAndroid Build Coastguard Workersecond <commit> that differ from the first <commit>.
44*387f9dfdSAndroid Build Coastguard Worker
45*387f9dfdSAndroid Build Coastguard WorkerThe following git-config settings set the default of the corresponding option:
46*387f9dfdSAndroid Build Coastguard Worker  clangFormat.binary
47*387f9dfdSAndroid Build Coastguard Worker  clangFormat.commit
48*387f9dfdSAndroid Build Coastguard Worker  clangFormat.extension
49*387f9dfdSAndroid Build Coastguard Worker  clangFormat.style
50*387f9dfdSAndroid Build Coastguard Worker'''
51*387f9dfdSAndroid Build Coastguard Worker
52*387f9dfdSAndroid Build Coastguard Worker# Name of the temporary index file in which save the output of clang-format.
53*387f9dfdSAndroid Build Coastguard Worker# This file is created within the .git directory.
54*387f9dfdSAndroid Build Coastguard Workertemp_index_basename = 'clang-format-index'
55*387f9dfdSAndroid Build Coastguard Worker
56*387f9dfdSAndroid Build Coastguard Worker
57*387f9dfdSAndroid Build Coastguard WorkerRange = collections.namedtuple('Range', 'start, count')
58*387f9dfdSAndroid Build Coastguard Worker
59*387f9dfdSAndroid Build Coastguard Worker
60*387f9dfdSAndroid Build Coastguard Workerdef main():
61*387f9dfdSAndroid Build Coastguard Worker  config = load_git_config()
62*387f9dfdSAndroid Build Coastguard Worker
63*387f9dfdSAndroid Build Coastguard Worker  # In order to keep '--' yet allow options after positionals, we need to
64*387f9dfdSAndroid Build Coastguard Worker  # check for '--' ourselves.  (Setting nargs='*' throws away the '--', while
65*387f9dfdSAndroid Build Coastguard Worker  # nargs=argparse.REMAINDER disallows options after positionals.)
66*387f9dfdSAndroid Build Coastguard Worker  argv = sys.argv[1:]
67*387f9dfdSAndroid Build Coastguard Worker  try:
68*387f9dfdSAndroid Build Coastguard Worker    idx = argv.index('--')
69*387f9dfdSAndroid Build Coastguard Worker  except ValueError:
70*387f9dfdSAndroid Build Coastguard Worker    dash_dash = []
71*387f9dfdSAndroid Build Coastguard Worker  else:
72*387f9dfdSAndroid Build Coastguard Worker    dash_dash = argv[idx:]
73*387f9dfdSAndroid Build Coastguard Worker    argv = argv[:idx]
74*387f9dfdSAndroid Build Coastguard Worker
75*387f9dfdSAndroid Build Coastguard Worker  default_extensions = ','.join([
76*387f9dfdSAndroid Build Coastguard Worker      # From clang/lib/Frontend/FrontendOptions.cpp, all lower case
77*387f9dfdSAndroid Build Coastguard Worker      'c', 'h',  # C
78*387f9dfdSAndroid Build Coastguard Worker      'm',  # ObjC
79*387f9dfdSAndroid Build Coastguard Worker      'mm',  # ObjC++
80*387f9dfdSAndroid Build Coastguard Worker      'cc', 'cp', 'cpp', 'c++', 'cxx', 'hh', 'hpp', 'hxx',  # C++
81*387f9dfdSAndroid Build Coastguard Worker      'cu',  # CUDA
82*387f9dfdSAndroid Build Coastguard Worker      # Other languages that clang-format supports
83*387f9dfdSAndroid Build Coastguard Worker      'proto', 'protodevel',  # Protocol Buffers
84*387f9dfdSAndroid Build Coastguard Worker      'java',  # Java
85*387f9dfdSAndroid Build Coastguard Worker      'js',  # JavaScript
86*387f9dfdSAndroid Build Coastguard Worker      'ts',  # TypeScript
87*387f9dfdSAndroid Build Coastguard Worker      'cs',  # C Sharp
88*387f9dfdSAndroid Build Coastguard Worker      ])
89*387f9dfdSAndroid Build Coastguard Worker
90*387f9dfdSAndroid Build Coastguard Worker  p = argparse.ArgumentParser(
91*387f9dfdSAndroid Build Coastguard Worker    usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter,
92*387f9dfdSAndroid Build Coastguard Worker    description=desc)
93*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('--binary',
94*387f9dfdSAndroid Build Coastguard Worker                 default=config.get('clangformat.binary', 'clang-format'),
95*387f9dfdSAndroid Build Coastguard Worker                 help='path to clang-format'),
96*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('--commit',
97*387f9dfdSAndroid Build Coastguard Worker                 default=config.get('clangformat.commit', 'HEAD'),
98*387f9dfdSAndroid Build Coastguard Worker                 help='default commit to use if none is specified'),
99*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('--diff', action='store_true',
100*387f9dfdSAndroid Build Coastguard Worker                 help='print a diff instead of applying the changes')
101*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('--extensions',
102*387f9dfdSAndroid Build Coastguard Worker                 default=config.get('clangformat.extensions',
103*387f9dfdSAndroid Build Coastguard Worker                                    default_extensions),
104*387f9dfdSAndroid Build Coastguard Worker                 help=('comma-separated list of file extensions to format, '
105*387f9dfdSAndroid Build Coastguard Worker                       'excluding the period and case-insensitive')),
106*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('-f', '--force', action='store_true',
107*387f9dfdSAndroid Build Coastguard Worker                 help='allow changes to unstaged files')
108*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('-p', '--patch', action='store_true',
109*387f9dfdSAndroid Build Coastguard Worker                 help='select hunks interactively')
110*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('-q', '--quiet', action='count', default=0,
111*387f9dfdSAndroid Build Coastguard Worker                 help='print less information')
112*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('--style',
113*387f9dfdSAndroid Build Coastguard Worker                 default=config.get('clangformat.style', None),
114*387f9dfdSAndroid Build Coastguard Worker                 help='passed to clang-format'),
115*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('-v', '--verbose', action='count', default=0,
116*387f9dfdSAndroid Build Coastguard Worker                 help='print extra information')
117*387f9dfdSAndroid Build Coastguard Worker  # We gather all the remaining positional arguments into 'args' since we need
118*387f9dfdSAndroid Build Coastguard Worker  # to use some heuristics to determine whether or not <commit> was present.
119*387f9dfdSAndroid Build Coastguard Worker  # However, to print pretty messages, we make use of metavar and help.
120*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('args', nargs='*', metavar='<commit>',
121*387f9dfdSAndroid Build Coastguard Worker                 help='revision from which to compute the diff')
122*387f9dfdSAndroid Build Coastguard Worker  p.add_argument('ignored', nargs='*', metavar='<file>...',
123*387f9dfdSAndroid Build Coastguard Worker                 help='if specified, only consider differences in these files')
124*387f9dfdSAndroid Build Coastguard Worker  opts = p.parse_args(argv)
125*387f9dfdSAndroid Build Coastguard Worker
126*387f9dfdSAndroid Build Coastguard Worker  opts.verbose -= opts.quiet
127*387f9dfdSAndroid Build Coastguard Worker  del opts.quiet
128*387f9dfdSAndroid Build Coastguard Worker
129*387f9dfdSAndroid Build Coastguard Worker  commits, files = interpret_args(opts.args, dash_dash, opts.commit)
130*387f9dfdSAndroid Build Coastguard Worker  if len(commits) > 1:
131*387f9dfdSAndroid Build Coastguard Worker    if not opts.diff:
132*387f9dfdSAndroid Build Coastguard Worker      die('--diff is required when two commits are given')
133*387f9dfdSAndroid Build Coastguard Worker  else:
134*387f9dfdSAndroid Build Coastguard Worker    if len(commits) > 2:
135*387f9dfdSAndroid Build Coastguard Worker      die('at most two commits allowed; %d given' % len(commits))
136*387f9dfdSAndroid Build Coastguard Worker  changed_lines = compute_diff_and_extract_lines(commits, files)
137*387f9dfdSAndroid Build Coastguard Worker  if opts.verbose >= 1:
138*387f9dfdSAndroid Build Coastguard Worker    ignored_files = set(changed_lines)
139*387f9dfdSAndroid Build Coastguard Worker  filter_by_extension(changed_lines, opts.extensions.lower().split(','))
140*387f9dfdSAndroid Build Coastguard Worker  if opts.verbose >= 1:
141*387f9dfdSAndroid Build Coastguard Worker    ignored_files.difference_update(changed_lines)
142*387f9dfdSAndroid Build Coastguard Worker    if ignored_files:
143*387f9dfdSAndroid Build Coastguard Worker      print('Ignoring changes in the following files (wrong extension):')
144*387f9dfdSAndroid Build Coastguard Worker      for filename in ignored_files:
145*387f9dfdSAndroid Build Coastguard Worker        print('    %s' % filename)
146*387f9dfdSAndroid Build Coastguard Worker    if changed_lines:
147*387f9dfdSAndroid Build Coastguard Worker      print('Running clang-format on the following files:')
148*387f9dfdSAndroid Build Coastguard Worker      for filename in changed_lines:
149*387f9dfdSAndroid Build Coastguard Worker        print('    %s' % filename)
150*387f9dfdSAndroid Build Coastguard Worker  if not changed_lines:
151*387f9dfdSAndroid Build Coastguard Worker    print('no modified files to format')
152*387f9dfdSAndroid Build Coastguard Worker    return
153*387f9dfdSAndroid Build Coastguard Worker  # The computed diff outputs absolute paths, so we must cd before accessing
154*387f9dfdSAndroid Build Coastguard Worker  # those files.
155*387f9dfdSAndroid Build Coastguard Worker  cd_to_toplevel()
156*387f9dfdSAndroid Build Coastguard Worker  if len(commits) > 1:
157*387f9dfdSAndroid Build Coastguard Worker    old_tree = commits[1]
158*387f9dfdSAndroid Build Coastguard Worker    new_tree = run_clang_format_and_save_to_tree(changed_lines,
159*387f9dfdSAndroid Build Coastguard Worker                                                 revision=commits[1],
160*387f9dfdSAndroid Build Coastguard Worker                                                 binary=opts.binary,
161*387f9dfdSAndroid Build Coastguard Worker                                                 style=opts.style)
162*387f9dfdSAndroid Build Coastguard Worker  else:
163*387f9dfdSAndroid Build Coastguard Worker    old_tree = create_tree_from_workdir(changed_lines)
164*387f9dfdSAndroid Build Coastguard Worker    new_tree = run_clang_format_and_save_to_tree(changed_lines,
165*387f9dfdSAndroid Build Coastguard Worker                                                 binary=opts.binary,
166*387f9dfdSAndroid Build Coastguard Worker                                                 style=opts.style)
167*387f9dfdSAndroid Build Coastguard Worker  if opts.verbose >= 1:
168*387f9dfdSAndroid Build Coastguard Worker    print('old tree: %s' % old_tree)
169*387f9dfdSAndroid Build Coastguard Worker    print('new tree: %s' % new_tree)
170*387f9dfdSAndroid Build Coastguard Worker  if old_tree == new_tree:
171*387f9dfdSAndroid Build Coastguard Worker    if opts.verbose >= 0:
172*387f9dfdSAndroid Build Coastguard Worker      print('clang-format did not modify any files')
173*387f9dfdSAndroid Build Coastguard Worker  elif opts.diff:
174*387f9dfdSAndroid Build Coastguard Worker    print_diff(old_tree, new_tree)
175*387f9dfdSAndroid Build Coastguard Worker  else:
176*387f9dfdSAndroid Build Coastguard Worker    changed_files = apply_changes(old_tree, new_tree, force=opts.force,
177*387f9dfdSAndroid Build Coastguard Worker                                  patch_mode=opts.patch)
178*387f9dfdSAndroid Build Coastguard Worker    if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
179*387f9dfdSAndroid Build Coastguard Worker      print('changed files:')
180*387f9dfdSAndroid Build Coastguard Worker      for filename in changed_files:
181*387f9dfdSAndroid Build Coastguard Worker        print('    %s' % filename)
182*387f9dfdSAndroid Build Coastguard Worker
183*387f9dfdSAndroid Build Coastguard Worker
184*387f9dfdSAndroid Build Coastguard Workerdef load_git_config(non_string_options=None):
185*387f9dfdSAndroid Build Coastguard Worker  """Return the git configuration as a dictionary.
186*387f9dfdSAndroid Build Coastguard Worker
187*387f9dfdSAndroid Build Coastguard Worker  All options are assumed to be strings unless in `non_string_options`, in which
188*387f9dfdSAndroid Build Coastguard Worker  is a dictionary mapping option name (in lower case) to either "--bool" or
189*387f9dfdSAndroid Build Coastguard Worker  "--int"."""
190*387f9dfdSAndroid Build Coastguard Worker  if non_string_options is None:
191*387f9dfdSAndroid Build Coastguard Worker    non_string_options = {}
192*387f9dfdSAndroid Build Coastguard Worker  out = {}
193*387f9dfdSAndroid Build Coastguard Worker  for entry in run('git', 'config', '--list', '--null').split('\0'):
194*387f9dfdSAndroid Build Coastguard Worker    if entry:
195*387f9dfdSAndroid Build Coastguard Worker      name, value = entry.split('\n', 1)
196*387f9dfdSAndroid Build Coastguard Worker      if name in non_string_options:
197*387f9dfdSAndroid Build Coastguard Worker        value = run('git', 'config', non_string_options[name], name)
198*387f9dfdSAndroid Build Coastguard Worker      out[name] = value
199*387f9dfdSAndroid Build Coastguard Worker  return out
200*387f9dfdSAndroid Build Coastguard Worker
201*387f9dfdSAndroid Build Coastguard Worker
202*387f9dfdSAndroid Build Coastguard Workerdef interpret_args(args, dash_dash, default_commit):
203*387f9dfdSAndroid Build Coastguard Worker  """Interpret `args` as "[commits] [--] [files]" and return (commits, files).
204*387f9dfdSAndroid Build Coastguard Worker
205*387f9dfdSAndroid Build Coastguard Worker  It is assumed that "--" and everything that follows has been removed from
206*387f9dfdSAndroid Build Coastguard Worker  args and placed in `dash_dash`.
207*387f9dfdSAndroid Build Coastguard Worker
208*387f9dfdSAndroid Build Coastguard Worker  If "--" is present (i.e., `dash_dash` is non-empty), the arguments to its
209*387f9dfdSAndroid Build Coastguard Worker  left (if present) are taken as commits.  Otherwise, the arguments are checked
210*387f9dfdSAndroid Build Coastguard Worker  from left to right if they are commits or files.  If commits are not given,
211*387f9dfdSAndroid Build Coastguard Worker  a list with `default_commit` is used."""
212*387f9dfdSAndroid Build Coastguard Worker  if dash_dash:
213*387f9dfdSAndroid Build Coastguard Worker    if len(args) == 0:
214*387f9dfdSAndroid Build Coastguard Worker      commits = [default_commit]
215*387f9dfdSAndroid Build Coastguard Worker    else:
216*387f9dfdSAndroid Build Coastguard Worker      commits = args
217*387f9dfdSAndroid Build Coastguard Worker    for commit in commits:
218*387f9dfdSAndroid Build Coastguard Worker      object_type = get_object_type(commit)
219*387f9dfdSAndroid Build Coastguard Worker      if object_type not in ('commit', 'tag'):
220*387f9dfdSAndroid Build Coastguard Worker        if object_type is None:
221*387f9dfdSAndroid Build Coastguard Worker          die("'%s' is not a commit" % commit)
222*387f9dfdSAndroid Build Coastguard Worker        else:
223*387f9dfdSAndroid Build Coastguard Worker          die("'%s' is a %s, but a commit was expected" % (commit, object_type))
224*387f9dfdSAndroid Build Coastguard Worker    files = dash_dash[1:]
225*387f9dfdSAndroid Build Coastguard Worker  elif args:
226*387f9dfdSAndroid Build Coastguard Worker    commits = []
227*387f9dfdSAndroid Build Coastguard Worker    while args:
228*387f9dfdSAndroid Build Coastguard Worker      if not disambiguate_revision(args[0]):
229*387f9dfdSAndroid Build Coastguard Worker        break
230*387f9dfdSAndroid Build Coastguard Worker      commits.append(args.pop(0))
231*387f9dfdSAndroid Build Coastguard Worker    if not commits:
232*387f9dfdSAndroid Build Coastguard Worker      commits = [default_commit]
233*387f9dfdSAndroid Build Coastguard Worker    files = args
234*387f9dfdSAndroid Build Coastguard Worker  else:
235*387f9dfdSAndroid Build Coastguard Worker    commits = [default_commit]
236*387f9dfdSAndroid Build Coastguard Worker    files = []
237*387f9dfdSAndroid Build Coastguard Worker  return commits, files
238*387f9dfdSAndroid Build Coastguard Worker
239*387f9dfdSAndroid Build Coastguard Worker
240*387f9dfdSAndroid Build Coastguard Workerdef disambiguate_revision(value):
241*387f9dfdSAndroid Build Coastguard Worker  """Returns True if `value` is a revision, False if it is a file, or dies."""
242*387f9dfdSAndroid Build Coastguard Worker  # If `value` is ambiguous (neither a commit nor a file), the following
243*387f9dfdSAndroid Build Coastguard Worker  # command will die with an appropriate error message.
244*387f9dfdSAndroid Build Coastguard Worker  run('git', 'rev-parse', value, verbose=False)
245*387f9dfdSAndroid Build Coastguard Worker  object_type = get_object_type(value)
246*387f9dfdSAndroid Build Coastguard Worker  if object_type is None:
247*387f9dfdSAndroid Build Coastguard Worker    return False
248*387f9dfdSAndroid Build Coastguard Worker  if object_type in ('commit', 'tag'):
249*387f9dfdSAndroid Build Coastguard Worker    return True
250*387f9dfdSAndroid Build Coastguard Worker  die('`%s` is a %s, but a commit or filename was expected' %
251*387f9dfdSAndroid Build Coastguard Worker      (value, object_type))
252*387f9dfdSAndroid Build Coastguard Worker
253*387f9dfdSAndroid Build Coastguard Worker
254*387f9dfdSAndroid Build Coastguard Workerdef get_object_type(value):
255*387f9dfdSAndroid Build Coastguard Worker  """Returns a string description of an object's type, or None if it is not
256*387f9dfdSAndroid Build Coastguard Worker  a valid git object."""
257*387f9dfdSAndroid Build Coastguard Worker  cmd = ['git', 'cat-file', '-t', value]
258*387f9dfdSAndroid Build Coastguard Worker  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
259*387f9dfdSAndroid Build Coastguard Worker  stdout, stderr = p.communicate()
260*387f9dfdSAndroid Build Coastguard Worker  if p.returncode != 0:
261*387f9dfdSAndroid Build Coastguard Worker    return None
262*387f9dfdSAndroid Build Coastguard Worker  return convert_string(stdout.strip())
263*387f9dfdSAndroid Build Coastguard Worker
264*387f9dfdSAndroid Build Coastguard Worker
265*387f9dfdSAndroid Build Coastguard Workerdef compute_diff_and_extract_lines(commits, files):
266*387f9dfdSAndroid Build Coastguard Worker  """Calls compute_diff() followed by extract_lines()."""
267*387f9dfdSAndroid Build Coastguard Worker  diff_process = compute_diff(commits, files)
268*387f9dfdSAndroid Build Coastguard Worker  changed_lines = extract_lines(diff_process.stdout)
269*387f9dfdSAndroid Build Coastguard Worker  diff_process.stdout.close()
270*387f9dfdSAndroid Build Coastguard Worker  diff_process.wait()
271*387f9dfdSAndroid Build Coastguard Worker  if diff_process.returncode != 0:
272*387f9dfdSAndroid Build Coastguard Worker    # Assume error was already printed to stderr.
273*387f9dfdSAndroid Build Coastguard Worker    sys.exit(2)
274*387f9dfdSAndroid Build Coastguard Worker  return changed_lines
275*387f9dfdSAndroid Build Coastguard Worker
276*387f9dfdSAndroid Build Coastguard Worker
277*387f9dfdSAndroid Build Coastguard Workerdef compute_diff(commits, files):
278*387f9dfdSAndroid Build Coastguard Worker  """Return a subprocess object producing the diff from `commits`.
279*387f9dfdSAndroid Build Coastguard Worker
280*387f9dfdSAndroid Build Coastguard Worker  The return value's `stdin` file object will produce a patch with the
281*387f9dfdSAndroid Build Coastguard Worker  differences between the working directory and the first commit if a single
282*387f9dfdSAndroid Build Coastguard Worker  one was specified, or the difference between both specified commits, filtered
283*387f9dfdSAndroid Build Coastguard Worker  on `files` (if non-empty).  Zero context lines are used in the patch."""
284*387f9dfdSAndroid Build Coastguard Worker  git_tool = 'diff-index'
285*387f9dfdSAndroid Build Coastguard Worker  if len(commits) > 1:
286*387f9dfdSAndroid Build Coastguard Worker    git_tool = 'diff-tree'
287*387f9dfdSAndroid Build Coastguard Worker  cmd = ['git', git_tool, '-p', '-U0'] + commits + ['--']
288*387f9dfdSAndroid Build Coastguard Worker  cmd.extend(files)
289*387f9dfdSAndroid Build Coastguard Worker  p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
290*387f9dfdSAndroid Build Coastguard Worker  p.stdin.close()
291*387f9dfdSAndroid Build Coastguard Worker  return p
292*387f9dfdSAndroid Build Coastguard Worker
293*387f9dfdSAndroid Build Coastguard Worker
294*387f9dfdSAndroid Build Coastguard Workerdef extract_lines(patch_file):
295*387f9dfdSAndroid Build Coastguard Worker  """Extract the changed lines in `patch_file`.
296*387f9dfdSAndroid Build Coastguard Worker
297*387f9dfdSAndroid Build Coastguard Worker  The return value is a dictionary mapping filename to a list of (start_line,
298*387f9dfdSAndroid Build Coastguard Worker  line_count) pairs.
299*387f9dfdSAndroid Build Coastguard Worker
300*387f9dfdSAndroid Build Coastguard Worker  The input must have been produced with ``-U0``, meaning unidiff format with
301*387f9dfdSAndroid Build Coastguard Worker  zero lines of context.  The return value is a dict mapping filename to a
302*387f9dfdSAndroid Build Coastguard Worker  list of line `Range`s."""
303*387f9dfdSAndroid Build Coastguard Worker  matches = {}
304*387f9dfdSAndroid Build Coastguard Worker  for line in patch_file:
305*387f9dfdSAndroid Build Coastguard Worker    line = convert_string(line)
306*387f9dfdSAndroid Build Coastguard Worker    match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
307*387f9dfdSAndroid Build Coastguard Worker    if match:
308*387f9dfdSAndroid Build Coastguard Worker      filename = match.group(1).rstrip('\r\n')
309*387f9dfdSAndroid Build Coastguard Worker    match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
310*387f9dfdSAndroid Build Coastguard Worker    if match:
311*387f9dfdSAndroid Build Coastguard Worker      start_line = int(match.group(1))
312*387f9dfdSAndroid Build Coastguard Worker      line_count = 1
313*387f9dfdSAndroid Build Coastguard Worker      if match.group(3):
314*387f9dfdSAndroid Build Coastguard Worker        line_count = int(match.group(3))
315*387f9dfdSAndroid Build Coastguard Worker      if line_count > 0:
316*387f9dfdSAndroid Build Coastguard Worker        matches.setdefault(filename, []).append(Range(start_line, line_count))
317*387f9dfdSAndroid Build Coastguard Worker  return matches
318*387f9dfdSAndroid Build Coastguard Worker
319*387f9dfdSAndroid Build Coastguard Worker
320*387f9dfdSAndroid Build Coastguard Workerdef filter_by_extension(dictionary, allowed_extensions):
321*387f9dfdSAndroid Build Coastguard Worker  """Delete every key in `dictionary` that doesn't have an allowed extension.
322*387f9dfdSAndroid Build Coastguard Worker
323*387f9dfdSAndroid Build Coastguard Worker  `allowed_extensions` must be a collection of lowercase file extensions,
324*387f9dfdSAndroid Build Coastguard Worker  excluding the period."""
325*387f9dfdSAndroid Build Coastguard Worker  allowed_extensions = frozenset(allowed_extensions)
326*387f9dfdSAndroid Build Coastguard Worker  for filename in list(dictionary.keys()):
327*387f9dfdSAndroid Build Coastguard Worker    base_ext = filename.rsplit('.', 1)
328*387f9dfdSAndroid Build Coastguard Worker    if len(base_ext) == 1 and '' in allowed_extensions:
329*387f9dfdSAndroid Build Coastguard Worker        continue
330*387f9dfdSAndroid Build Coastguard Worker    if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
331*387f9dfdSAndroid Build Coastguard Worker      del dictionary[filename]
332*387f9dfdSAndroid Build Coastguard Worker
333*387f9dfdSAndroid Build Coastguard Worker
334*387f9dfdSAndroid Build Coastguard Workerdef cd_to_toplevel():
335*387f9dfdSAndroid Build Coastguard Worker  """Change to the top level of the git repository."""
336*387f9dfdSAndroid Build Coastguard Worker  toplevel = run('git', 'rev-parse', '--show-toplevel')
337*387f9dfdSAndroid Build Coastguard Worker  os.chdir(toplevel)
338*387f9dfdSAndroid Build Coastguard Worker
339*387f9dfdSAndroid Build Coastguard Worker
340*387f9dfdSAndroid Build Coastguard Workerdef create_tree_from_workdir(filenames):
341*387f9dfdSAndroid Build Coastguard Worker  """Create a new git tree with the given files from the working directory.
342*387f9dfdSAndroid Build Coastguard Worker
343*387f9dfdSAndroid Build Coastguard Worker  Returns the object ID (SHA-1) of the created tree."""
344*387f9dfdSAndroid Build Coastguard Worker  return create_tree(filenames, '--stdin')
345*387f9dfdSAndroid Build Coastguard Worker
346*387f9dfdSAndroid Build Coastguard Worker
347*387f9dfdSAndroid Build Coastguard Workerdef run_clang_format_and_save_to_tree(changed_lines, revision=None,
348*387f9dfdSAndroid Build Coastguard Worker                                      binary='clang-format', style=None):
349*387f9dfdSAndroid Build Coastguard Worker  """Run clang-format on each file and save the result to a git tree.
350*387f9dfdSAndroid Build Coastguard Worker
351*387f9dfdSAndroid Build Coastguard Worker  Returns the object ID (SHA-1) of the created tree."""
352*387f9dfdSAndroid Build Coastguard Worker  def iteritems(container):
353*387f9dfdSAndroid Build Coastguard Worker      try:
354*387f9dfdSAndroid Build Coastguard Worker          return container.iteritems() # Python 2
355*387f9dfdSAndroid Build Coastguard Worker      except AttributeError:
356*387f9dfdSAndroid Build Coastguard Worker          return container.items() # Python 3
357*387f9dfdSAndroid Build Coastguard Worker  def index_info_generator():
358*387f9dfdSAndroid Build Coastguard Worker    for filename, line_ranges in iteritems(changed_lines):
359*387f9dfdSAndroid Build Coastguard Worker      if revision:
360*387f9dfdSAndroid Build Coastguard Worker        git_metadata_cmd = ['git', 'ls-tree',
361*387f9dfdSAndroid Build Coastguard Worker                            '%s:%s' % (revision, os.path.dirname(filename)),
362*387f9dfdSAndroid Build Coastguard Worker                            os.path.basename(filename)]
363*387f9dfdSAndroid Build Coastguard Worker        git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE,
364*387f9dfdSAndroid Build Coastguard Worker                                        stdout=subprocess.PIPE)
365*387f9dfdSAndroid Build Coastguard Worker        stdout = git_metadata.communicate()[0]
366*387f9dfdSAndroid Build Coastguard Worker        mode = oct(int(stdout.split()[0], 8))
367*387f9dfdSAndroid Build Coastguard Worker      else:
368*387f9dfdSAndroid Build Coastguard Worker        mode = oct(os.stat(filename).st_mode)
369*387f9dfdSAndroid Build Coastguard Worker      # Adjust python3 octal format so that it matches what git expects
370*387f9dfdSAndroid Build Coastguard Worker      if mode.startswith('0o'):
371*387f9dfdSAndroid Build Coastguard Worker          mode = '0' + mode[2:]
372*387f9dfdSAndroid Build Coastguard Worker      blob_id = clang_format_to_blob(filename, line_ranges,
373*387f9dfdSAndroid Build Coastguard Worker                                     revision=revision,
374*387f9dfdSAndroid Build Coastguard Worker                                     binary=binary,
375*387f9dfdSAndroid Build Coastguard Worker                                     style=style)
376*387f9dfdSAndroid Build Coastguard Worker      yield '%s %s\t%s' % (mode, blob_id, filename)
377*387f9dfdSAndroid Build Coastguard Worker  return create_tree(index_info_generator(), '--index-info')
378*387f9dfdSAndroid Build Coastguard Worker
379*387f9dfdSAndroid Build Coastguard Worker
380*387f9dfdSAndroid Build Coastguard Workerdef create_tree(input_lines, mode):
381*387f9dfdSAndroid Build Coastguard Worker  """Create a tree object from the given input.
382*387f9dfdSAndroid Build Coastguard Worker
383*387f9dfdSAndroid Build Coastguard Worker  If mode is '--stdin', it must be a list of filenames.  If mode is
384*387f9dfdSAndroid Build Coastguard Worker  '--index-info' is must be a list of values suitable for "git update-index
385*387f9dfdSAndroid Build Coastguard Worker  --index-info", such as "<mode> <SP> <sha1> <TAB> <filename>".  Any other mode
386*387f9dfdSAndroid Build Coastguard Worker  is invalid."""
387*387f9dfdSAndroid Build Coastguard Worker  assert mode in ('--stdin', '--index-info')
388*387f9dfdSAndroid Build Coastguard Worker  cmd = ['git', 'update-index', '--add', '-z', mode]
389*387f9dfdSAndroid Build Coastguard Worker  with temporary_index_file():
390*387f9dfdSAndroid Build Coastguard Worker    p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
391*387f9dfdSAndroid Build Coastguard Worker    for line in input_lines:
392*387f9dfdSAndroid Build Coastguard Worker      p.stdin.write(to_bytes('%s\0' % line))
393*387f9dfdSAndroid Build Coastguard Worker    p.stdin.close()
394*387f9dfdSAndroid Build Coastguard Worker    if p.wait() != 0:
395*387f9dfdSAndroid Build Coastguard Worker      die('`%s` failed' % ' '.join(cmd))
396*387f9dfdSAndroid Build Coastguard Worker    tree_id = run('git', 'write-tree')
397*387f9dfdSAndroid Build Coastguard Worker    return tree_id
398*387f9dfdSAndroid Build Coastguard Worker
399*387f9dfdSAndroid Build Coastguard Worker
400*387f9dfdSAndroid Build Coastguard Workerdef clang_format_to_blob(filename, line_ranges, revision=None,
401*387f9dfdSAndroid Build Coastguard Worker                         binary='clang-format', style=None):
402*387f9dfdSAndroid Build Coastguard Worker  """Run clang-format on the given file and save the result to a git blob.
403*387f9dfdSAndroid Build Coastguard Worker
404*387f9dfdSAndroid Build Coastguard Worker  Runs on the file in `revision` if not None, or on the file in the working
405*387f9dfdSAndroid Build Coastguard Worker  directory if `revision` is None.
406*387f9dfdSAndroid Build Coastguard Worker
407*387f9dfdSAndroid Build Coastguard Worker  Returns the object ID (SHA-1) of the created blob."""
408*387f9dfdSAndroid Build Coastguard Worker  clang_format_cmd = [binary]
409*387f9dfdSAndroid Build Coastguard Worker  if style:
410*387f9dfdSAndroid Build Coastguard Worker    clang_format_cmd.extend(['-style='+style])
411*387f9dfdSAndroid Build Coastguard Worker  clang_format_cmd.extend([
412*387f9dfdSAndroid Build Coastguard Worker      '-lines=%s:%s' % (start_line, start_line+line_count-1)
413*387f9dfdSAndroid Build Coastguard Worker      for start_line, line_count in line_ranges])
414*387f9dfdSAndroid Build Coastguard Worker  if revision:
415*387f9dfdSAndroid Build Coastguard Worker    clang_format_cmd.extend(['-assume-filename='+filename])
416*387f9dfdSAndroid Build Coastguard Worker    git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)]
417*387f9dfdSAndroid Build Coastguard Worker    git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE,
418*387f9dfdSAndroid Build Coastguard Worker                                stdout=subprocess.PIPE)
419*387f9dfdSAndroid Build Coastguard Worker    git_show.stdin.close()
420*387f9dfdSAndroid Build Coastguard Worker    clang_format_stdin = git_show.stdout
421*387f9dfdSAndroid Build Coastguard Worker  else:
422*387f9dfdSAndroid Build Coastguard Worker    clang_format_cmd.extend([filename])
423*387f9dfdSAndroid Build Coastguard Worker    git_show = None
424*387f9dfdSAndroid Build Coastguard Worker    clang_format_stdin = subprocess.PIPE
425*387f9dfdSAndroid Build Coastguard Worker  try:
426*387f9dfdSAndroid Build Coastguard Worker    clang_format = subprocess.Popen(clang_format_cmd, stdin=clang_format_stdin,
427*387f9dfdSAndroid Build Coastguard Worker                                    stdout=subprocess.PIPE)
428*387f9dfdSAndroid Build Coastguard Worker    if clang_format_stdin == subprocess.PIPE:
429*387f9dfdSAndroid Build Coastguard Worker      clang_format_stdin = clang_format.stdin
430*387f9dfdSAndroid Build Coastguard Worker  except OSError as e:
431*387f9dfdSAndroid Build Coastguard Worker    if e.errno == errno.ENOENT:
432*387f9dfdSAndroid Build Coastguard Worker      die('cannot find executable "%s"' % binary)
433*387f9dfdSAndroid Build Coastguard Worker    else:
434*387f9dfdSAndroid Build Coastguard Worker      raise
435*387f9dfdSAndroid Build Coastguard Worker  clang_format_stdin.close()
436*387f9dfdSAndroid Build Coastguard Worker  hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin']
437*387f9dfdSAndroid Build Coastguard Worker  hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout,
438*387f9dfdSAndroid Build Coastguard Worker                                 stdout=subprocess.PIPE)
439*387f9dfdSAndroid Build Coastguard Worker  clang_format.stdout.close()
440*387f9dfdSAndroid Build Coastguard Worker  stdout = hash_object.communicate()[0]
441*387f9dfdSAndroid Build Coastguard Worker  if hash_object.returncode != 0:
442*387f9dfdSAndroid Build Coastguard Worker    die('`%s` failed' % ' '.join(hash_object_cmd))
443*387f9dfdSAndroid Build Coastguard Worker  if clang_format.wait() != 0:
444*387f9dfdSAndroid Build Coastguard Worker    die('`%s` failed' % ' '.join(clang_format_cmd))
445*387f9dfdSAndroid Build Coastguard Worker  if git_show and git_show.wait() != 0:
446*387f9dfdSAndroid Build Coastguard Worker    die('`%s` failed' % ' '.join(git_show_cmd))
447*387f9dfdSAndroid Build Coastguard Worker  return convert_string(stdout).rstrip('\r\n')
448*387f9dfdSAndroid Build Coastguard Worker
449*387f9dfdSAndroid Build Coastguard Worker
450*387f9dfdSAndroid Build Coastguard Worker@contextlib.contextmanager
451*387f9dfdSAndroid Build Coastguard Workerdef temporary_index_file(tree=None):
452*387f9dfdSAndroid Build Coastguard Worker  """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting
453*387f9dfdSAndroid Build Coastguard Worker  the file afterward."""
454*387f9dfdSAndroid Build Coastguard Worker  index_path = create_temporary_index(tree)
455*387f9dfdSAndroid Build Coastguard Worker  old_index_path = os.environ.get('GIT_INDEX_FILE')
456*387f9dfdSAndroid Build Coastguard Worker  os.environ['GIT_INDEX_FILE'] = index_path
457*387f9dfdSAndroid Build Coastguard Worker  try:
458*387f9dfdSAndroid Build Coastguard Worker    yield
459*387f9dfdSAndroid Build Coastguard Worker  finally:
460*387f9dfdSAndroid Build Coastguard Worker    if old_index_path is None:
461*387f9dfdSAndroid Build Coastguard Worker      del os.environ['GIT_INDEX_FILE']
462*387f9dfdSAndroid Build Coastguard Worker    else:
463*387f9dfdSAndroid Build Coastguard Worker      os.environ['GIT_INDEX_FILE'] = old_index_path
464*387f9dfdSAndroid Build Coastguard Worker    os.remove(index_path)
465*387f9dfdSAndroid Build Coastguard Worker
466*387f9dfdSAndroid Build Coastguard Worker
467*387f9dfdSAndroid Build Coastguard Workerdef create_temporary_index(tree=None):
468*387f9dfdSAndroid Build Coastguard Worker  """Create a temporary index file and return the created file's path.
469*387f9dfdSAndroid Build Coastguard Worker
470*387f9dfdSAndroid Build Coastguard Worker  If `tree` is not None, use that as the tree to read in.  Otherwise, an
471*387f9dfdSAndroid Build Coastguard Worker  empty index is created."""
472*387f9dfdSAndroid Build Coastguard Worker  gitdir = run('git', 'rev-parse', '--git-dir')
473*387f9dfdSAndroid Build Coastguard Worker  path = os.path.join(gitdir, temp_index_basename)
474*387f9dfdSAndroid Build Coastguard Worker  if tree is None:
475*387f9dfdSAndroid Build Coastguard Worker    tree = '--empty'
476*387f9dfdSAndroid Build Coastguard Worker  run('git', 'read-tree', '--index-output='+path, tree)
477*387f9dfdSAndroid Build Coastguard Worker  return path
478*387f9dfdSAndroid Build Coastguard Worker
479*387f9dfdSAndroid Build Coastguard Worker
480*387f9dfdSAndroid Build Coastguard Workerdef print_diff(old_tree, new_tree):
481*387f9dfdSAndroid Build Coastguard Worker  """Print the diff between the two trees to stdout."""
482*387f9dfdSAndroid Build Coastguard Worker  # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
483*387f9dfdSAndroid Build Coastguard Worker  # is expected to be viewed by the user, and only the former does nice things
484*387f9dfdSAndroid Build Coastguard Worker  # like color and pagination.
485*387f9dfdSAndroid Build Coastguard Worker  #
486*387f9dfdSAndroid Build Coastguard Worker  # We also only print modified files since `new_tree` only contains the files
487*387f9dfdSAndroid Build Coastguard Worker  # that were modified, so unmodified files would show as deleted without the
488*387f9dfdSAndroid Build Coastguard Worker  # filter.
489*387f9dfdSAndroid Build Coastguard Worker  subprocess.check_call(['git', 'diff', '--diff-filter=M', old_tree, new_tree,
490*387f9dfdSAndroid Build Coastguard Worker                         '--'])
491*387f9dfdSAndroid Build Coastguard Worker
492*387f9dfdSAndroid Build Coastguard Worker
493*387f9dfdSAndroid Build Coastguard Workerdef apply_changes(old_tree, new_tree, force=False, patch_mode=False):
494*387f9dfdSAndroid Build Coastguard Worker  """Apply the changes in `new_tree` to the working directory.
495*387f9dfdSAndroid Build Coastguard Worker
496*387f9dfdSAndroid Build Coastguard Worker  Bails if there are local changes in those files and not `force`.  If
497*387f9dfdSAndroid Build Coastguard Worker  `patch_mode`, runs `git checkout --patch` to select hunks interactively."""
498*387f9dfdSAndroid Build Coastguard Worker  changed_files = run('git', 'diff-tree', '--diff-filter=M', '-r', '-z',
499*387f9dfdSAndroid Build Coastguard Worker                      '--name-only', old_tree,
500*387f9dfdSAndroid Build Coastguard Worker                      new_tree).rstrip('\0').split('\0')
501*387f9dfdSAndroid Build Coastguard Worker  if not force:
502*387f9dfdSAndroid Build Coastguard Worker    unstaged_files = run('git', 'diff-files', '--name-status', *changed_files)
503*387f9dfdSAndroid Build Coastguard Worker    if unstaged_files:
504*387f9dfdSAndroid Build Coastguard Worker      print('The following files would be modified but '
505*387f9dfdSAndroid Build Coastguard Worker                'have unstaged changes:', file=sys.stderr)
506*387f9dfdSAndroid Build Coastguard Worker      print(unstaged_files, file=sys.stderr)
507*387f9dfdSAndroid Build Coastguard Worker      print('Please commit, stage, or stash them first.', file=sys.stderr)
508*387f9dfdSAndroid Build Coastguard Worker      sys.exit(2)
509*387f9dfdSAndroid Build Coastguard Worker  if patch_mode:
510*387f9dfdSAndroid Build Coastguard Worker    # In patch mode, we could just as well create an index from the new tree
511*387f9dfdSAndroid Build Coastguard Worker    # and checkout from that, but then the user will be presented with a
512*387f9dfdSAndroid Build Coastguard Worker    # message saying "Discard ... from worktree".  Instead, we use the old
513*387f9dfdSAndroid Build Coastguard Worker    # tree as the index and checkout from new_tree, which gives the slightly
514*387f9dfdSAndroid Build Coastguard Worker    # better message, "Apply ... to index and worktree".  This is not quite
515*387f9dfdSAndroid Build Coastguard Worker    # right, since it won't be applied to the user's index, but oh well.
516*387f9dfdSAndroid Build Coastguard Worker    with temporary_index_file(old_tree):
517*387f9dfdSAndroid Build Coastguard Worker      subprocess.check_call(['git', 'checkout', '--patch', new_tree])
518*387f9dfdSAndroid Build Coastguard Worker    index_tree = old_tree
519*387f9dfdSAndroid Build Coastguard Worker  else:
520*387f9dfdSAndroid Build Coastguard Worker    with temporary_index_file(new_tree):
521*387f9dfdSAndroid Build Coastguard Worker      run('git', 'checkout-index', '-a', '-f')
522*387f9dfdSAndroid Build Coastguard Worker  return changed_files
523*387f9dfdSAndroid Build Coastguard Worker
524*387f9dfdSAndroid Build Coastguard Worker
525*387f9dfdSAndroid Build Coastguard Workerdef run(*args, **kwargs):
526*387f9dfdSAndroid Build Coastguard Worker  stdin = kwargs.pop('stdin', '')
527*387f9dfdSAndroid Build Coastguard Worker  verbose = kwargs.pop('verbose', True)
528*387f9dfdSAndroid Build Coastguard Worker  strip = kwargs.pop('strip', True)
529*387f9dfdSAndroid Build Coastguard Worker  for name in kwargs:
530*387f9dfdSAndroid Build Coastguard Worker    raise TypeError("run() got an unexpected keyword argument '%s'" % name)
531*387f9dfdSAndroid Build Coastguard Worker  p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
532*387f9dfdSAndroid Build Coastguard Worker                       stdin=subprocess.PIPE)
533*387f9dfdSAndroid Build Coastguard Worker  stdout, stderr = p.communicate(input=stdin)
534*387f9dfdSAndroid Build Coastguard Worker
535*387f9dfdSAndroid Build Coastguard Worker  stdout = convert_string(stdout)
536*387f9dfdSAndroid Build Coastguard Worker  stderr = convert_string(stderr)
537*387f9dfdSAndroid Build Coastguard Worker
538*387f9dfdSAndroid Build Coastguard Worker  if p.returncode == 0:
539*387f9dfdSAndroid Build Coastguard Worker    if stderr:
540*387f9dfdSAndroid Build Coastguard Worker      if verbose:
541*387f9dfdSAndroid Build Coastguard Worker        print('`%s` printed to stderr:' % ' '.join(args), file=sys.stderr)
542*387f9dfdSAndroid Build Coastguard Worker      print(stderr.rstrip(), file=sys.stderr)
543*387f9dfdSAndroid Build Coastguard Worker    if strip:
544*387f9dfdSAndroid Build Coastguard Worker      stdout = stdout.rstrip('\r\n')
545*387f9dfdSAndroid Build Coastguard Worker    return stdout
546*387f9dfdSAndroid Build Coastguard Worker  if verbose:
547*387f9dfdSAndroid Build Coastguard Worker    print('`%s` returned %s' % (' '.join(args), p.returncode), file=sys.stderr)
548*387f9dfdSAndroid Build Coastguard Worker  if stderr:
549*387f9dfdSAndroid Build Coastguard Worker    print(stderr.rstrip(), file=sys.stderr)
550*387f9dfdSAndroid Build Coastguard Worker  sys.exit(2)
551*387f9dfdSAndroid Build Coastguard Worker
552*387f9dfdSAndroid Build Coastguard Worker
553*387f9dfdSAndroid Build Coastguard Workerdef die(message):
554*387f9dfdSAndroid Build Coastguard Worker  print('error:', message, file=sys.stderr)
555*387f9dfdSAndroid Build Coastguard Worker  sys.exit(2)
556*387f9dfdSAndroid Build Coastguard Worker
557*387f9dfdSAndroid Build Coastguard Worker
558*387f9dfdSAndroid Build Coastguard Workerdef to_bytes(str_input):
559*387f9dfdSAndroid Build Coastguard Worker    # Encode to UTF-8 to get binary data.
560*387f9dfdSAndroid Build Coastguard Worker    if isinstance(str_input, bytes):
561*387f9dfdSAndroid Build Coastguard Worker        return str_input
562*387f9dfdSAndroid Build Coastguard Worker    return str_input.encode('utf-8')
563*387f9dfdSAndroid Build Coastguard Worker
564*387f9dfdSAndroid Build Coastguard Worker
565*387f9dfdSAndroid Build Coastguard Workerdef to_string(bytes_input):
566*387f9dfdSAndroid Build Coastguard Worker    if isinstance(bytes_input, str):
567*387f9dfdSAndroid Build Coastguard Worker        return bytes_input
568*387f9dfdSAndroid Build Coastguard Worker    return bytes_input.encode('utf-8')
569*387f9dfdSAndroid Build Coastguard Worker
570*387f9dfdSAndroid Build Coastguard Worker
571*387f9dfdSAndroid Build Coastguard Workerdef convert_string(bytes_input):
572*387f9dfdSAndroid Build Coastguard Worker    try:
573*387f9dfdSAndroid Build Coastguard Worker        return to_string(bytes_input.decode('utf-8'))
574*387f9dfdSAndroid Build Coastguard Worker    except AttributeError: # 'str' object has no attribute 'decode'.
575*387f9dfdSAndroid Build Coastguard Worker        return str(bytes_input)
576*387f9dfdSAndroid Build Coastguard Worker    except UnicodeError:
577*387f9dfdSAndroid Build Coastguard Worker        return str(bytes_input)
578*387f9dfdSAndroid Build Coastguard Worker
579*387f9dfdSAndroid Build Coastguard Workerif __name__ == '__main__':
580*387f9dfdSAndroid Build Coastguard Worker  main()
581