blob: ead0bdd1d0fee6d96a8a930f07651536bb967b8e [file] [log] [blame]
#!/usr/bin/env python3
# Copyright (c) 2019, 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.
# This script is designed to run on a buildbot to build from the source
# of https://github.com/google/desugar_jdk_libs and publish to the
# r8-release Cloud Storage Bucket.
#
# These files are uploaded:
#
# raw/desugar_jdk_libs/<VERSION>/desugar_jdk_libs.jar
# raw/desugar_jdk_libs/<VERSION>/desugar_jdk_libs.zip
# raw/com/android/tools/desugar_jdk_libs/<VERSION>/desugar_jdk_libs-<VERSION>.jar
#
# The first two are the raw jar file and the maven compatible zip file. The
# third is the raw jar file placed and named so that the URL
# https://storage.googleapis.com/r8-releases/raw can be treated as a maven
# repository to fetch the artifact com.android.tools:desugar_jdk_libs:1.0.0
import archive
import defines
import git_utils
import gradle
import hashlib
import jdk
import optparse
import os
import re
import shutil
import subprocess
import sys
import utils
import zipfile
VERSION_FILE_JDK8 = 'VERSION.txt'
VERSION_FILE_JDK11_LEGACY = 'VERSION_JDK11_LEGACY.txt'
VERSION_FILE_JDK11_MINIMAL = 'VERSION_JDK11_MINIMAL.txt'
VERSION_FILE_JDK11 = 'VERSION_JDK11.txt'
VERSION_FILE_JDK11_NIO = 'VERSION_JDK11_NIO.txt'
VERSION_MAP = {
'jdk8': VERSION_FILE_JDK8,
'jdk11_legacy': VERSION_FILE_JDK11_LEGACY,
'jdk11_minimal': VERSION_FILE_JDK11_MINIMAL,
'jdk11': VERSION_FILE_JDK11,
'jdk11_nio': VERSION_FILE_JDK11_NIO
}
GITHUB_REPRO = 'desugar_jdk_libs'
BASE_LIBRARY_NAME = 'desugar_jdk_libs'
LIBRARY_NAME_MAP = {
'jdk8': BASE_LIBRARY_NAME,
'jdk11_legacy': BASE_LIBRARY_NAME,
'jdk11_minimal': BASE_LIBRARY_NAME + '_minimal',
'jdk11': BASE_LIBRARY_NAME,
'jdk11_nio': BASE_LIBRARY_NAME + '_nio'
}
MAVEN_RELEASE_TARGET_MAP = {
'jdk8': 'maven_release',
'jdk11_legacy': 'maven_release_jdk11_legacy',
'jdk11_minimal': 'maven_release_jdk11_minimal',
'jdk11': 'maven_release_jdk11',
'jdk11_nio': 'maven_release_jdk11_nio'
}
MAVEN_RELEASE_ZIP = {
'jdk8': BASE_LIBRARY_NAME + '.zip',
'jdk11_legacy': BASE_LIBRARY_NAME + '_jdk11_legacy.zip',
'jdk11_minimal': BASE_LIBRARY_NAME + '_jdk11_minimal.zip',
'jdk11': BASE_LIBRARY_NAME + '_jdk11.zip',
'jdk11_nio': BASE_LIBRARY_NAME + '_jdk11_nio.zip'
}
DESUGAR_JDK_LIBS_HASH_FILE = os.path.join(defines.THIRD_PARTY, 'openjdk',
'desugar_jdk_libs_11',
'desugar_jdk_libs_hash')
def ParseOptions(argv):
result = optparse.OptionParser()
result.add_option(
'--variant',
help="Variant(s) to build",
metavar=('<variants(s)>'),
choices=['jdk8', 'jdk11_legacy', 'jdk11_minimal', 'jdk11', 'jdk11_nio'],
default=[],
action='append')
result.add_option('--dry-run',
'--dry_run',
help='Running on bot, use third_party dependency.',
default=False,
action='store_true')
result.add_option('--dry-run-output',
'--dry_run_output',
help='Output directory for dry run.',
type="string",
action="store")
result.add_option('--github-account',
'--github_account',
help='GitHub account to clone from.',
default="google",
type="string",
action="store")
result.add_option('--build_only',
'--build-only',
help='Build desugared library without archiving.',
type="string",
action="store")
(options, args) = result.parse_args(argv)
return (options, args)
def GetVersion(version_file_name):
with open(version_file_name, 'r') as version_file:
lines = [line.strip() for line in version_file.readlines()]
lines = [line for line in lines if not line.startswith('#')]
if len(lines) != 1:
raise Exception('Version file ' + version_file +
' is expected to have exactly one line')
version = lines[0].strip()
utils.check_basic_semver_version(version,
'in version file ' + version_file_name,
allowPrerelease=True)
return version
def Upload(options, file_name, storage_path, destination, is_main):
print('Uploading %s to %s' % (file_name, destination))
if options.dry_run:
if options.dry_run_output:
dry_run_destination = \
os.path.join(options.dry_run_output, os.path.basename(file_name))
print('Dry run, not actually uploading. Copying to ' +
dry_run_destination)
shutil.copyfile(file_name, dry_run_destination)
else:
print('Dry run, not actually uploading')
else:
utils.upload_file_to_cloud_storage(file_name, destination)
print(
'File available at: %s' %
destination.replace('gs://', 'https://storage.googleapis.com/', 1))
def CloneDesugaredLibrary(github_account, checkout_dir, desugar_jdk_libs_hash):
git_utils.GitClone(
'https://github.com/' + github_account + '/' + GITHUB_REPRO,
checkout_dir)
git_utils.GitCheckout(desugar_jdk_libs_hash, checkout_dir)
def GetJavaEnv(androidHomeTemp):
java_env = dict(os.environ, JAVA_HOME=jdk.GetJdk11Home())
java_env['PATH'] = java_env['PATH'] + os.pathsep + os.path.join(
jdk.GetJdk11Home(), 'bin')
java_env['GRADLE_OPTS'] = '-Xmx1g'
java_env['ANDROID_HOME'] = androidHomeTemp
return java_env
def setUpFakeAndroidHome(androidHomeTemp):
# Bazel will check if 30 is present then extract android.jar from 32.
# We copy android.jar from third_party to mimic repository structure.
subpath = os.path.join(androidHomeTemp, "build-tools")
cmd = ["mkdir", subpath]
subprocess.check_call(cmd)
subpath = os.path.join(subpath, "32.0.0")
cmd = ["mkdir", subpath]
subprocess.check_call(cmd)
subpath = os.path.join(androidHomeTemp, "platforms")
cmd = ["mkdir", subpath]
subprocess.check_call(cmd)
subpath30 = os.path.join(subpath, "android-30")
cmd = ["mkdir", subpath30]
subprocess.check_call(cmd)
subpath = os.path.join(subpath, "android-32")
cmd = ["mkdir", subpath]
subprocess.check_call(cmd)
dest = os.path.join(subpath, "android.jar")
sha = os.path.join(utils.THIRD_PARTY, "android_jar", "lib-v32.tar.gz.sha1")
utils.DownloadFromGoogleCloudStorage(sha)
src = os.path.join(utils.THIRD_PARTY, "android_jar", "lib-v32",
"android.jar")
cmd = ["cp", src, dest]
subprocess.check_call(cmd)
def BuildDesugaredLibrary(checkout_dir, variant, version=None):
if not variant in MAVEN_RELEASE_TARGET_MAP:
raise Exception('Variant ' + variant + ' is not supported')
if variant != 'jdk8' and variant != 'jdk11_legacy' and version is None:
raise Exception('Variant ' + variant +
' require version for undesugaring')
if variant != 'jdk8':
# Hack to workaround b/256723819.
os.remove(
os.path.join(checkout_dir, "jdk11", "src", "java.base", "share",
"classes", "java", "time", "format",
"DesugarDateTimeFormatterBuilder.java"))
with utils.ChangedWorkingDirectory(checkout_dir):
with utils.TempDir() as androidHomeTemp:
setUpFakeAndroidHome(androidHomeTemp)
javaEnv = GetJavaEnv(androidHomeTemp)
bazel = os.path.join(utils.BAZEL_TOOL, 'lib', 'bazel', 'bin',
'bazel')
cmd = [
bazel, '--bazelrc=/dev/null', 'build', '--spawn_strategy=local',
'--verbose_failures', MAVEN_RELEASE_TARGET_MAP[variant]
]
utils.PrintCmd(cmd)
subprocess.check_call(cmd, env=javaEnv)
cmd = [bazel, 'shutdown']
utils.PrintCmd(cmd)
subprocess.check_call(cmd, env=javaEnv)
# Locate the library jar and the maven zip with the jar from the
# bazel build.
if variant == 'jdk8':
library_jar = os.path.join(checkout_dir, 'bazel-bin', 'src',
'share', 'classes', 'java',
'libjava.jar')
else:
# All JDK11 variants use the same library code.
library_jar = os.path.join(checkout_dir, 'bazel-bin', 'jdk11',
'src',
'd8_java_base_selected_with_addon.jar')
maven_zip = os.path.join(checkout_dir, 'bazel-bin',
MAVEN_RELEASE_ZIP[variant])
if variant != 'jdk8' and variant != 'jdk11_legacy':
# The undesugaring is temporary...
undesugared_maven_zip = os.path.join(checkout_dir,
'undesugared_maven')
Undesugar(variant, maven_zip, version, undesugared_maven_zip)
undesugared_maven_zip = os.path.join(checkout_dir,
'undesugared_maven.zip')
return (library_jar, undesugared_maven_zip)
else:
return (library_jar, maven_zip)
def hash_for(file, hash):
with open(file, 'rb') as f:
while True:
# Read chunks of 1MB
chunk = f.read(2**20)
if not chunk:
break
hash.update(chunk)
return hash.hexdigest()
def write_md5_for(file):
hexdigest = hash_for(file, hashlib.md5())
with (open(file + '.md5', 'w')) as file:
file.write(hexdigest)
def write_sha1_for(file):
hexdigest = hash_for(file, hashlib.sha1())
with (open(file + '.sha1', 'w')) as file:
file.write(hexdigest)
def Undesugar(variant, maven_zip, version, undesugared_maven_zip):
gradle.RunGradle([
utils.GRADLE_TASK_R8, utils.GRADLE_TASK_TEST_JAR,
utils.GRADLE_TASK_TEST_DEPS_JAR, '-Pno_internal'
])
with utils.TempDir() as tmp:
with zipfile.ZipFile(maven_zip, 'r') as zip_ref:
zip_ref.extractall(tmp)
desugar_jdk_libs_jar = os.path.join(
tmp, 'com', 'android', 'tools', LIBRARY_NAME_MAP[variant], version,
'%s-%s.jar' % (LIBRARY_NAME_MAP[variant], version))
print(desugar_jdk_libs_jar)
undesugared_jar = os.path.join(tmp, 'undesugared.jar')
buildLibs = os.path.join(defines.REPO_ROOT, 'build', 'libs')
cmd = [
jdk.GetJavaExecutable(), '-cp',
'%s:%s:%s' %
(utils.R8_JAR, utils.R8_TESTS_JAR, utils.R8_TESTS_DEPS_JAR),
'com.android.tools.r8.desugar.desugaredlibrary.jdk11.DesugaredLibraryJDK11Undesugarer',
desugar_jdk_libs_jar, undesugared_jar
]
print(cmd)
try:
output = subprocess.check_output(
cmd, stderr=subprocess.STDOUT).decode('utf-8')
except subprocess.CalledProcessError as e:
print(e)
print(e.output)
raise e
print(output)
# Copy the undesugared jar into place and update the checksums.
shutil.copyfile(undesugared_jar, desugar_jdk_libs_jar)
write_md5_for(desugar_jdk_libs_jar)
write_sha1_for(desugar_jdk_libs_jar)
shutil.make_archive(undesugared_maven_zip, 'zip', tmp)
print(undesugared_maven_zip)
output = subprocess.check_output(
['ls', '-l', os.path.dirname(undesugared_maven_zip)],
stderr=subprocess.STDOUT).decode('utf-8')
print(output)
def MustBeExistingDirectory(path):
if (not os.path.exists(path) or not os.path.isdir(path)):
raise Exception(path + ' does not exist or is not a directory')
def BuildAndUpload(options, variant):
desugar_jdk_libs_hash = ''
with open(DESUGAR_JDK_LIBS_HASH_FILE, 'r') as input_hash:
desugar_jdk_libs_hash = input_hash.readline()
if options.build_only:
with utils.TempDir() as checkout_dir:
CloneDesugaredLibrary(options.github_account, checkout_dir,
desugar_jdk_libs_hash)
(library_jar,
maven_zip) = BuildDesugaredLibrary(checkout_dir, variant,
desugar_jdk_libs_hash)
shutil.copyfile(
library_jar,
os.path.join(options.build_only, os.path.basename(library_jar)))
shutil.copyfile(
maven_zip,
os.path.join(options.build_only, os.path.basename(maven_zip)))
return
# Only handling versioned desugar_jdk_libs.
is_main = False
with utils.TempDir() as checkout_dir:
CloneDesugaredLibrary(options.github_account, checkout_dir,
desugar_jdk_libs_hash)
version = GetVersion(os.path.join(checkout_dir, VERSION_MAP[variant]))
destination = archive.GetVersionDestination(
'gs://', LIBRARY_NAME_MAP[variant] + '/' + version, is_main)
if utils.cloud_storage_exists(destination) and not options.dry_run:
raise Exception('Target archive directory %s already exists' %
destination)
(library_jar,
maven_zip) = BuildDesugaredLibrary(checkout_dir, variant, version)
storage_path = LIBRARY_NAME_MAP[variant] + '/' + version
# Upload the jar file with the library.
destination = archive.GetUploadDestination(
storage_path, LIBRARY_NAME_MAP[variant] + '.jar', is_main)
Upload(options, library_jar, storage_path, destination, is_main)
# Upload the maven zip file with the library.
destination = archive.GetUploadDestination(storage_path,
MAVEN_RELEASE_ZIP[variant],
is_main)
Upload(options, maven_zip, storage_path, destination, is_main)
# Upload the jar file for accessing GCS as a maven repro.
maven_destination = archive.GetUploadDestination(
utils.get_maven_path(LIBRARY_NAME_MAP[variant], version),
'%s-%s.jar' % (LIBRARY_NAME_MAP[variant], version), is_main)
if options.dry_run:
print('Dry run, not actually creating maven repo')
else:
utils.upload_file_to_cloud_storage(library_jar, maven_destination)
print('Maven repo root available at: %s' %
archive.GetMavenUrl(is_main))
def Main(argv):
(options, args) = ParseOptions(argv)
if (len(args) > 0):
raise Exception('Unsupported arguments')
if not utils.is_bot() and not (options.dry_run or options.build_only):
raise Exception('You are not a bot, don\'t archive builds. ' +
'Use --dry-run or --build-only to test locally')
if options.dry_run_output:
MustBeExistingDirectory(options.dry_run_output)
if options.build_only:
MustBeExistingDirectory(options.build_only)
if utils.is_bot():
archive.SetRLimitToMax()
# Make sure bazel is extracted in third_party.
utils.DownloadFromGoogleCloudStorage(utils.BAZEL_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.DESUGAR_JDK_LIBS_11_SHA_FILE)
for v in options.variant:
BuildAndUpload(options, v)
if __name__ == '__main__':
sys.exit(Main(sys.argv[1:]))