Support building of YouTube 16.10

This extends run_on_app.py to build the required desugared library DEX
using tracereferences followed by R8.

Bug: 183704605
Change-Id: I361ce39db94a1fb267c854320d0ab497b8048ea6
diff --git a/build.gradle b/build.gradle
index 30c839c..087686b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -441,7 +441,8 @@
         "youtube/youtube.android_14.44",
         "youtube/youtube.android_15.08",
         "youtube/youtube.android_15.09",
-        "youtube/youtube.android_15.33"
+        "youtube/youtube.android_15.33",
+        "youtube/youtube.android_16.10"
     ],
 ]
 
diff --git a/third_party/youtube/youtube.android_16.10.tar.gz.sha1 b/third_party/youtube/youtube.android_16.10.tar.gz.sha1
new file mode 100644
index 0000000..fd379e8
--- /dev/null
+++ b/third_party/youtube/youtube.android_16.10.tar.gz.sha1
@@ -0,0 +1 @@
+dee8de7ff9e23c5cdce2ae8ac48468f52ab24d4f
\ No newline at end of file
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index 94eae01..9bd13a1 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -8,6 +8,7 @@
 import copy
 import optparse
 import os
+import shutil
 import sys
 import time
 
@@ -450,6 +451,79 @@
         elif trimmed.startswith('-libraryjars'):
           raise Exception("Unexpected -libraryjars found in " + pgconf)
 
+def should_build(options):
+  return not options.no_build and not options.golem
+
+def build_desugared_library_dex(options, quiet, temp, android_java8_libs, inputs, outdir):
+  if not input:
+    raise Exception("If 'android_java8_libs' is specified the inputs must be explicit"
+       + "(not defined using '-injars' in Proguard configuration files)")
+  if outdir.endswith('.zip') or outdir.endswith('.jar'):
+    raise Exception("If 'android_java8_libs' is specified the output must be a directory")
+
+  jar = None
+  main = None
+  if options.hash:
+    jar = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
+
+  assert(options.compiler_build in ['full', 'lib'])
+  lib_prefix = 'r8lib-' if options.compiler_build == 'lib' else ''
+
+  # Determine the tracereferences tool.
+  tool = lib_prefix + 'tracereferences'
+  if options.hash:
+    main = 'com.android.tools.r8.TraceReferences'
+  tracereferences_output = os.path.join(outdir, 'tracereferences.pgcfg')
+  args = [
+    '--map-diagnostics:MissingDefinitionsDiagnostic', 'error', 'warning',
+    '--keep-rules',
+    '--lib', 'third_party/android_jar/lib-v30/android.jar',
+    '--target', android_java8_libs['library'],
+    '--output', tracereferences_output,
+  ]
+  for source in inputs:
+    args.extend(['--source', source])
+  exit_code = toolhelper.run(tool, args,
+      build=should_build(options),
+      debug=not options.no_debug,
+      quiet=quiet,
+      jar=jar,
+      main=main)
+  if exit_code != 0:
+    raise Exception("tracereferences failed")
+
+  # Determine the r8 tool.
+  tool = lib_prefix + 'r8'
+  if options.compiler_build == 'full':
+    tool = ''
+  else:
+    assert(options.compiler_build == 'lib')
+    tool = 'r8lib-r8'
+  if options.hash:
+    main = 'com.android.tools.r8.R8'
+  android_java8_libs_output = os.path.join(temp, 'android_java8_libs')
+  os.makedirs(android_java8_libs_output)
+  args = [
+    '--no-desugaring',
+    '--lib', '%s/android_jar/lib-v30/android.jar' % utils.THIRD_PARTY,
+    '--output', android_java8_libs_output,
+    '--pg-conf', tracereferences_output,
+    android_java8_libs['library']
+  ]
+  for pgconf in android_java8_libs['pgconf']:
+    args.extend(['--pg-conf', pgconf])
+  exit_code = toolhelper.run(tool, args,
+      build=should_build(options),
+      debug=not options.no_debug,
+      quiet=quiet,
+      jar=jar,
+      main=main)
+  # Copy the desugared library DEX to the output.
+  dex_file_name = 'classes' + str(len(glob(os.path.join(outdir, '*.dex'))) + 1) + '.dex'
+  shutil.copyfile(
+      os.path.join(android_java8_libs_output, 'classes.dex'),
+      os.path.join(outdir, dex_file_name))
+
 def run_with_options(options, args, extra_args=None, stdout=None, quiet=False):
   if extra_args is None:
     extra_args = []
@@ -495,18 +569,6 @@
     print('Valid types are {}'.format(version.keys()))
     return 1
   values = version[type]
-  inputs = []
-  # For R8 'deploy' the JAR is located using the Proguard configuration
-  # -injars option. For chrome and nest we don't have the injars in the
-  # proguard files.
-  if 'inputs' in values and (options.compiler != 'r8'
-                             or type != 'deploy'
-                             or options.app == 'chrome'
-                             or options.app == 'nest'
-                             or options.app == 'r8'
-                             or options.app == 'iosched'
-                             or options.app == 'tachiyomi'):
-    inputs = values['inputs']
 
   args.extend(['--output', outdir])
   if 'min-api' in values:
