Add --desugared-library-dex flag to apk_masseur.py

Change-Id: Ie44506ad568319fd86478ee4d41621b2f4b3d754
diff --git a/tools/apk_masseur.py b/tools/apk_masseur.py
index 2bcea93..5153327 100755
--- a/tools/apk_masseur.py
+++ b/tools/apk_masseur.py
@@ -3,21 +3,27 @@
 # 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 apk_utils
 import glob
 import optparse
 import os
 import shutil
 import sys
+
+import apk_utils
 import utils
+import zip_utils
 
 USAGE = 'usage: %prog [options] <apk>'
 
 def parse_options():
   parser = optparse.OptionParser(usage=USAGE)
   parser.add_option('--dex',
-                    help=('directory with dex files to use instead of those in '
-                        + 'the apk'),
+                    help='Directory or archive with dex files to use instead '
+                         'of those in the apk',
+                    default=None)
+  parser.add_option('--desugared-library-dex',
+                    help='Path to desugared library dex file to use or archive '
+                         'containing a single classes.dex file',
                     default=None)
   parser.add_option('--resources',
                     help=('pattern that matches resources to use instead of '
@@ -49,14 +55,18 @@
   apk = args[0]
   return (options, apk)
 
-def repack(apk, processed_out, resources, temp, quiet, logging):
+def is_archive(file):
+  return file.endswith('.zip') or file.endswith('.jar')
+
+def repack(
+    apk, processed_out, desugared_library_dex, resources, temp, quiet, logging):
   processed_apk = os.path.join(temp, 'processed.apk')
   shutil.copyfile(apk, processed_apk)
   if not processed_out:
     utils.Print('Using original APK as is', quiet=quiet)
     return processed_apk
   utils.Print(
-      'Repacking APK with dex files from {}'.format(processed_apk), quiet=quiet)
+      'Repacking APK with dex files from {}'.format(processed_out), quiet=quiet)
 
   # Delete original dex files in APK.
   with utils.ChangedWorkingDirectory(temp, quiet=quiet):
@@ -64,16 +74,33 @@
     utils.RunCmd(cmd, quiet=quiet, logging=logging)
 
   # Unzip the jar or zip file into `temp`.
-  if processed_out.endswith('.zip') or processed_out.endswith('.jar'):
+  if is_archive(processed_out):
     cmd = ['unzip', processed_out, '-d', temp]
     if quiet:
       cmd.insert(1, '-q')
     utils.RunCmd(cmd, quiet=quiet, logging=logging)
     processed_out = temp
+  elif desugared_library_dex:
+    for dex_name in glob.glob('*.dex', root_dir=processed_out):
+      src = os.path.join(processed_out, dex_name)
+      dst = os.path.join(temp, dex_name)
+      shutil.copyfile(src, dst)
+    processed_out = temp
+
+  if desugared_library_dex:
+    desugared_library_dex_index = len(glob.glob('*.dex', root_dir=temp)) + 1
+    desugared_library_dex_name = 'classes%s.dex' % desugared_library_dex_index
+    desugared_library_dex_dst = os.path.join(temp, desugared_library_dex_name)
+    if is_archive(desugared_library_dex):
+      zip_utils.extract_member(
+          desugared_library_dex, 'classes.dex', desugared_library_dex_dst)
+    else:
+      shutil.copyfile(desugared_library_dex, desugared_library_dex_dst)
 
   # Insert the new dex and resource files from `processed_out` into the APK.
   with utils.ChangedWorkingDirectory(processed_out, quiet=quiet):
     dex_files = glob.glob('*.dex')
+    dex_files.sort()
     resource_files = glob.glob(resources) if resources else []
     cmd = ['zip', '-u', '-0', processed_apk] + dex_files + resource_files
     utils.RunCmd(cmd, quiet=quiet, logging=logging)
@@ -90,9 +117,9 @@
   return apk_utils.align(signed_apk, aligned_apk)
 
 def masseur(
-    apk, dex=None, resources=None, out=None, adb_options=None,
-    sign_before_align=False, keystore=None, install=False, quiet=False,
-    logging=True):
+    apk, dex=None, desugared_library_dex=None, resources=None, out=None,
+    adb_options=None, sign_before_align=False, keystore=None, install=False,
+    quiet=False, logging=True):
   if not out:
     out = os.path.basename(apk)
   if not keystore:
@@ -100,8 +127,10 @@
   with utils.TempDir() as temp:
     processed_apk = None
     if dex:
-      processed_apk = repack(apk, dex, resources, temp, quiet, logging)
+      processed_apk = repack(
+          apk, dex, desugared_library_dex, resources, temp, quiet, logging)
     else:
+      assert not desugared_library_dex
       utils.Print(
           'Signing original APK without modifying dex files', quiet=quiet)
       processed_apk = os.path.join(temp, 'processed.apk')