| #!/usr/bin/env python3 | 
 | # Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file | 
 | # for details. All rights reserved. Use of this source code is governed by a | 
 | # BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | import argparse | 
 | import os | 
 | import subprocess | 
 | import sys | 
 | import utils | 
 |  | 
 | VERSION_FILE = 'src/main/java/com/android/tools/r8/Version.java' | 
 | VERSION_PREFIX = 'String LABEL = "' | 
 |  | 
 | def parse_options(): | 
 |   parser = argparse.ArgumentParser(description='Release r8') | 
 |   parser.add_argument('--branch', | 
 |                       metavar=('<branch>'), | 
 |                       help='Branch to cherry-pick to') | 
 |   parser.add_argument('--current-checkout', '--current_checkout', | 
 |                       default=False, | 
 |                       action='store_true', | 
 |                       help='Perform cherry picks into the current checkout') | 
 |   parser.add_argument('--no-upload', '--no_upload', | 
 |                       default=False, | 
 |                       action='store_true', | 
 |                       help='Do not upload to Gerrit') | 
 |   parser.add_argument('hashes', metavar='<hash>', nargs='+', | 
 |                     help='Hashed to merge') | 
 |  | 
 |   return parser.parse_args() | 
 |  | 
 |  | 
 | def run(args): | 
 |   # Checkout the branch. | 
 |   subprocess.check_output(['git', 'checkout', args.branch]) | 
 |  | 
 |   if (args.current_checkout): | 
 |     for i in range(len(args.hashes) + 1): | 
 |       branch = 'cherry-%d' % (i + 1) | 
 |       print('Deleting branch %s' % branch) | 
 |       subprocess.run(['git', 'branch', branch, '-D']) | 
 |  | 
 |   bugs = set() | 
 |  | 
 |   count = 1 | 
 |   for hash in args.hashes: | 
 |     branch = 'cherry-%d' % count | 
 |     print('Cherry-picking %s in %s' % (hash, branch)) | 
 |     if (count == 1): | 
 |       subprocess.run(['git', 'new-branch', branch, '--upstream',  'origin/%s' % args.branch]) | 
 |     else: | 
 |       subprocess.run(['git', 'new-branch', branch, '--upstream-current']) | 
 |  | 
 |     subprocess.run(['git', 'cherry-pick', hash]) | 
 |     confirm_and_upload(branch, args, bugs) | 
 |     count = count + 1 | 
 |  | 
 |   branch = 'cherry-%d' % count | 
 |   subprocess.run(['git', 'new-branch', branch, '--upstream-current']) | 
 |  | 
 |   old_version = 'unknown' | 
 |   for line in open(VERSION_FILE, 'r'): | 
 |     index = line.find(VERSION_PREFIX) | 
 |     if index > 0: | 
 |       index += len(VERSION_PREFIX) | 
 |       subline = line[index:] | 
 |       old_version = subline[:subline.index('"')] | 
 |       break | 
 |  | 
 |   new_version = 'unknown' | 
 |   if old_version.find('.') > 0: | 
 |     split_version = old_version.split('.') | 
 |     new_version = '.'.join(split_version[:-1] + [str(int(split_version[-1]) + 1)]) | 
 |     subprocess.run(['sed', '-i', 's/%s/%s/' % (old_version, new_version), VERSION_FILE]) | 
 |   else: | 
 |     editor = os.environ.get('VISUAL') | 
 |     if not editor: | 
 |       editor = os.environ.get('EDITOR') | 
 |     if not editor: | 
 |       editor = 'vi' | 
 |     else: | 
 |       print("Opening %s for version update with %s" % (VERSION_FILE, editor)) | 
 |       subprocess.run([editor, VERSION_FILE]) | 
 |  | 
 |   message = ("Version %s\n\n" % new_version) | 
 |   for bug in sorted(bugs): | 
 |     message += 'Bug: b/%s\n' % bug | 
 |  | 
 |   subprocess.run(['git', 'commit', '-a', '-m', message]) | 
 |   confirm_and_upload(branch, args, None) | 
 |   if (not args.current_checkout): | 
 |     while True: | 
 |       try: | 
 |         answer = input("Type 'delete' to finish and delete checkout in %s: " % os.getcwd()) | 
 |         if answer == 'delete': | 
 |           break | 
 |       except KeyboardInterrupt: | 
 |         pass | 
 |  | 
 | def confirm_and_upload(branch, args, bugs): | 
 |   question = ('Ready to continue (cwd %s, will not upload to Gerrit)' % os.getcwd() | 
 |     if args.no_upload else | 
 |         'Ready to upload %s (cwd %s)' % (branch, os.getcwd())) | 
 |  | 
 |   while True: | 
 |     try: | 
 |       answer = input(question + ' [yes/abort]? ') | 
 |       if answer == 'yes': | 
 |         break | 
 |       if answer == 'abort': | 
 |         print('Aborting new branch for %s' % branch) | 
 |         sys.exit(1) | 
 |     except KeyboardInterrupt: | 
 |       pass | 
 |  | 
 |   # Compute the set of bug refs from the commit message after confirmation. | 
 |   # If done before a conflicting cherry-pick status will potentially include | 
 |   # references that are orthogonal to the pick. | 
 |   if bugs != None: | 
 |     commit_message = subprocess.check_output(['git', 'log', '--format=%B', '-n', '1', 'HEAD']) | 
 |     commit_lines = [l.strip() for l in commit_message.decode('UTF-8').split('\n')] | 
 |     for line in commit_lines: | 
 |       if line.startswith('Bug: '): | 
 |         normalized = line.replace('Bug: ', '').replace('b/', '') | 
 |         if len(normalized) > 0: | 
 |           bugs.add(normalized) | 
 |  | 
 |   if (not args.no_upload): | 
 |     subprocess.run(['git', 'cl', 'upload', '--bypass-hooks']) | 
 |  | 
 | def main(): | 
 |   args = parse_options() | 
 |  | 
 |   if (not args.current_checkout): | 
 |     with utils.TempDir() as temp: | 
 |       print("Performing cherry-picking in %s" % temp) | 
 |       subprocess.check_call(['git', 'clone', utils.REPO_SOURCE, temp]) | 
 |       with utils.ChangedWorkingDirectory(temp): | 
 |         run(args) | 
 |   else: | 
 |     # Run in current directory. | 
 |     print("Performing cherry-picking in %s" % os.getcwd()) | 
 |     subprocess.check_output(['git', 'fetch', 'origin']) | 
 |     run(args) | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(main()) |