blob: fdfb75c830c68a1c097a5c1ff056bf88b3b72f20 [file] [log] [blame]
Søren Gjessee6087bf2023-01-17 10:49:29 +01001#!/usr/bin/env python3
2# Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
3# for details. All rights reserved. Use of this source code is governed by a
4# BSD-style license that can be found in the LICENSE file.
5
6import argparse
7import os
8import re
9try:
10 import resource
11except ImportError:
12 # Not a Unix system. Do what Gandalf tells you not to.
13 pass
14import shutil
15import subprocess
16import sys
17
18import utils
19
20ARCHIVE_BUCKET = 'r8-releases'
21REPO = 'https://github.com/google/smali'
22NO_DRYRUN_OUTPUT = object()
23
24def checkout(temp):
25 subprocess.check_call(['git', 'clone', REPO, temp])
26 return temp
27
28
29def parse_options():
30 result = argparse.ArgumentParser(description='Release Smali')
31 result.add_argument('--archive-hash',
32 required=True,
33 metavar=('<main hash>'),
34 help='The hash to use for archiving a smali build')
35 result.add_argument('--version',
36 required=True,
37 metavar=('<version>'),
38 help='The version of smali to archive.')
39 result.add_argument('--dry-run', '--dry_run',
40 nargs='?',
41 help='Build only, no upload.',
42 metavar='<output directory>',
43 default=None,
44 const=NO_DRYRUN_OUTPUT)
45 result.add_argument('--checkout',
46 help='Use existing checkout.')
47 return result.parse_args()
48
49
50def set_rlimit_to_max():
51 (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
52 resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))
53
54
55def Main():
56 options = parse_options()
57 if not utils.is_bot() and not options.dry_run:
58 raise Exception('You are not a bot, don\'t archive builds. '
59 + 'Use --dry-run to test locally')
60 if options.checkout and not options.dry_run:
61 raise Exception('Using local checkout is only allowed with --dry-run')
62 if not options.checkout and (not options.archive_hash or not options.version):
63 raise Exception('Both --archive-hash and --version are required')
64
65 if utils.is_bot() and not utils.IsWindows():
66 set_rlimit_to_max()
67
68 with utils.TempDir() as temp:
69 # Resolve dry run location to support relative directories.
70 dry_run_output = None
71 if options.dry_run and options.dry_run != NO_DRYRUN_OUTPUT:
72 if not os.path.isdir(options.dry_run):
73 os.mkdir(options.dry_run)
74 dry_run_output = os.path.abspath(options.dry_run)
75
76 checkout_dir = options.checkout if options.checkout else checkout(temp)
77 with utils.ChangedWorkingDirectory(checkout_dir):
78 assert options.archive_hash
79 subprocess.check_call(['git', 'checkout', options.archive_hash])
80
81 # Find version from `build.gradle`.
82 for line in open(os.path.join('build.gradle'), 'r'):
83 result = re.match(
84 r'^version = \'(\d+)\.(\d+)\.(\d+)\'', line)
85 if result:
86 break
87 version = '%s.%s.%s' % (result.group(1), result.group(2), result.group(3))
88 if version != options.version:
89 raise Exception(
90 'Commit % has version %s, expected version %s'
91 % (options.archive_hash, version, options.version))
92 print('Building version: %s' % version)
93
94 # Build release to local Maven repository.
95 m2 = os.path.join(temp, 'm2')
96 os.mkdir(m2)
97 subprocess.check_call(
98 ['./gradlew', '-Dmaven.repo.local=%s' % m2 , 'release', 'publishToMavenLocal'])
99 base = os.path.join('com', 'android', 'tools', 'smali')
100
101 # Check that the local maven repository only has the single version directory in
102 # each artifact directory.
103 for name in ['smali-util', 'smali-dexlib2', 'smali', 'smali-baksmali']:
104 dirnames = next(os.walk(os.path.join(m2, base, name)), (None, None, []))[1]
105 if not dirnames or len(dirnames) != 1 or dirnames[0] != version:
106 raise Exception('Found unexpected directory %s in %s' % (dirnames, name))
107
108 # Build an archive with the relevant content of the local maven repository.
109 m2_filtered = os.path.join(temp, 'm2_filtered')
110 shutil.copytree(m2, m2_filtered, ignore=shutil.ignore_patterns('maven-metadata-local.xml'))
111 maven_release_archive = shutil.make_archive(
112 'smali-maven-release-%s' % version, 'zip', m2_filtered, base)
113
114 # Collect names of the fat jars.
115 fat_jars = list(map(
116 lambda prefix: '%s-%s-fat.jar' % (prefix, version),
117 ['smali/build/libs/smali', 'baksmali/build/libs/baksmali']))
118
119 # Copy artifacts.
120 files = [maven_release_archive]
121 files.extend(fat_jars)
122 if options.dry_run:
123 if dry_run_output:
124 print('Dry run, not actually uploading. Copying to %s:' % dry_run_output)
125 for file in files:
126 destination = os.path.join(dry_run_output, os.path.basename(file))
127 shutil.copyfile(file, destination)
128 print(" %s" % destination)
129 else:
130 print('Dry run, not actually uploading. Generated files:')
131 for file in files:
132 print(" %s" % os.path.basename(file))
133 else:
134 destination_prefix = 'gs://%s/smali/%s' % (ARCHIVE_BUCKET, version)
135 if utils.cloud_storage_exists(destination_prefix):
136 raise Exception('Target archive directory %s already exists' % destination_prefix)
137 for file in files:
138 destination = '%s/%s' % (destination_prefix, os.path.basename(file))
139 if utils.cloud_storage_exists(destination):
140 raise Exception('Target %s already exists' % destination)
141 utils.upload_file_to_cloud_storage(file, destination)
142 public_url = 'https://storage.googleapis.com/%s/smali/%s' % (ARCHIVE_BUCKET, version)
143 print('Artifacts available at: %s' % public_url)
144
145 print("Done!")
146
147if __name__ == '__main__':
148 sys.exit(Main())