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

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: Self review style nits 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 16
17 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
18 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'gyp'))
jbudorick 2015/09/15 18:36:41 nit: I'd prefer this one down above the build_util
agrieve 2015/09/15 19:30:05 Done.
15 19
16 from devil.android import apk_helper 20 from devil.android import apk_helper
17 from devil.android import device_utils 21 from devil.android import device_utils
18 from devil.android import device_errors 22 from devil.android import device_errors
23 from devil.android.sdk import version_codes
19 from devil.utils import reraiser_thread 24 from devil.utils import reraiser_thread
20 from pylib import constants 25 from pylib import constants
21 from pylib.utils import run_tests_helper 26 from pylib.utils import run_tests_helper
27 from pylib.utils import time_profile
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('--no-threading', 58 parser.add_argument('--no-threading',
43 action='store_true', 59 action='store_true',
44 default=False, 60 default=False,
45 help='Do not install and push concurrently') 61 help='Do not install and push concurrently')
46 parser.add_argument('-v', 62 parser.add_argument('-v',
47 '--verbose', 63 '--verbose',
48 dest='verbose_count', 64 dest='verbose_count',
49 default=0, 65 default=0,
50 action='count', 66 action='count',
51 help='Verbose level (multiple times for more)') 67 help='Verbose level (multiple times for more)')
52 68
53 args = parser.parse_args() 69 args = parser.parse_args()
54 70
55 logging.basicConfig(format='%(asctime)s (%(thread)d) %(message)s')
56 run_tests_helper.SetLogLevel(args.verbose_count) 71 run_tests_helper.SetLogLevel(args.verbose_count)
57 constants.SetBuildType('Debug') 72 constants.SetBuildType('Debug')
58 73
74 main_timer = time_profile.TimeProfile()
75 install_timer = time_profile.TimeProfile()
76 push_native_timer = time_profile.TimeProfile()
77 push_dex_timer = time_profile.TimeProfile()
78
59 if args.device: 79 if args.device:
60 # Retries are annoying when commands fail for legitimate reasons. Might want 80 # Retries are annoying when commands fail for legitimate reasons. Might want
61 # to enable them if this is ever used on bots though. 81 # to enable them if this is ever used on bots though.
62 device = device_utils.DeviceUtils(args.device, default_retries=0) 82 device = device_utils.DeviceUtils(args.device, default_retries=0)
63 else: 83 else:
64 devices = device_utils.DeviceUtils.HealthyDevices(default_retries=0) 84 devices = device_utils.DeviceUtils.HealthyDevices(default_retries=0)
65 if not devices: 85 if not devices:
66 raise device_errors.NoDevicesError() 86 raise device_errors.NoDevicesError()
67 elif len(devices) == 1: 87 elif len(devices) == 1:
68 device = devices[0] 88 device = devices[0]
69 else: 89 else:
70 all_devices = device_utils.DeviceUtils.parallel(devices) 90 all_devices = device_utils.DeviceUtils.parallel(devices)
71 msg = ('More than one device available.\n' 91 msg = ('More than one device available.\n'
72 'Use --device=SERIAL to select a device.\n' 92 'Use --device=SERIAL to select a device.\n'
73 'Available devices:\n') 93 'Available devices:\n')
74 descriptions = all_devices.pMap(lambda d: d.build_description).pGet(None) 94 descriptions = all_devices.pMap(lambda d: d.build_description).pGet(None)
75 for d, desc in zip(devices, descriptions): 95 for d, desc in zip(devices, descriptions):
76 msg += ' %s (%s)\n' % (d, desc) 96 msg += ' %s (%s)\n' % (d, desc)
77 raise Exception(msg) 97 raise Exception(msg)
78 98
79 apk_package = apk_helper.ApkHelper(args.apk_path).GetPackageName() 99 apk_help = apk_helper.ApkHelper(args.apk_path)
100 apk_package = apk_help.GetPackageName()
80 device_incremental_dir = '/data/local/tmp/incremental-app-%s' % apk_package 101 device_incremental_dir = '/data/local/tmp/incremental-app-%s' % apk_package
81 102
82 if args.uninstall: 103 if args.uninstall:
83 logging.info('Uninstalling .apk') 104 is_installed = device.GetApplicationPaths(apk_package)
jbudorick 2015/09/15 18:36:41 Is this faster than just a raw Uninstall call? Tha
agrieve 2015/09/15 19:30:05 DeviceUtils.Uninstall() fails if the app isn't alr
84 device.Uninstall(apk_package) 105 if is_installed:
106 logging.info('Uninstalling .apk')
107 device.Uninstall(apk_package)
85 logging.info('Removing side-loaded files') 108 logging.info('Removing side-loaded files')
86 device.RunShellCommand(['rm', '-rf', device_incremental_dir], 109 device.RunShellCommand(['rm', '-rf', device_incremental_dir],
87 check_return=True) 110 check_return=True)
111 logging.info('Uninstall took %s seconds.', main_timer.GetDelta())
88 return 112 return
89 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.')
jbudorick 2015/09/15 18:36:41 o_o yikes
agrieve 2015/09/15 19:30:05 yeah... :( maybe someone will figure it out, but f
119
90 # Install .apk(s) if any of them have changed. 120 # Install .apk(s) if any of them have changed.
91 def do_install(): 121 def do_install():
122 install_timer.Start()
92 if args.splits: 123 if args.splits:
93 splits = [] 124 splits = []
94 for split_glob in args.splits: 125 for split_glob in args.splits:
95 splits.extend((f for f in glob.glob(split_glob))) 126 splits.extend((f for f in glob.glob(split_glob)))
96 device.InstallSplitApk(args.apk_path, splits, reinstall=True, 127 device.InstallSplitApk(args.apk_path, splits, reinstall=True,
97 allow_cached_props=True) 128 allow_cached_props=True)
98 else: 129 else:
99 device.Install(args.apk_path, reinstall=True) 130 device.Install(args.apk_path, reinstall=True)
100 logging.info('Finished installing .apk') 131 install_timer.Stop(log=False)
101 132
102 # Push .so files to the device (if they have changed). 133 # Push .so and .dex files to the device (if they have changed).
103 def do_push_libs(): 134 def do_push_files():
104 if args.lib_dir: 135 if args.lib_dir:
136 push_native_timer.Start()
105 device_lib_dir = posixpath.join(device_incremental_dir, 'lib') 137 device_lib_dir = posixpath.join(device_incremental_dir, 'lib')
106 device.PushChangedFiles([(args.lib_dir, device_lib_dir)], 138 device.PushChangedFiles([(args.lib_dir, device_lib_dir)],
107 delete_device_stale=True) 139 delete_device_stale=True)
108 logging.info('Finished pushing native libs') 140 push_native_timer.Stop(log=False)
109 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))
jbudorick 2015/09/15 18:36:41 cross your fingers and hope for copy-on-write, I s
agrieve 2015/09/15 19:30:05 Yeah, might come back to this, but it's sub-second
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)
jbudorick 2015/09/15 18:36:41 I think I'm missing something... how is this creat
agrieve 2015/09/15 19:30:05 You're missing the invisible > that I changed too
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()
110 # 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
111 # been designed for multi-threading. Enabling only because this is a 174 # been designed for multi-threading. Enabling only because this is a
112 # developer-only tool. 175 # developer-only tool.
113 if args.no_threading: 176 if args.no_threading:
114 do_install() 177 do_install()
115 do_push_libs() 178 do_push_files()
116 else: 179 else:
117 reraiser_thread.RunAsync((do_install, do_push_libs)) 180 reraiser_thread.RunAsync((do_install, do_push_files))
118 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())
119 185
120 186
121 if __name__ == '__main__': 187 if __name__ == '__main__':
122 sys.exit(main()) 188 sys.exit(main())
123 189
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698