@@ -527,6 +589,7 @@
             sanitized_lib_path, sanitized_pgconf_path, values['pgconf'])
         libraries = [sanitized_lib_path]
         args.extend(['--pg-conf', sanitized_pgconf_path])
+        inputs = []
       else:
         # -injars without -libraryjars or vice versa is not supported.
         check_no_injars_and_no_libraryjars(values['pgconf'])
@@ -538,7 +601,7 @@
           SanitizeLibraries(
             sanitized_lib_path, values['libraries'], values['inputs'])
           libraries = [sanitized_lib_path]
-          inputs = values['inputs']
+        inputs = values['inputs']
       app_provided_pg_conf = True
     if options.k:
       args.extend(['--pg-conf', options.k])
@@ -616,7 +679,6 @@
           additional_pg_conf = GenerateAdditionalProguardConfiguration(
               temp, os.path.abspath(pg_outdir))
           args.extend(['--pg-conf', additional_pg_conf])
-      build = not options.no_build and not options.golem
       stderr_path = os.path.join(temp, 'stderr')
       with open(stderr_path, 'w') as stderr:
         jar = None
@@ -630,7 +692,7 @@
           jar = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
           main = 'com.android.tools.r8.' + options.compiler.upper()
         exit_code = toolhelper.run(tool, args,
-            build=build,
+            build=should_build(options),
             debug=not options.no_debug,
             profile=options.profile,
             track_memory_file=options.track_memory_to_file,
@@ -659,6 +721,11 @@
             .format(options.print_memoryuse,
                 utils.grep_memoryuse(options.track_memory_to_file)))
 
+      if 'android_java8_libs' in values:
+        android_java8_libs = values['android_java8_libs']
+        build_desugared_library_dex(options, quiet, temp, android_java8_libs, inputs, outdir)
+
+
   if options.print_runtimeraw:
     print('{}(RunTimeRaw): {} ms'
         .format(options.print_runtimeraw, 1000.0 * (time.time() - t0)))
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index 1cfd0f0..b66b70f 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -41,6 +41,8 @@
     cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.D8'])
   elif tool == 'r8lib-r8':
     cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.R8'])
+  elif tool == 'r8lib-tracereferences':
+    cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.tracereferences.TraceReferences'])
   else:
     cmd.extend(['-jar', utils.R8_JAR, tool])
   lib, args = extract_lib_from_args(args)
diff --git a/tools/youtube_data.py b/tools/youtube_data.py
index eafe5bb..b13407a 100644
--- a/tools/youtube_data.py
+++ b/tools/youtube_data.py
@@ -37,6 +37,9 @@
 V15_33_BASE = os.path.join(BASE, 'youtube.android_15.33')
 V15_33_PREFIX = os.path.join(V15_33_BASE, 'YouTubeRelease')
 
+V16_10_BASE = os.path.join(BASE, 'youtube.android_16.10')
+V16_10_PREFIX = os.path.join(V16_10_BASE, 'YouTubeRelease')
+
 # NOTE: we always use android.jar for SDK v25, later we might want to revise it
 #       to use proper android.jar version for each of youtube version separately.
 ANDROID_JAR = utils.get_android_jar(25)
@@ -283,4 +286,34 @@
       'min-api' : ANDROID_L_API,
     }
   },
+  '16.10': {
+    'dex' : {
+      'inputs': [os.path.join(V16_10_BASE, 'YouTubeRelease_unsigned.apk')],
+      'pgmap': '%s_proguard.map' % V16_10_PREFIX,
+      'libraries' : [ANDROID_JAR],
+      'min-api' : ANDROID_L_API,
+    },
+    'deploy' : {
+      'sanitize_libraries': False,
+      'inputs': ['%s_deploy.jar' % V16_10_PREFIX],
+      'libraries' : [os.path.join(V16_10_BASE, 'legacy_YouTubeRelease_combined_library_jars_filtered.jar')],
+      'pgconf': [
+          '%s_proguard.config' % V16_10_PREFIX,
+          '%s/proguardsettings/YouTubeRelease_proguard.config' % utils.THIRD_PARTY,
+          utils.IGNORE_WARNINGS_RULES],
+      'min-api' : ANDROID_L_API,
+      'android_java8_libs': {
+        'library': '%s/desugar_jdk_libs/desugared_jdk_libs.jar' % V16_10_BASE,
+        'pgconf': [
+          '%s/desugar_jdk_libs/base.pgcfg' % V16_10_BASE,
+          '%s/desugar_jdk_libs/minify_desugar_jdk_libs.pgcfg' % V16_10_BASE
+        ]
+      }
+    },
+    'proguarded' : {
+      'inputs': ['%s_proguard.jar' % V16_10_PREFIX],
+      'pgmap': '%s_proguard.map' % V16_10_PREFIX,
+      'min-api' : ANDROID_L_API,
+    }
+  },
 }