blob: d90ea426599dfb40083f9ce53a08d057f2266b76 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright (c) 2017, 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 hashlib
import jdk
import subprocess
import sys
import zipfile
from os import makedirs
from os.path import basename
from os.path import join
from shutil import copyfile
from shutil import make_archive
from shutil import move
from string import Template
import gradle
import utils
LICENSETEMPLATE = Template("""
<license>
<name>$name</name>
<url>$url</url>
<distribution>repo</distribution>
</license>""")
R8_POMTEMPLATE = Template("""<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.android.tools</groupId>
<artifactId>r8</artifactId>
<version>$version</version>
<name>D8 dexer and R8 shrinker</name>
<description>
D8 dexer and R8 shrinker.
</description>
<url>http://r8.googlesource.com/r8</url>
<inceptionYear>2016</inceptionYear>
<licenses>
<license>
<name>BSD-3-Clause</name>
<url>https://opensource.org/licenses/BSD-3-Clause</url>
<distribution>repo</distribution>
</license>$library_licenses
</licenses>
<dependencies>
</dependencies>
<developers>
<developer>
<name>The Android Open Source Project</name>
</developer>
</developers>
<scm>
<connection>
https://r8.googlesource.com/r8.git
</connection>
<url>
https://r8.googlesource.com/r8
</url>
</scm>
</project>
""")
DESUGAR_CONFIGUATION_POMTEMPLATE = Template("""<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.android.tools</groupId>
<artifactId>$artifactId</artifactId>
<version>$version</version>
<name>D8 configuration to desugar desugar_jdk_libs</name>
<description>
D8 configuration to desugar desugar_jdk_libs.
</description>
<url>http://r8.googlesource.com/r8</url>
<inceptionYear>2019</inceptionYear>
<licenses>
<license>
<name>BSD-3-Clause</name>
<url>https://opensource.org/licenses/BSD-3-Clause</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>The Android Open Source Project</name>
</developer>
</developers>
<scm>
<connection>
https://r8.googlesource.com/r8.git
</connection>
<url>
https://r8.googlesource.com/r8
</url>
</scm>
</project>
""")
def parse_options(argv):
result = argparse.ArgumentParser()
result.add_argument('--out', help='The zip file to output')
group = result.add_mutually_exclusive_group()
group.add_argument(
'--desugar-configuration',
action='store_true',
help='Build desugar library configuration (original JDK-8)')
group.add_argument(
'--desugar-configuration-jdk8',
action='store_true',
help='Build desugar library configuration (original JDK-8)')
group.add_argument(
'--desugar-configuration-jdk11-legacy',
action='store_true',
help='Build desugar library configuration (JDK-11 legacy)')
group.add_argument(
'--desugar-configuration-jdk11-minimal',
action='store_true',
help='Build desugar library configuration (JDK-11 minimal)')
group.add_argument('--desugar-configuration-jdk11',
action='store_true',
help='Build desugar library configuration (JDK-11)')
group.add_argument('--desugar-configuration-jdk11-nio',
action='store_true',
help='Build desugar library configuration (JDK-11 nio)')
return result.parse_args(argv)
def determine_version():
version_file = join(utils.SRC_ROOT, 'com', 'android', 'tools', 'r8',
'Version.java')
with open(version_file, 'r') as file:
for line in file:
if 'final String LABEL ' in line:
result = line[line.find('"') + 1:]
result = result[:result.find('"')]
return result
raise Exception('Unable to determine version.')
def generate_library_licenses():
artifact_prefix = '- artifact: '
license_prefix = 'license: '
licenses = []
license_url_prefix = 'licenseUrl: '
license_urls = []
# The ./LIBRARY-LICENSE file is a simple yaml file, which for each dependency
# has the following information:
#
# - artifact: <maven artifact> // in the form <group-id>:<artifact-id>:+
# name: <name of dependency>
# copyrightHolder: <name of copyright holder>
# license: <license name>
# licenseUrl: <url to license test>
#
# E.g. for Guava:
#
# - artifact: com.google.guava:guava:+
# name: Guava Google Core Libraries for Java
# copyrightHolder: The Guava Authors
# license: The Apache Software License, Version 2.0
# licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
#
# This file should always be up to date as the build will fail if it
# is does not have information for all dependencies.
with open('LIBRARY-LICENSE', 'r') as file:
name = None
url = None
for line in file:
trimmed = line.strip()
# Collect license name and url for each artifact. They must come in
# pairs for each artifact.
if trimmed.startswith(artifact_prefix):
assert not name
assert not url
if trimmed.startswith(license_prefix):
name = trimmed[len(license_prefix):]
if trimmed.startswith(license_url_prefix):
url = trimmed[len(license_url_prefix):]
# Licenses come in name/url pairs. When both are present add pair
# to collected licenses if either name or url has not been recorded yet,
# as some licenses with slightly different names point to the same url.
if name and url:
if (not name in licenses) or (not url in license_urls):
licenses.append(name)
license_urls.append(url)
name = None
url = None
assert len(licenses) == len(license_urls)
result = ''
for i in range(len(licenses)):
name = licenses[i]
url = license_urls[i]
result += LICENSETEMPLATE.substitute(name=name, url=url)
return result
def write_default_r8_pom_file(pom_file, version):
write_pom_file(R8_POMTEMPLATE, pom_file, version)
def write_pom_file(template,
pom_file,
version,
artifact_id=None,
library_licenses=''):
version_pom = (template.substitute(artifactId=artifact_id,
version=version,
library_licenses=library_licenses)
if artifact_id else template.substitute(
version=version, library_licenses=library_licenses))
with open(pom_file, 'w') as file:
file.write(version_pom)
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 generate_maven_zip(name, version, pom_file, jar_file, out):
with utils.TempDir() as tmp_dir:
# Create the base maven version directory
version_dir = join(tmp_dir, utils.get_maven_path(name, version))
makedirs(version_dir)
# Write the pom file.
pom_file_location = join(version_dir, name + '-' + version + '.pom')
copyfile(pom_file, pom_file_location)
# Write the jar file.
jar_file_location = join(version_dir, name + '-' + version + '.jar')
copyfile(jar_file, jar_file_location)
# Create check sums.
write_md5_for(jar_file_location)
write_md5_for(pom_file_location)
write_sha1_for(jar_file_location)
write_sha1_for(pom_file_location)
# Zip it up - make_archive will append zip to the file, so remove.
assert out.endswith('.zip')
base_no_zip = out[0:len(out) - 4]
make_archive(base_no_zip, 'zip', tmp_dir)
def generate_r8_maven_zip(out, version_file=None, skip_gradle_build=False):
if not skip_gradle_build:
gradle.RunGradle([utils.GRADLE_TASK_R8LIB, '-Pno_internal'])
version = determine_version()
with utils.TempDir() as tmp_dir:
file_copy = join(tmp_dir, 'copy_of_jar.jar')
copyfile(utils.R8LIB_JAR, file_copy)
if version_file:
with zipfile.ZipFile(file_copy, 'a') as zip:
zip.write(version_file, basename(version_file))
# Generate the pom file.
pom_file = join(tmp_dir, 'r8.pom')
write_pom_file(R8_POMTEMPLATE,
pom_file,
version,
library_licenses=generate_library_licenses())
# Write the maven zip file.
generate_maven_zip('r8', version, pom_file, file_copy, out)
# Write the desugaring configuration of a jar file with the following content:
# java/
# util/
# <java.util conversions classes>
# time/
# <java.time conversions classes>
# META-INF/
# desugar/
# d8/
# desugar.json
# lint/
# <lint files>
def generate_jar_with_desugar_configuration(configuration, implementation,
conversions, destination):
with utils.TempDir() as tmp_dir:
# Add conversion classes.
with zipfile.ZipFile(conversions, 'r') as conversions_zip:
conversions_zip.extractall(tmp_dir)
# Add configuration.
configuration_dir = join(tmp_dir, 'META-INF', 'desugar', 'd8')
makedirs(configuration_dir)
copyfile(configuration, join(configuration_dir, 'desugar.json'))
# Add lint configuartion.
lint_dir = join(configuration_dir, 'lint')
makedirs(lint_dir)
cmd = [
jdk.GetJavaExecutable(), '-cp', utils.R8_JAR,
'com.android.tools.r8.ir.desugar.desugaredlibrary.lint.GenerateDesugaredLibraryLintFiles',
configuration, implementation, lint_dir,
utils.get_android_jar(34)
]
utils.PrintCmd(cmd)
subprocess.check_call(cmd)
# Add LICENSE file.
copyfile(join(utils.REPO_ROOT, 'LICENSE'), join(tmp_dir, 'LICENSE'))
make_archive(destination, 'zip', tmp_dir)
move(destination + '.zip', destination)
def convert_desugar_configuration(configuration, conversions, implementation,
machine_configuration):
cmd = [
jdk.GetJavaExecutable(), '-cp', utils.R8_JAR,
'com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.DesugaredLibraryConverter',
configuration, implementation, conversions,
utils.get_android_jar(33), machine_configuration
]
subprocess.check_call(cmd)
# Generate the maven zip for the configuration to desugar desugar_jdk_libs.
def generate_desugar_configuration_maven_zip(out, configuration, implementation,
conversions):
with utils.TempDir() as tmp_dir:
(name, version) = utils.desugar_configuration_name_and_version(
configuration, False)
if (not version.startswith("1.")):
machine_configuration = join(tmp_dir, "machine.json")
convert_desugar_configuration(configuration, conversions,
implementation, machine_configuration)
configuration = machine_configuration
# Generate the pom file.
pom_file = join(tmp_dir, 'desugar_configuration.pom')
write_pom_file(DESUGAR_CONFIGUATION_POMTEMPLATE,
pom_file,
version,
artifact_id=name)
# Generate the jar with the configuration file.
jar_file = join(tmp_dir, 'desugar_configuration.jar')
generate_jar_with_desugar_configuration(configuration, implementation,
conversions, jar_file)
# Write the maven zip file.
generate_maven_zip(name, version, pom_file, jar_file, out)
def main(argv):
options = parse_options(argv)
if options.out == None:
raise Exception('Need to supply output zip with --out.')
if options.desugar_configuration or options.desugar_configuration_jdk8:
generate_desugar_configuration_maven_zip(
options.out, utils.DESUGAR_CONFIGURATION,
utils.DESUGAR_IMPLEMENTATION,
utils.LIBRARY_DESUGAR_CONVERSIONS_LEGACY_ZIP)
elif options.desugar_configuration_jdk11_legacy:
generate_desugar_configuration_maven_zip(
options.out, utils.DESUGAR_CONFIGURATION_JDK11_LEGACY,
utils.DESUGAR_IMPLEMENTATION_JDK11,
utils.LIBRARY_DESUGAR_CONVERSIONS_LEGACY_ZIP)
elif options.desugar_configuration_jdk11_minimal:
generate_desugar_configuration_maven_zip(
options.out, utils.DESUGAR_CONFIGURATION_JDK11_MINIMAL,
utils.DESUGAR_IMPLEMENTATION_JDK11,
utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP)
elif options.desugar_configuration_jdk11:
generate_desugar_configuration_maven_zip(
options.out, utils.DESUGAR_CONFIGURATION_JDK11,
utils.DESUGAR_IMPLEMENTATION_JDK11,
utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP)
elif options.desugar_configuration_jdk11_nio:
generate_desugar_configuration_maven_zip(
options.out, utils.DESUGAR_CONFIGURATION_JDK11_NIO,
utils.DESUGAR_IMPLEMENTATION_JDK11,
utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP)
else:
generate_r8_maven_zip(options.out)
if __name__ == "__main__":
exit(main(sys.argv[1:]))