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

Side by Side 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 unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2015 The Chromium Authors. All rights reserved. 3 # Copyright 2015 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """Install *_incremental.apk targets as well as their dependent files.""" 7 """Install *_incremental.apk targets as well as their dependent files."""
8 8
9 import argparse 9 import argparse
10 import glob 10 import glob
11 import logging 11 import logging
12 import os
12 import posixpath 13 import posixpath
14 import shutil
13 import sys 15 import sys
14 import time
15 16
17 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
16 from devil.android import apk_helper 18 from devil.android import apk_helper
17 from devil.android import device_utils 19 from devil.android import device_utils
18 from devil.android import device_errors 20 from devil.android import device_errors
21 from devil.android.sdk import version_codes
19 from devil.utils import reraiser_thread 22 from devil.utils import reraiser_thread
20 from pylib import constants 23 from pylib import constants
21 from pylib.utils import run_tests_helper 24 from pylib.utils import run_tests_helper
25 from pylib.utils import time_profile
26
27 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'gyp'))
28 from util import build_utils
29
30
31 def _TransformDexPaths(paths):
32 """Given paths like ["/a/b/c", "/a/c/d"], returns ["b.c", "c.d"]."""
33 prefix_len = len(os.path.commonprefix(paths))
34 return [p[prefix_len:].replace(os.sep, '.') for p in paths]
22 35
23 36
24 def main(): 37 def main():
25 start_time = time.time()
26 parser = argparse.ArgumentParser() 38 parser = argparse.ArgumentParser()
27 parser.add_argument('apk_path', 39 parser.add_argument('apk_path',
28 help='The path to the APK to install.') 40 help='The path to the APK to install.')
29 parser.add_argument('--split', 41 parser.add_argument('--split',
30 action='append', 42 action='append',
31 dest='splits', 43 dest='splits',
32 help='A glob matching the apk splits. ' 44 help='A glob matching the apk splits. '
33 'Can be specified multiple times.') 45 'Can be specified multiple times.')
34 parser.add_argument('--lib-dir', 46 parser.add_argument('--lib-dir',
35 help='Path to native libraries directory.') 47 help='Path to native libraries directory.')
48 parser.add_argument('--dex-files',
49 help='List of dex files to push.',
50 action='append',
51 default=[])
36 parser.add_argument('-d', '--device', dest='device', 52 parser.add_argument('-d', '--device', dest='device',
37 help='Target device for apk to install on.') 53 help='Target device for apk to install on.')
38 parser.add_argument('--uninstall', 54 parser.add_argument('--uninstall',
39 action='store_true', 55 action='store_true',
40 default=False, 56 default=False,
41 help='Remove the app and all side-loaded files.') 57 help='Remove the app and all side-loaded files.')
42 parser.add_argument('--output-directory', 58 parser.add_argument('--output-directory',
43 help='Path to the root build directory.') 59 help='Path to the root build directory.')
44 parser.add_argument('--no-threading', 60 parser.add_argument('--no-threading',
45 action='store_true', 61 action='store_true',
46 default=False, 62 default=False,
47 help='Do not install and push concurrently') 63 help='Do not install and push concurrently')
48 parser.add_argument('-v', 64 parser.add_argument('-v',
49 '--verbose', 65 '--verbose',
50 dest='verbose_count', 66 dest='verbose_count',
51 default=0, 67 default=0,
52 action='count', 68 action='count',
53 help='Verbose level (multiple times for more)') 69 help='Verbose level (multiple times for more)')
54 70
55 args = parser.parse_args() 71 args = parser.parse_args()
56 72
57 logging.basicConfig(format='%(asctime)s (%(thread)d) %(message)s')
58 run_tests_helper.SetLogLevel(args.verbose_count) 73 run_tests_helper.SetLogLevel(args.verbose_count)
59 constants.SetBuildType('Debug') 74 constants.SetBuildType('Debug')
60 if args.output_directory: 75 if args.output_directory:
61 constants.SetOutputDirectory(args.output_directory) 76 constants.SetOutputDirectory(args.output_directory)
62 77
78 main_timer = time_profile.TimeProfile()
79 install_timer = time_profile.TimeProfile()
80 push_native_timer = time_profile.TimeProfile()
81 push_dex_timer = time_profile.TimeProfile()
82
63 if args.device: 83 if args.device:
64 # Retries are annoying when commands fail for legitimate reasons. Might want 84 # Retries are annoying when commands fail for legitimate reasons. Might want
65 # to enable them if this is ever used on bots though. 85 # to enable them if this is ever used on bots though.
66 device = device_utils.DeviceUtils(args.device, default_retries=0) 86 device = device_utils.DeviceUtils(args.device, default_retries=0)
67 else: 87 else:
68 devices = device_utils.DeviceUtils.HealthyDevices(default_retries=0) 88 devices = device_utils.DeviceUtils.HealthyDevices(default_retries=0)
69 if not devices: 89 if not devices:
70 raise device_errors.NoDevicesError() 90 raise device_errors.NoDevicesError()
71 elif len(devices) == 1: 91 elif len(devices) == 1:
72 device = devices[0] 92 device = devices[0]
73 else: 93 else:
74 all_devices = device_utils.DeviceUtils.parallel(devices) 94 all_devices = device_utils.DeviceUtils.parallel(devices)
75 msg = ('More than one device available.\n' 95 msg = ('More than one device available.\n'
76 'Use --device=SERIAL to select a device.\n' 96 'Use --device=SERIAL to select a device.\n'
77 'Available devices:\n') 97 'Available devices:\n')
78 descriptions = all_devices.pMap(lambda d: d.build_description).pGet(None) 98 descriptions = all_devices.pMap(lambda d: d.build_description).pGet(None)
79 for d, desc in zip(devices, descriptions): 99 for d, desc in zip(devices, descriptions):
80 msg += ' %s (%s)\n' % (d, desc) 100 msg += ' %s (%s)\n' % (d, desc)
81 raise Exception(msg) 101 raise Exception(msg)
82 102
83 apk_package = apk_helper.ApkHelper(args.apk_path).GetPackageName() 103 apk_help = apk_helper.ApkHelper(args.apk_path)
104 apk_package = apk_help.GetPackageName()
84 device_incremental_dir = '/data/local/tmp/incremental-app-%s' % apk_package 105 device_incremental_dir = '/data/local/tmp/incremental-app-%s' % apk_package
85 106
86 if args.uninstall: 107 if args.uninstall:
87 logging.info('Uninstalling .apk')
88 device.Uninstall(apk_package) 108 device.Uninstall(apk_package)
89 logging.info('Removing side-loaded files')
90 device.RunShellCommand(['rm', '-rf', device_incremental_dir], 109 device.RunShellCommand(['rm', '-rf', device_incremental_dir],
91 check_return=True) 110 check_return=True)
111 logging.info('Uninstall took %s seconds.', main_timer.GetDelta())
92 return 112 return
93 113
114 if device.build_version_sdk >= version_codes.MARSHMALLOW:
115 if apk_help.HasIsolatedProcesses():
116 raise Exception('Cannot use perform incremental installs on Android M+ '
117 'without first disabling isolated processes. Use GN arg: '
118 'disable_incremental_isolated_processes=true to do so.')
119
94 # Install .apk(s) if any of them have changed. 120 # Install .apk(s) if any of them have changed.
95 def do_install(): 121 def do_install():
122 install_timer.Start()
96 if args.splits: 123 if args.splits:
97 splits = [] 124 splits = []
98 for split_glob in args.splits: 125 for split_glob in args.splits:
99 splits.extend((f for f in glob.glob(split_glob))) 126 splits.extend((f for f in glob.glob(split_glob)))
100 device.InstallSplitApk(args.apk_path, splits, reinstall=True, 127 device.InstallSplitApk(args.apk_path, splits, reinstall=True,
101 allow_cached_props=True) 128 allow_cached_props=True)
102 else: 129 else:
103 device.Install(args.apk_path, reinstall=True) 130 device.Install(args.apk_path, reinstall=True)
104 logging.info('Finished installing .apk') 131 install_timer.Stop(log=False)
105 132
106 # Push .so files to the device (if they have changed). 133 # Push .so and .dex files to the device (if they have changed).
107 def do_push_libs(): 134 def do_push_files():
108 if args.lib_dir: 135 if args.lib_dir:
136 push_native_timer.Start()
109 device_lib_dir = posixpath.join(device_incremental_dir, 'lib') 137 device_lib_dir = posixpath.join(device_incremental_dir, 'lib')
110 device.PushChangedFiles([(args.lib_dir, device_lib_dir)], 138 device.PushChangedFiles([(args.lib_dir, device_lib_dir)],
111 delete_device_stale=True) 139 delete_device_stale=True)
112 logging.info('Finished pushing native libs') 140 push_native_timer.Stop(log=False)
113 141
142 if args.dex_files:
143 push_dex_timer.Start()
144 # Put all .dex files to be pushed into a temporary directory so that we
145 # can use delete_device_stale=True.
146 with build_utils.TempDir() as temp_dir:
147 device_dex_dir = posixpath.join(device_incremental_dir, 'dex')
148 # Ensure no two files have the same name.
149 transformed_names = _TransformDexPaths(args.dex_files)
150 for src_path, dest_name in zip(args.dex_files, transformed_names):
151 shutil.copyfile(src_path, os.path.join(temp_dir, dest_name))
152 device.PushChangedFiles([(temp_dir, device_dex_dir)],
153 delete_device_stale=True)
154 push_dex_timer.Stop(log=False)
155
156 # Create 2 lock files:
157 # * install.lock tells the app to pause on start-up (until we release it).
158 # * firstrun.lock is used by the app to pause all secondary processes until
159 # the primary process finishes loading the .dex / .so files.
160 def create_lock_files():
161 # Creates or zeros out lock files.
162 cmd = ('D="%s";'
163 'mkdir -p $D &&'
164 'echo -n >$D/install.lock 2>$D/firstrun.lock')
165 device.RunShellCommand(cmd % device_incremental_dir, check_return=True)
166
167 # The firstrun.lock is released by the app itself.
168 def release_installer_lock():
169 device.RunShellCommand('echo > %s/install.lock' % device_incremental_dir,
170 check_return=True)
171
172 create_lock_files()
114 # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't 173 # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't
115 # been designed for multi-threading. Enabling only because this is a 174 # been designed for multi-threading. Enabling only because this is a
116 # developer-only tool. 175 # developer-only tool.
117 if args.no_threading: 176 if args.no_threading:
118 do_install() 177 do_install()
119 do_push_libs() 178 do_push_files()
120 else: 179 else:
121 reraiser_thread.RunAsync((do_install, do_push_libs)) 180 reraiser_thread.RunAsync((do_install, do_push_files))
122 logging.info('Took %s seconds', round(time.time() - start_time, 1)) 181 release_installer_lock()
182 logging.info('Took %s seconds (install=%s, libs=%s, dex=%s)',
183 main_timer.GetDelta(), install_timer.GetDelta(),
184 push_native_timer.GetDelta(), push_dex_timer.GetDelta())
123 185
124 186
125 if __name__ == '__main__': 187 if __name__ == '__main__':
126 sys.exit(main()) 188 sys.exit(main())
127 189
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698