| #!/usr/bin/env python3 | 
 | # Copyright (c) 2023, 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 re | 
 | try: | 
 |     import resource | 
 | except ImportError: | 
 |     # Not a Unix system. Do what Gandalf tells you not to. | 
 |     pass | 
 | import shutil | 
 | import subprocess | 
 | import sys | 
 |  | 
 | import jdk | 
 | import utils | 
 |  | 
 | ARCHIVE_BUCKET = 'r8-releases' | 
 | REPO = 'https://github.com/google/smali' | 
 | NO_DRYRUN_OUTPUT = object() | 
 |  | 
 |  | 
 | def checkout(temp): | 
 |     subprocess.check_call(['git', 'clone', REPO, temp]) | 
 |     return temp | 
 |  | 
 |  | 
 | def parse_options(): | 
 |     result = argparse.ArgumentParser(description='Release Smali') | 
 |     result.add_argument('--version', | 
 |                         metavar=('<version>'), | 
 |                         help='The version of smali to archive.') | 
 |     result.add_argument('--dry-run', | 
 |                         '--dry_run', | 
 |                         nargs='?', | 
 |                         help='Build only, no upload.', | 
 |                         metavar='<output directory>', | 
 |                         default=None, | 
 |                         const=NO_DRYRUN_OUTPUT) | 
 |     result.add_argument('--checkout', help='Use existing checkout.') | 
 |     return result.parse_args() | 
 |  | 
 |  | 
 | def set_rlimit_to_max(): | 
 |     (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE) | 
 |     resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard)) | 
 |  | 
 |  | 
 | def Main(): | 
 |     options = parse_options() | 
 |     if not utils.is_bot() and not options.dry_run: | 
 |         raise Exception('You are not a bot, don\'t archive builds. ' + | 
 |                         'Use --dry-run to test locally') | 
 |     if options.checkout and not options.dry_run: | 
 |         raise Exception('Using local checkout is only allowed with --dry-run') | 
 |     if not options.checkout and not options.version: | 
 |         raise Exception( | 
 |             'Option --version is required (when not using local checkout)') | 
 |  | 
 |     if utils.is_bot() and not utils.IsWindows(): | 
 |         set_rlimit_to_max() | 
 |  | 
 |     utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE) | 
 |     with utils.TempDir() as temp: | 
 |         # Resolve dry run location to support relative directories. | 
 |         dry_run_output = None | 
 |         if options.dry_run and options.dry_run != NO_DRYRUN_OUTPUT: | 
 |             if not os.path.isdir(options.dry_run): | 
 |                 os.mkdir(options.dry_run) | 
 |             dry_run_output = os.path.abspath(options.dry_run) | 
 |  | 
 |         checkout_dir = options.checkout if options.checkout else checkout(temp) | 
 |         with utils.ChangedWorkingDirectory(checkout_dir): | 
 |             if options.version: | 
 |                 output = subprocess.check_output( | 
 |                     ['git', 'tag', '-l', options.version]) | 
 |                 if len(output) == 0: | 
 |                     raise Exception( | 
 |                         'Repository does not have a release tag for version %s' | 
 |                         % options.version) | 
 |                 subprocess.check_call(['git', 'checkout', options.version]) | 
 |  | 
 |             # Find version from `build.gradle`. | 
 |             for line in open(os.path.join('build.gradle'), 'r'): | 
 |                 result = re.match(r'^version = \'(\d+)\.(\d+)\.(\d+)\'', line) | 
 |                 if result: | 
 |                     break | 
 |             version = '%s.%s.%s' % (result.group(1), result.group(2), | 
 |                                     result.group(3)) | 
 |             if options.version and version != options.version: | 
 |                 message = 'version %s, expected version %s' % (version, | 
 |                                                                options.version) | 
 |                 if (options.checkout): | 
 |                     raise Exception('Checkout %s has %s' % | 
 |                                     (options.checkout, message)) | 
 |                 else: | 
 |                     raise Exception('Tag % has %s' % (options.version, message)) | 
 |  | 
 |             print('Building version: %s' % version) | 
 |  | 
 |             # Build release to local Maven repository compiling with JDK-11. | 
 |             m2 = os.path.join(temp, 'm2') | 
 |             os.mkdir(m2) | 
 |             subprocess.check_call([ | 
 |                 './gradlew', | 
 |                 '-Dorg.gradle.java.home=%s' % jdk.GetJdk11Home(), | 
 |                 '-Dmaven.repo.local=%s' % m2, 'release', 'test', | 
 |                 'publishToMavenLocal', | 
 |             ]) | 
 |             base = os.path.join('com', 'android', 'tools', 'smali') | 
 |  | 
 |             # Check that the local maven repository only has the single version directory in | 
 |             # each artifact directory. | 
 |             for name in [ | 
 |                     'smali-util', 'smali-dexlib2', 'smali', 'smali-baksmali' | 
 |             ]: | 
 |                 dirnames = next(os.walk(os.path.join(m2, base, name)), | 
 |                                 (None, None, []))[1] | 
 |                 if not dirnames or len(dirnames) != 1 or dirnames[0] != version: | 
 |                     raise Exception('Found unexpected directory %s in %s' % | 
 |                                     (dirnames, name)) | 
 |  | 
 |             # Build an archive with the relevant content of the local maven repository. | 
 |             m2_filtered = os.path.join(temp, 'm2_filtered') | 
 |             shutil.copytree( | 
 |                 m2, | 
 |                 m2_filtered, | 
 |                 ignore=shutil.ignore_patterns('maven-metadata-local.xml')) | 
 |             maven_release_archive = shutil.make_archive( | 
 |                 'smali-maven-release-%s' % version, 'zip', m2_filtered, base) | 
 |  | 
 |             # Collect names of the fat jars. | 
 |             fat_jars = list( | 
 |                 map(lambda prefix: '%s-%s-fat.jar' % (prefix, version), | 
 |                     ['smali/build/libs/smali', 'baksmali/build/libs/baksmali'])) | 
 |  | 
 |             # Copy artifacts. | 
 |             files = [maven_release_archive] | 
 |             files.extend(fat_jars) | 
 |             if options.dry_run: | 
 |                 if dry_run_output: | 
 |                     print('Dry run, not actually uploading. Copying to %s:' % | 
 |                           dry_run_output) | 
 |                     for file in files: | 
 |                         destination = os.path.join(dry_run_output, | 
 |                                                    os.path.basename(file)) | 
 |                         shutil.copyfile(file, destination) | 
 |                         print("  %s" % destination) | 
 |                 else: | 
 |                     print('Dry run, not actually uploading. Generated files:') | 
 |                     for file in files: | 
 |                         print("  %s" % os.path.basename(file)) | 
 |             else: | 
 |                 destination_prefix = 'gs://%s/smali/%s' % (ARCHIVE_BUCKET, | 
 |                                                            version) | 
 |                 if utils.cloud_storage_exists(destination_prefix): | 
 |                     raise Exception( | 
 |                         'Target archive directory %s already exists' % | 
 |                         destination_prefix) | 
 |                 for file in files: | 
 |                     destination = '%s/%s' % (destination_prefix, | 
 |                                              os.path.basename(file)) | 
 |                     if utils.cloud_storage_exists(destination): | 
 |                         raise Exception('Target %s already exists' % | 
 |                                         destination) | 
 |                     utils.upload_file_to_cloud_storage(file, destination) | 
 |                 public_url = 'https://storage.googleapis.com/%s/smali/%s' % ( | 
 |                     ARCHIVE_BUCKET, version) | 
 |                 print('Artifacts available at: %s' % public_url) | 
 |  | 
 |     print("Done!") | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     sys.exit(Main()) |