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