Generate a Maven artefact with the desugar library configuration

This also add the version to the JSON configuration format.

Test: tools/archive.py --dry-run --dry-run-output ooo
Bug: 134732760
Change-Id: Ia5060f0c91afe0e904fdbf70742c6db5cc785727
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
index f43b696..cb63a44 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
@@ -1,5 +1,6 @@
 {
-  "version": 1,
+  "configuration_format_version": 1,
+  "version": "0.1.0",
   "required_compilation_api_level": 26,
   "library_flags": [
     {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs_comments.md b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs_comments.md
index 3e0a514..db105b8 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs_comments.md
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs_comments.md
@@ -2,21 +2,24 @@
 
 ## Version
 
-The first field `version` encodes a versioning number internal to R8/D8 in the
-form of an unsigned integer. It allows R8/D8 to know if the file given is supported.
+The first field `configuration_format_version` encodes a versioning number internal to R8/D8
+in the form of an unsigned integer. It allows R8/D8 to know if the file given is supported.
 Non-backward compatible changes to the desugared library increase the version number, and such
 library cannot be compiled without upgrading R8/D8 to the latest version.
 
+The second field `version` holds the version of the content for the configuration. This number
+must be updated each time the configuration is changed.
+
 ## Required compilation API level
 
-The second field `required_compilation_api_level` encodes the minimal Android API level required for
+The third field `required_compilation_api_level` encodes the minimal Android API level required for
 the desugared library to be compiled correctly. If the API of library used for compilation of the
 library or a program using the library is lower than this level, one has to upgrade the SDK version
 used to be able to use desugared libraries.
 
 ## Library and program flags
 
-The third and last fields are `library_flags` and `program_flags`. They include the set of flags
+The fourth and last fields are `library_flags` and `program_flags`. They include the set of flags
 required for respectively the library and the program using the desugared library compilation. The
 sets of flags are different depending on the min API level used. The flags are in a list, where
 each list entry specifies up to which min API level the set of flags should be applied. During
diff --git a/tools/archive.py b/tools/archive.py
index 15b0d23..32c5c32 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -121,9 +121,14 @@
   if utils.is_bot():
     SetRLimitToMax()
   PrintResourceInfo()
+
   # Create maven release which uses a build that exclude dependencies.
-  create_maven_release.run(utils.MAVEN_ZIP)
-  create_maven_release.run(utils.MAVEN_ZIP_LIB, is_r8lib=True)
+  create_maven_release.generate_r8_maven_zip(utils.MAVEN_ZIP)
+  create_maven_release.generate_r8_maven_zip(
+      utils.MAVEN_ZIP_LIB, is_r8lib=True)
+  # Create maven release of the desuage_jdk_libs configuration.
+  create_maven_release.generate_desugar_configuration_maven_zip(
+      utils.DESUGAR_CONFIGURATION_MAVEN_ZIP)
 
   # Generate and copy a full build without dependencies.
   gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
@@ -185,6 +190,8 @@
       utils.COMPATPROGUARDLIB_JAR + '.map',
       utils.MAVEN_ZIP,
       utils.MAVEN_ZIP_LIB,
+      utils.DESUGAR_CONFIGURATION,
+      utils.DESUGAR_CONFIGURATION_MAVEN_ZIP,
       utils.GENERATED_LICENSE,
     ]:
       file_name = os.path.basename(file)
@@ -199,23 +206,46 @@
         if options.dry_run_output:
           dry_run_destination = os.path.join(options.dry_run_output, file_name)
           print('Dry run, not actually uploading. Copying to '
-            + dry_run_destination)
+              + dry_run_destination)
           shutil.copyfile(tagged_jar, dry_run_destination)
         else:
           print('Dry run, not actually uploading')
       else:
         utils.upload_file_to_cloud_storage(tagged_jar, destination)
         print('File available at: %s' % GetUrl(version, file_name, is_master))
+
+      # Upload R8 to a maven compatible location.
       if file == utils.R8_JAR:
-        # Upload R8 to a maven compatible location.
         maven_dst = GetUploadDestination(utils.get_maven_path('r8', version),
                                          'r8-%s.jar' % version, is_master)
         if options.dry_run:
-          print('Dry run, not actually creating maven repo')
+          print('Dry run, not actually creating maven repo for R8')
         else:
           utils.upload_file_to_cloud_storage(tagged_jar, maven_dst)
           print('Maven repo root available at: %s' % GetMavenUrl(is_master))
 
+      # Upload desugar_jdk_libs configuration to a maven compatible location.
+      if file == utils.DESUGAR_CONFIGURATION:
+        jar_name = 'desugar_jdk_libs_configuration-%s.jar' % version
+        maven_dst = GetUploadDestination(
+            utils.get_maven_path('desugar_jdk_libs_configuration', version),
+                                 jar_name, is_master)
+
+        with utils.TempDir() as tmp_dir:
+          desugar_jdk_libs_configuration_jar = os.path.join(tmp_dir, jar_name)
+          create_maven_release.generate_jar_with_desugar_configuration(
+              utils.DESUGAR_CONFIGURATION, desugar_jdk_libs_configuration_jar)
+
+          if options.dry_run:
+            print('Dry run, not actually creating maven repo for '
+                + 'desugar configuration.')
+            shutil.copyfile(
+                desugar_jdk_libs_configuration_jar,
+                os.path.join(options.dry_run_output, jar_name))
+          else:
+            utils.upload_file_to_cloud_storage(
+                desugar_jdk_libs_configuration_jar, maven_dst)
+            print('Maven repo root available at: %s' % GetMavenUrl(is_master))
 
 if __name__ == '__main__':
   sys.exit(Main())
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 2a66424..b4343f3 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -6,9 +6,10 @@
 import argparse
 import gradle
 import hashlib
+import json
 from os import makedirs
 from os.path import join
-from shutil import copyfile, make_archive, rmtree
+from shutil import copyfile, make_archive, move, rmtree
 import subprocess
 import sys
 from string import Template
@@ -31,7 +32,7 @@
       <distribution>repo</distribution>
     </license>""")
 
-POMTEMPLATE = Template(
+R8_POMTEMPLATE = Template(
 """<project
     xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -71,11 +72,56 @@
 </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>desugar_jdk_libs_configuration</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>
+  <dependencies>
+    <groupId>com.android.tools</groupId>
+    <artifactId>desugar_jdk_libs</artifactId>
+    <version>1.0.0</version>
+  </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>
+""")
+
 def parse_options(argv):
   result = argparse.ArgumentParser()
   result.add_argument('--out', help='The zip file to output')
   result.add_argument('--r8lib', action='store_true',
                       help='Build r8 with dependencies included shrunken')
+  result.add_argument('--desugar-configuration', action='store_true',
+                      help='Build r8 with dependencies included shrunken')
   return result.parse_args(argv)
 
 def determine_version():
@@ -211,10 +257,8 @@
         group=group, artifact=artifact, version=version)
   return result
 
-def write_pom_file(version, pom_file, exclude_dependencies):
-  dependencies = "" if exclude_dependencies else generate_dependencies()
-  library_licenses = generate_library_licenses() if exclude_dependencies else ""
-  version_pom = POMTEMPLATE.substitute(
+def write_pom_file(template, pom_file, version, dependencies='', library_licenses=''):
+  version_pom = template.substitute(
       version=version, dependencies=dependencies, library_licenses=library_licenses)
   with open(pom_file, 'w') as file:
     file.write(version_pom)
@@ -239,40 +283,91 @@
   with (open(file + '.sha1', 'w')) as file:
     file.write(hexdigest)
 
-def run(out, is_r8lib=False):
-  if out == None:
-    print 'Need to supply output zip with --out.'
-    exit(1)
-  # Build the R8 no deps artifact.
-  if not is_r8lib:
-    gradle.RunGradleExcludeDeps([utils.R8])
-  else:
-    gradle.RunGradle([utils.R8LIB, '-Pno_internal'])
-  # Create directory structure for this version.
-  version = determine_version()
+def generate_maven_zip(name, version, pom_file, jar_file, out):
   with utils.TempDir() as tmp_dir:
-    version_dir = join(tmp_dir, utils.get_maven_path('r8', version))
+    # 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 = join(version_dir, 'r8-' + version + '.pom')
-    write_pom_file(version, pom_file, is_r8lib)
-    # Copy the jar to the output.
-    target_jar = join(version_dir, 'r8-' + version + '.jar')
-    copyfile(utils.R8LIB_JAR if is_r8lib else utils.R8_JAR, target_jar)
+    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(target_jar)
-    write_md5_for(pom_file)
-    write_sha1_for(target_jar)
-    write_sha1_for(pom_file)
+    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):
+  # Build the R8 no deps artifact.
+  if not is_r8lib:
+    gradle.RunGradleExcludeDeps([utils.R8])
+  else:
+    gradle.RunGradle([utils.R8LIB, '-Pno_internal'])
+
+  version = determine_version()
+  # Generate the pom file.
+  pom_file = 'r8.pom'
+  write_pom_file(
+      R8_POMTEMPLATE,
+      pom_file,
+      version,
+      "" if is_r8lib else generate_dependencies(),
+      generate_library_licenses() if is_r8lib else "")
+  # Write the maven zip file.
+  generate_maven_zip(
+      'r8',
+      version,
+      pom_file,
+      utils.R8LIB_JAR if is_r8lib else utils.R8_JAR,
+      out)
+
+# Write the desugaring configuration of a jar file with the following content:
+#  META-INF
+#    desugar
+#      d8
+#        desugar.json
+def generate_jar_with_desugar_configuration(configuration, destination):
+  with utils.TempDir() as tmp_dir:
+    configuration_dir = join(tmp_dir, 'META-INF', 'desugar', 'd8')
+    makedirs(configuration_dir)
+    copyfile(configuration, join(configuration_dir, 'desugar.json'))
+    make_archive(destination, 'zip', tmp_dir)
+    move(destination + '.zip', destination)
+
+# Generate the maven zip for the configuration to desugar desugar_jdk_libs.
+def generate_desugar_configuration_maven_zip(out):
+  version = utils.desugar_configuration_version()
+  # Generate the pom file.
+  pom_file = 'desugar_configuration.pom'
+  write_pom_file(DESUGAR_CONFIGUATION_POMTEMPLATE, pom_file, version)
+  # Generate the jar with the configuration file.
+  jar_file = 'desugar_configuration.jar'
+  generate_jar_with_desugar_configuration(
+      utils.DESUGAR_CONFIGURATION, jar_file)
+  # Write the maven zip file.
+  generate_maven_zip(
+      'desugar_jdk_libs_configuration', version, pom_file, jar_file, out)
+
+
 def main(argv):
   options = parse_options(argv)
-  out = options.out
-  run(out, options.r8lib)
+  if options.r8lib and options.desugar_configuration:
+    raise Exception(
+        'Cannot combine options --r8lib and --desugar-configuration')
+  if options.out == None:
+    raise Exception(
+        'Need to supply output zip with --out.')
+  if options.desugar_configuration:
+    generate_desugar_configuration_maven_zip(options.out)
+  else:
+    generate_r8_maven_zip(options.out, options.r8lib)
 
 if __name__ == "__main__":
   exit(main(sys.argv[1:]))
diff --git a/tools/utils.py b/tools/utils.py
index 5305272..72cdbd4 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -5,6 +5,7 @@
 # Different utility functions used accross scripts
 
 import hashlib
+import json
 import os
 import re
 import shutil
@@ -32,6 +33,7 @@
 LIBS = os.path.join(BUILD, 'libs')
 GENERATED_LICENSE_DIR = os.path.join(BUILD, 'generatedLicense')
 SRC_ROOT = os.path.join(REPO_ROOT, 'src', 'main', 'java')
+TEST_ROOT = os.path.join(REPO_ROOT, 'src', 'test', 'java')
 
 D8 = 'd8'
 R8 = 'r8'
@@ -55,6 +57,18 @@
 COMPATPROGUARDLIB_JAR = os.path.join(LIBS, 'compatproguardlib.jar')
 MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
 MAVEN_ZIP_LIB = os.path.join(LIBS, 'r8lib.zip')
+# TODO(b/134732760): The JSON configuration should be moved.
+DESUGAR_CONFIGURATION = os.path.join(
+      TEST_ROOT,
+      'com',
+      'android',
+      'tools',
+      'r8',
+      'desugar',
+      'corelib',
+      'desugar_jdk_libs.json')
+DESUGAR_CONFIGURATION_MAVEN_ZIP = os.path.join(
+  LIBS, 'desugar_jdk_libs_configuration.zip')
 GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
 RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
 R8LIB_KEEP_RULES = os.path.join(REPO_ROOT, 'src/main/keep.txt')
@@ -530,3 +544,18 @@
   output = subprocess.check_output(cmd, stderr = subprocess.STDOUT)
   # output is on form 'R8 <version>' so we just strip of 'R8 '.
   return output.splitlines()[0][3:]
+
+def desugar_configuration_version():
+  with open(DESUGAR_CONFIGURATION, 'r') as f:
+    configuration_json = json.loads(f.read())
+    configuration_format_version = \
+        configuration_json.get('configuration_format_version')
+    if (configuration_format_version != 1):
+      raise Exception(
+          'Unsupported "configuration_format_version" '
+              + configuration_format_version)
+    version = configuration_json.get('version')
+    if not version:
+      raise Exception(
+          'No "version" found in ' + utils.DESUGAR_CONFIGURATION)
+    return version