Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Unified Diff: build/android/incremental_install/installer.py

Issue 1338813003: GN: Side-load dex files as well as native code in incremental installs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix pylint warnings Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: build/android/incremental_install/installer.py
diff --git a/build/android/incremental_install.py b/build/android/incremental_install/installer.py
similarity index 55%
rename from build/android/incremental_install.py
rename to build/android/incremental_install/installer.py
index d88a04d6d7680bcc2419e631af7a71d2608c2c69..1d70335491e3016a155c6763e8ff25bd9817df31 100755
--- a/build/android/incremental_install.py
+++ b/build/android/incremental_install/installer.py
@@ -9,20 +9,32 @@
import argparse
import glob
import logging
+import os
import posixpath
+import shutil
import sys
-import time
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
from devil.android import apk_helper
from devil.android import device_utils
from devil.android import device_errors
+from devil.android.sdk import version_codes
from devil.utils import reraiser_thread
from pylib import constants
from pylib.utils import run_tests_helper
+from pylib.utils import time_profile
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'gyp'))
+from util import build_utils
+
+
+def _TransformDexPaths(paths):
+ """Given paths like ["/a/b/c", "/a/c/d"], returns ["b.c", "c.d"]."""
+ prefix_len = len(os.path.commonprefix(paths))
+ return [p[prefix_len:].replace(os.sep, '.') for p in paths]
def main():
- start_time = time.time()
parser = argparse.ArgumentParser()
parser.add_argument('apk_path',
help='The path to the APK to install.')
@@ -33,6 +45,10 @@ def main():
'Can be specified multiple times.')
parser.add_argument('--lib-dir',
help='Path to native libraries directory.')
+ parser.add_argument('--dex-files',
+ help='List of dex files to push.',
+ action='append',
+ default=[])
parser.add_argument('-d', '--device', dest='device',
help='Target device for apk to install on.')
parser.add_argument('--uninstall',
@@ -54,12 +70,16 @@ def main():
args = parser.parse_args()
- logging.basicConfig(format='%(asctime)s (%(thread)d) %(message)s')
run_tests_helper.SetLogLevel(args.verbose_count)
constants.SetBuildType('Debug')
if args.output_directory:
constants.SetOutputDirectory(args.output_directory)
+ main_timer = time_profile.TimeProfile()
+ install_timer = time_profile.TimeProfile()
+ push_native_timer = time_profile.TimeProfile()
+ push_dex_timer = time_profile.TimeProfile()
+
if args.device:
# Retries are annoying when commands fail for legitimate reasons. Might want
# to enable them if this is ever used on bots though.
@@ -80,19 +100,26 @@ def main():
msg += ' %s (%s)\n' % (d, desc)
raise Exception(msg)
- apk_package = apk_helper.ApkHelper(args.apk_path).GetPackageName()
+ apk_help = apk_helper.ApkHelper(args.apk_path)
+ apk_package = apk_help.GetPackageName()
device_incremental_dir = '/data/local/tmp/incremental-app-%s' % apk_package
if args.uninstall:
- logging.info('Uninstalling .apk')
device.Uninstall(apk_package)
- logging.info('Removing side-loaded files')
device.RunShellCommand(['rm', '-rf', device_incremental_dir],
check_return=True)
+ logging.info('Uninstall took %s seconds.', main_timer.GetDelta())
return
+ if device.build_version_sdk >= version_codes.MARSHMALLOW:
+ if apk_help.HasIsolatedProcesses():
+ raise Exception('Cannot use perform incremental installs on Android M+ '
+ 'without first disabling isolated processes. Use GN arg: '
+ 'disable_incremental_isolated_processes=true to do so.')
+
# Install .apk(s) if any of them have changed.
def do_install():
+ install_timer.Start()
if args.splits:
splits = []
for split_glob in args.splits:
@@ -101,25 +128,60 @@ def main():
allow_cached_props=True)
else:
device.Install(args.apk_path, reinstall=True)
- logging.info('Finished installing .apk')
+ install_timer.Stop(log=False)
- # Push .so files to the device (if they have changed).
- def do_push_libs():
+ # Push .so and .dex files to the device (if they have changed).
+ def do_push_files():
if args.lib_dir:
+ push_native_timer.Start()
device_lib_dir = posixpath.join(device_incremental_dir, 'lib')
device.PushChangedFiles([(args.lib_dir, device_lib_dir)],
delete_device_stale=True)
- logging.info('Finished pushing native libs')
+ push_native_timer.Stop(log=False)
+
+ if args.dex_files:
+ push_dex_timer.Start()
+ # Put all .dex files to be pushed into a temporary directory so that we
+ # can use delete_device_stale=True.
+ with build_utils.TempDir() as temp_dir:
+ device_dex_dir = posixpath.join(device_incremental_dir, 'dex')
+ # Ensure no two files have the same name.
+ transformed_names = _TransformDexPaths(args.dex_files)
+ for src_path, dest_name in zip(args.dex_files, transformed_names):
+ shutil.copyfile(src_path, os.path.join(temp_dir, dest_name))
+ device.PushChangedFiles([(temp_dir, device_dex_dir)],
+ delete_device_stale=True)
+ push_dex_timer.Stop(log=False)
+
+ # Create 2 lock files:
+ # * install.lock tells the app to pause on start-up (until we release it).
+ # * firstrun.lock is used by the app to pause all secondary processes until
+ # the primary process finishes loading the .dex / .so files.
+ def create_lock_files():
+ # Creates or zeros out lock files.
+ cmd = ('D="%s";'
+ 'mkdir -p $D &&'
+ 'echo -n >$D/install.lock 2>$D/firstrun.lock')
+ device.RunShellCommand(cmd % device_incremental_dir, check_return=True)
+
+ # The firstrun.lock is released by the app itself.
+ def release_installer_lock():
+ device.RunShellCommand('echo > %s/install.lock' % device_incremental_dir,
+ check_return=True)
+ create_lock_files()
# Concurrency here speeds things up quite a bit, but DeviceUtils hasn't
# been designed for multi-threading. Enabling only because this is a
# developer-only tool.
if args.no_threading:
do_install()
- do_push_libs()
+ do_push_files()
else:
- reraiser_thread.RunAsync((do_install, do_push_libs))
- logging.info('Took %s seconds', round(time.time() - start_time, 1))
+ reraiser_thread.RunAsync((do_install, do_push_files))
+ release_installer_lock()
+ logging.info('Took %s seconds (install=%s, libs=%s, dex=%s)',
+ main_timer.GetDelta(), install_timer.GetDelta(),
+ push_native_timer.GetDelta(), push_dex_timer.GetDelta())
if __name__ == '__main__':

Powered by Google App Engine
This is Rietveld 408576698