|  | #!/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 gradle | 
|  | import hashlib | 
|  | import jdk | 
|  | import json | 
|  | from os import makedirs | 
|  | from os.path import join, basename | 
|  | from shutil import copyfile, make_archive, move, rmtree | 
|  | import subprocess | 
|  | import sys | 
|  | from string import Template | 
|  | import tempfile | 
|  | import utils | 
|  | import zipfile | 
|  |  | 
|  | DEPENDENCYTEMPLATE = Template( | 
|  | """ | 
|  | <dependency> | 
|  | <groupId>$group</groupId> | 
|  | <artifactId>$artifact</artifactId> | 
|  | <version>$version</version> | 
|  | </dependency>""") | 
|  |  | 
|  | 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 | 
|  | </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('--r8lib', action='store_true', | 
|  | help='Build r8 with dependencies included shrunken') | 
|  | 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)') | 
|  | 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 | 
|  |  | 
|  |  | 
|  | # Generate the dependencies block for the pom file. | 
|  | # | 
|  | # We ask gradle to list all dependencies. In that output | 
|  | # we locate the runtimeClasspath block for 'main' which | 
|  | # looks something like: | 
|  | # | 
|  | # runtimeClasspath - Runtime classpath of source set 'main'. | 
|  | # +--- net.sf.jopt-simple:jopt-simple:4.6 | 
|  | # +--- com.googlecode.json-simple:json-simple:1.1 | 
|  | # +--- com.google.guava:guava:23.0 | 
|  | # +--- it.unimi.dsi:fastutil:7.2.0 | 
|  | # +--- org.ow2.asm:asm:6.0 | 
|  | # +--- org.ow2.asm:asm-commons:6.0 | 
|  | # |    \--- org.ow2.asm:asm-tree:6.0 | 
|  | # |         \--- org.ow2.asm:asm:6.0 | 
|  | # +--- org.ow2.asm:asm-tree:6.0 (*) | 
|  | # +--- org.ow2.asm:asm-analysis:6.0 | 
|  | # |    \--- org.ow2.asm:asm-tree:6.0 (*) | 
|  | # \--- org.ow2.asm:asm-util:6.0 | 
|  | #      \--- org.ow2.asm:asm-tree:6.0 (*) | 
|  | # | 
|  | # We filter out the repeats that are marked by '(*)'. | 
|  | # | 
|  | # For each remaining line, we remove the junk at the start | 
|  | # in chunks. As an example: | 
|  | # | 
|  | # '  |    \--- org.ow2.asm:asm-tree:6.0  '  --strip--> | 
|  | # '|    \--- org.ow2.asm:asm-tree:6.0'  --> | 
|  | # '\--- org.ow2.asm:asm-tree:6.0'  --> | 
|  | # 'org.ow2.asm:asm-tree:6.0' | 
|  | # | 
|  | # The end result is the dependency we are looking for: | 
|  | # | 
|  | # groupId: org.ow2.asm | 
|  | # artifact: asm-tree | 
|  | # version: 6.0 | 
|  | def generate_dependencies(): | 
|  | dependencies = gradle.RunGradleGetOutput(['dependencies']) | 
|  | dependency_lines = [] | 
|  | collect = False | 
|  | for line in dependencies.splitlines(): | 
|  | if line and 'runtimeClasspath' in line and "'main'" in line: | 
|  | collect = True | 
|  | continue | 
|  | if collect: | 
|  | if not len(line) == 0: | 
|  | if not '(*)' in line: | 
|  | trimmed = line.strip() | 
|  | while trimmed.find(' ') != -1: | 
|  | trimmed = trimmed[trimmed.find(' ') + 1:].strip() | 
|  | if not trimmed in dependency_lines: | 
|  | dependency_lines.append(trimmed) | 
|  | else: | 
|  | break | 
|  | result = '' | 
|  | for dep in dependency_lines: | 
|  | components = dep.split(':') | 
|  | assert len(components) == 3 | 
|  | group = components[0] | 
|  | artifact = components[1] | 
|  | version = components[2] | 
|  | result += DEPENDENCYTEMPLATE.substitute( | 
|  | group=group, artifact=artifact, version=version) | 
|  | return result | 
|  |  | 
|  | def write_default_r8_pom_file(pom_file, version): | 
|  | write_pom_file(R8_POMTEMPLATE, pom_file, version, dependencies=generate_dependencies()) | 
|  |  | 
|  | def write_pom_file( | 
|  | template, pom_file, version, artifact_id=None, dependencies='', library_licenses=''): | 
|  | version_pom = ( | 
|  | template.substitute( | 
|  | artifactId=artifact_id, | 
|  | version=version, | 
|  | dependencies=dependencies, | 
|  | library_licenses=library_licenses) | 
|  | if artifact_id else | 
|  | template.substitute( | 
|  | version=version, dependencies=dependencies, 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, is_r8lib=False, version_file=None, skip_gradle_build=False): | 
|  | # Build the R8 no deps artifact. | 
|  | if not skip_gradle_build: | 
|  | if not is_r8lib: | 
|  | gradle.RunGradleExcludeDeps([utils.R8]) | 
|  | else: | 
|  | gradle.RunGradle([utils.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 if is_r8lib else utils.R8_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, | 
|  | dependencies='' if is_r8lib else generate_dependencies(), | 
|  | library_licenses=generate_library_licenses() if is_r8lib else '') | 
|  | # 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.GenerateLintFiles', | 
|  | configuration, | 
|  | implementation, | 
|  | lint_dir] | 
|  | utils.PrintCmd(cmd) | 
|  | subprocess.check_call(cmd) | 
|  |  | 
|  | 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) | 
|  | elif options.desugar_configuration_jdk11_legacy: | 
|  | generate_desugar_configuration_maven_zip( | 
|  | options.out, utils.DESUGAR_CONFIGURATION_JDK11_LEGACY, | 
|  | utils.DESUGAR_IMPLEMENTATION_JDK11) | 
|  | else: | 
|  | generate_r8_maven_zip(options.out, options.r8lib) | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | exit(main(sys.argv[1:])) |