blob: 7473393cf49c4dba47dd65190d4de349a002b6f1 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Ryan Cui3045c5d2012-07-13 18:00:33 -07002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Steve Funge984a532013-11-25 17:09:25 -08006"""Script that deploys a Chrome build to a device.
Ryan Cuia56a71e2012-10-18 18:40:35 -07007
8The script supports deploying Chrome from these sources:
9
101. A local build output directory, such as chromium/src/out/[Debug|Release].
112. A Chrome tarball uploaded by a trybot/official-builder to GoogleStorage.
123. A Chrome tarball existing locally.
13
14The script copies the necessary contents of the source location (tarball or
15build directory) and rsyncs the contents of the staging directory onto your
16device's rootfs.
17"""
Ryan Cui3045c5d2012-07-13 18:00:33 -070018
Mike Frysinger383367e2014-09-16 15:06:17 -040019from __future__ import print_function
20
Mike Frysingerc3061a62015-06-04 04:16:18 -040021import argparse
Ryan Cui7193a7e2013-04-26 14:15:19 -070022import collections
Ryan Cuif890a3e2013-03-07 18:57:06 -080023import contextlib
Ryan Cui7193a7e2013-04-26 14:15:19 -070024import functools
Steve Funge984a532013-11-25 17:09:25 -080025import glob
David James88e6f032013-03-02 08:13:20 -080026import multiprocessing
Ryan Cui3045c5d2012-07-13 18:00:33 -070027import os
Ryan Cui5b7c2ed2013-03-18 18:45:46 -070028import shlex
Steve Funge984a532013-11-25 17:09:25 -080029import shutil
Mike Frysinger6165cdc2020-02-21 02:38:07 -050030import sys
Ryan Cui3045c5d2012-07-13 18:00:33 -070031import time
Ryan Cui3045c5d2012-07-13 18:00:33 -070032
Aviv Keshetb7519e12016-10-04 00:50:00 -070033from chromite.lib import constants
34from chromite.lib import failures_lib
David Pursellcfd58872015-03-19 09:15:48 -070035from chromite.cli.cros import cros_chrome_sdk
Ryan Cuia56a71e2012-10-18 18:40:35 -070036from chromite.lib import chrome_util
Ryan Cuie535b172012-10-19 18:25:03 -070037from chromite.lib import commandline
Ralph Nathan91874ca2015-03-19 13:29:41 -070038from chromite.lib import cros_build_lib
39from chromite.lib import cros_logging as logging
Ryan Cui777ff422012-12-07 13:12:54 -080040from chromite.lib import gs
Ryan Cui3045c5d2012-07-13 18:00:33 -070041from chromite.lib import osutils
David James88e6f032013-03-02 08:13:20 -080042from chromite.lib import parallel
Ryan Cui3045c5d2012-07-13 18:00:33 -070043from chromite.lib import remote_access as remote
David James3432acd2013-11-27 10:02:18 -080044from chromite.lib import timeout_util
Steven Bennetts368c3e52016-09-23 13:05:21 -070045from gn_helpers import gn_helpers
Ryan Cui3045c5d2012-07-13 18:00:33 -070046
47
Mike Frysinger6165cdc2020-02-21 02:38:07 -050048assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
49
50
Ryan Cui3045c5d2012-07-13 18:00:33 -070051KERNEL_A_PARTITION = 2
52KERNEL_B_PARTITION = 4
53
54KILL_PROC_MAX_WAIT = 10
55POST_KILL_WAIT = 2
56
David Haddock3151d912017-10-24 03:50:32 +000057MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Anushruth8d797672019-10-17 12:22:31 -070058LSOF_COMMAND_CHROME = 'lsof %s/chrome'
59LSOF_COMMAND = 'lsof %s'
David Haddock3151d912017-10-24 03:50:32 +000060DBUS_RELOAD_COMMAND = 'killall -HUP dbus-daemon'
Ryan Cui3045c5d2012-07-13 18:00:33 -070061
Steve Funge984a532013-11-25 17:09:25 -080062_ANDROID_DIR = '/system/chrome'
63_ANDROID_DIR_EXTRACT_PATH = 'system/chrome/*'
64
David James2cb34002013-03-01 18:42:40 -080065_CHROME_DIR = '/opt/google/chrome'
Thiago Goncales12793312013-05-23 11:26:17 -070066_CHROME_DIR_MOUNT = '/mnt/stateful_partition/deploy_rootfs/opt/google/chrome'
Ben Pastenee484b342020-06-30 18:29:27 -070067_CHROME_TEST_BIN_DIR = '/usr/local/libexec/chrome-binary-tests'
David James2cb34002013-03-01 18:42:40 -080068
David Haddock3151d912017-10-24 03:50:32 +000069_UMOUNT_DIR_IF_MOUNTPOINT_CMD = (
70 'if mountpoint -q %(dir)s; then umount %(dir)s; fi')
71_BIND_TO_FINAL_DIR_CMD = 'mount --rbind %s %s'
72_SET_MOUNT_FLAGS_CMD = 'mount -o remount,exec,suid %s'
Anushruth8d797672019-10-17 12:22:31 -070073_MKDIR_P_CMD = 'mkdir -p --mode 0775 %s'
Ben Pastenee484b342020-06-30 18:29:27 -070074_FIND_TEST_BIN_CMD = 'find %s -maxdepth 1 -executable -type f' % (
75 _CHROME_TEST_BIN_DIR)
David Haddock3151d912017-10-24 03:50:32 +000076
77DF_COMMAND = 'df -k %s'
78
ChromeOS Developer536f59a2020-08-10 14:39:27 -070079LACROS_DIR = '/usr/local/lacros-chrome'
Erik Chen75a2f492020-08-06 19:15:11 -070080_CONF_FILE = '/etc/chrome_dev.conf'
81_KILL_LACROS_CHROME_CMD = 'pkill -f %(lacros_dir)s/chrome'
82MODIFIED_CONF_FILE = f'modified {_CONF_FILE}'
83
84# This command checks if "--enable-features=LacrosSupport" is present in
85# /etc/chrome_dev.conf. If it is not, then it is added.
86# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
87# to modify chrome_dev.conf. Either revisit this policy or find another
88# mechanism to pass configuration to ash-chrome.
89ENABLE_LACROS_VIA_CONF_COMMAND = f"""
90 if ! grep -q "^--enable-features=LacrosSupport$" {_CONF_FILE}; then
91 echo "--enable-features=LacrosSupport" >> {_CONF_FILE};
92 echo {MODIFIED_CONF_FILE};
93 fi
94"""
95
96# This command checks if "--lacros-chrome-path=" is present with the right value
97# in /etc/chrome_dev.conf. If it is not, then all previous instances are removed
98# and the new one is added.
99# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
100# to modify chrome_dev.conf. Either revisit this policy or find another
101# mechanism to pass configuration to ash-chrome.
102_SET_LACROS_PATH_VIA_CONF_COMMAND = """
103 if ! grep -q "^--lacros-chrome-path=%(lacros_path)s$" %(conf_file)s; then
104 sed 's/--lacros-chrome-path/#--lacros-chrome-path/' %(conf_file)s;
105 echo "--lacros-chrome-path=%(lacros_path)s" >> %(conf_file)s;
106 echo %(modified_conf_file)s;
107 fi
108"""
Mike Frysingere65f3752014-12-08 00:46:39 -0500109
Ryan Cui3045c5d2012-07-13 18:00:33 -0700110def _UrlBaseName(url):
111 """Return the last component of the URL."""
112 return url.rstrip('/').rpartition('/')[-1]
113
114
Yu-Ju Hongc54d3342014-05-14 12:42:06 -0700115class DeployFailure(failures_lib.StepFailure):
David James88e6f032013-03-02 08:13:20 -0800116 """Raised whenever the deploy fails."""
117
118
Ryan Cui7193a7e2013-04-26 14:15:19 -0700119DeviceInfo = collections.namedtuple(
120 'DeviceInfo', ['target_dir_size', 'target_fs_free'])
121
122
Ryan Cui3045c5d2012-07-13 18:00:33 -0700123class DeployChrome(object):
124 """Wraps the core deployment functionality."""
Mike Frysingere65f3752014-12-08 00:46:39 -0500125
Ryan Cuia56a71e2012-10-18 18:40:35 -0700126 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -0700127 """Initialize the class.
128
Mike Frysinger02e1e072013-11-10 22:11:34 -0500129 Args:
Mike Frysingerc3061a62015-06-04 04:16:18 -0400130 options: options object.
Ryan Cuie535b172012-10-19 18:25:03 -0700131 tempdir: Scratch space for the class. Caller has responsibility to clean
132 it up.
Steve Funge984a532013-11-25 17:09:25 -0800133 staging_dir: Directory to stage the files to.
Ryan Cuie535b172012-10-19 18:25:03 -0700134 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700135 self.tempdir = tempdir
136 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -0700137 self.staging_dir = staging_dir
Robert Flack1dc7ea82014-11-26 13:50:24 -0500138 if not self.options.staging_only:
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700139 if options.device:
140 hostname = options.device.hostname
141 port = options.device.port
142 else:
143 hostname = options.to
144 port = options.port
145 self.device = remote.ChromiumOSDevice(hostname, port=port,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700146 ping=options.ping,
147 private_key=options.private_key,
148 include_dev_paths=False)
Steven Bennettsca73efa2018-07-10 13:36:56 -0700149 self._root_dir_is_still_readonly = multiprocessing.Event()
Steven Bennetts5a7c72d2016-10-17 20:04:46 +0000150
Erik Chen75a2f492020-08-06 19:15:11 -0700151 self._deployment_name = 'lacros' if options.lacros else 'chrome'
152 self.copy_paths = chrome_util.GetCopyPaths(self._deployment_name)
153
154 self.chrome_dir = LACROS_DIR if self.options.lacros else _CHROME_DIR
155
156 # Whether UI was stopped during setup.
157 self._stopped_ui = False
Steve Funge984a532013-11-25 17:09:25 -0800158
Ryan Cui7193a7e2013-04-26 14:15:19 -0700159 def _GetRemoteMountFree(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400160 result = self.device.run(DF_COMMAND % remote_dir)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700161 line = result.output.splitlines()[1]
Steve Funge984a532013-11-25 17:09:25 -0800162 value = line.split()[3]
163 multipliers = {
164 'G': 1024 * 1024 * 1024,
165 'M': 1024 * 1024,
166 'K': 1024,
167 }
168 return int(value.rstrip('GMK')) * multipliers.get(value[-1], 1)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700169
170 def _GetRemoteDirSize(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400171 result = self.device.run('du -ks %s' % remote_dir,
172 capture_output=True, encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700173 return int(result.output.split()[0])
174
175 def _GetStagingDirSize(self):
Mike Frysinger45602c72019-09-22 02:15:11 -0400176 result = cros_build_lib.dbg_run(['du', '-ks', self.staging_dir],
Mike Frysinger0282d222019-12-17 17:15:48 -0500177 stdout=True, capture_output=True,
Mike Frysingerb88d5fb2019-10-23 00:38:45 -0400178 encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700179 return int(result.output.split()[0])
180
Ryan Cui3045c5d2012-07-13 18:00:33 -0700181 def _ChromeFileInUse(self):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400182 result = self.device.run(LSOF_COMMAND_CHROME % (self.options.target_dir,),
183 check=False, capture_output=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700184 return result.returncode == 0
185
Justin TerAvestfac210e2017-04-13 11:39:00 -0600186 def _Reboot(self):
187 # A reboot in developer mode takes a while (and has delays), so the user
188 # will have time to read and act on the USB boot instructions below.
189 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
190 self.device.Reboot()
191
Ryan Cui3045c5d2012-07-13 18:00:33 -0700192 def _DisableRootfsVerification(self):
193 if not self.options.force:
194 logging.error('Detected that the device has rootfs verification enabled.')
195 logging.info('This script can automatically remove the rootfs '
196 'verification, which requires that it reboot the device.')
197 logging.info('Make sure the device is in developer mode!')
198 logging.info('Skip this prompt by specifying --force.')
Ben Chan37d64e52018-10-05 15:35:02 -0700199 if not cros_build_lib.BooleanPrompt('Remove rootfs verification?', False):
Steven Bennettsca73efa2018-07-10 13:36:56 -0700200 return False
Ryan Cui3045c5d2012-07-13 18:00:33 -0700201
202 logging.info('Removing rootfs verification from %s', self.options.to)
203 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
204 # Use --force to bypass the checks.
David Haddock3151d912017-10-24 03:50:32 +0000205 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
206 '--remove_rootfs_verification --force')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700207 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400208 self.device.run(cmd % partition, check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700209
Justin TerAvestfac210e2017-04-13 11:39:00 -0600210 self._Reboot()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700211
David James88e6f032013-03-02 08:13:20 -0800212 # Now that the machine has been rebooted, we need to kill Chrome again.
Erik Chen75a2f492020-08-06 19:15:11 -0700213 self._KillAshChromeIfNeeded()
David James88e6f032013-03-02 08:13:20 -0800214
215 # Make sure the rootfs is writable now.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500216 self._MountRootfsAsWritable(check=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700217
Steven Bennettsca73efa2018-07-10 13:36:56 -0700218 return True
219
Ryan Cui3045c5d2012-07-13 18:00:33 -0700220 def _CheckUiJobStarted(self):
221 # status output is in the format:
222 # <job_name> <status> ['process' <pid>].
223 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800224 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400225 result = self.device.run('status ui', capture_output=True,
226 encoding='utf-8')
Ryan Cuif2d1a582013-02-19 14:08:13 -0800227 except cros_build_lib.RunCommandError as e:
228 if 'Unknown job' in e.result.error:
229 return False
230 else:
231 raise e
232
Ryan Cui3045c5d2012-07-13 18:00:33 -0700233 return result.output.split()[1].split('/')[0] == 'start'
234
Erik Chen75a2f492020-08-06 19:15:11 -0700235 def _KillLacrosChrome(self):
236 """This method kills lacros-chrome on the device, if it's running."""
237 self.device.run(_KILL_LACROS_CHROME_CMD %
238 {'lacros_dir': self.options.target_dir}, check=False)
239
240 def _KillAshChromeIfNeeded(self):
241 """This method kills ash-chrome on the device, if it's running.
242
243 This method calls 'stop ui', and then also manually pkills both ash-chrome
244 and the session manager.
245 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700246 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800247 logging.info('Shutting down Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400248 self.device.run('stop ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700249
250 # Developers sometimes run session_manager manually, in which case we'll
251 # need to help shut the chrome processes down.
252 try:
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700253 with timeout_util.Timeout(self.options.process_timeout):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700254 while self._ChromeFileInUse():
255 logging.warning('The chrome binary on the device is in use.')
256 logging.warning('Killing chrome and session_manager processes...\n')
257
Mike Frysinger3459bf52020-03-31 00:52:11 -0400258 self.device.run("pkill 'chrome|session_manager'", check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700259 # Wait for processes to actually terminate
260 time.sleep(POST_KILL_WAIT)
261 logging.info('Rechecking the chrome binary...')
David James3432acd2013-11-27 10:02:18 -0800262 except timeout_util.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800263 msg = ('Could not kill processes after %s seconds. Please exit any '
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700264 'running chrome processes and try again.'
265 % self.options.process_timeout)
David James88e6f032013-03-02 08:13:20 -0800266 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700267
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500268 def _MountRootfsAsWritable(self, check=False):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800269 """Mounts the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700270
Steven Bennettsca73efa2018-07-10 13:36:56 -0700271 If the command fails and the root dir is not writable then this function
272 sets self._root_dir_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700273
Mike Frysinger02e1e072013-11-10 22:11:34 -0500274 Args:
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500275 check: See remote.RemoteAccess.RemoteSh for details.
David James88e6f032013-03-02 08:13:20 -0800276 """
Yu-Ju Hong7e116ac2014-01-03 17:08:26 -0800277 # TODO: Should migrate to use the remount functions in remote_access.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400278 result = self.device.run(MOUNT_RW_COMMAND, check=check,
279 capture_output=True, encoding='utf-8')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700280 if result.returncode and not self.device.IsDirWritable('/'):
281 self._root_dir_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700282
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800283 def _EnsureTargetDir(self):
284 """Ensures that the target directory exists on the remote device."""
285 target_dir = self.options.target_dir
286 # Any valid /opt directory should already exist so avoid the remote call.
287 if os.path.commonprefix([target_dir, '/opt']) == '/opt':
288 return
Mike Frysinger3459bf52020-03-31 00:52:11 -0400289 self.device.run(['mkdir', '-p', '--mode', '0775', target_dir])
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800290
Ryan Cui7193a7e2013-04-26 14:15:19 -0700291 def _GetDeviceInfo(self):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800292 """Returns the disk space used and available for the target diectory."""
Ryan Cui7193a7e2013-04-26 14:15:19 -0700293 steps = [
294 functools.partial(self._GetRemoteDirSize, self.options.target_dir),
295 functools.partial(self._GetRemoteMountFree, self.options.target_dir)
296 ]
297 return_values = parallel.RunParallelSteps(steps, return_values=True)
298 return DeviceInfo(*return_values)
299
300 def _CheckDeviceFreeSpace(self, device_info):
301 """See if target device has enough space for Chrome.
302
Mike Frysinger02e1e072013-11-10 22:11:34 -0500303 Args:
Ryan Cui7193a7e2013-04-26 14:15:19 -0700304 device_info: A DeviceInfo named tuple.
305 """
306 effective_free = device_info.target_dir_size + device_info.target_fs_free
307 staging_size = self._GetStagingDirSize()
308 if effective_free < staging_size:
Daniel Erat1ae46382014-08-14 10:23:39 -0700309 raise DeployFailure(
310 'Not enough free space on the device. Required: %s MiB, '
Mike Frysinger93e8ffa2019-07-03 20:24:18 -0400311 'actual: %s MiB.' % (staging_size // 1024, effective_free // 1024))
Ryan Cui7193a7e2013-04-26 14:15:19 -0700312 if device_info.target_fs_free < (100 * 1024):
313 logging.warning('The device has less than 100MB free. deploy_chrome may '
314 'hang during the transfer.')
315
Satoru Takabayashif2893002017-06-20 14:52:48 +0900316 def _ShouldUseCompression(self):
317 """Checks if compression should be used for rsync."""
318 if self.options.compress == 'always':
319 return True
320 elif self.options.compress == 'never':
321 return False
322 elif self.options.compress == 'auto':
323 return not self.device.HasGigabitEthernet()
324
Ryan Cui3045c5d2012-07-13 18:00:33 -0700325 def _Deploy(self):
Erik Chen75a2f492020-08-06 19:15:11 -0700326 logging.info('Copying %s to %s on device...', self._deployment_name,
327 self.options.target_dir)
Ilja H. Friedel0ab63e12017-03-28 13:29:48 -0700328 # CopyToDevice will fall back to scp if rsync is corrupted on stateful.
329 # This does not work for deploy.
Satoru Takabayashiab380bc2015-01-15 16:00:41 +0900330 if not self.device.HasRsync():
331 raise DeployFailure(
David Pursellcfd58872015-03-19 09:15:48 -0700332 'rsync is not found on the device.\n'
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500333 'Run dev_install on the device to get rsync installed.')
Robert Flack1dc7ea82014-11-26 13:50:24 -0500334 self.device.CopyToDevice('%s/' % os.path.abspath(self.staging_dir),
335 self.options.target_dir,
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700336 mode='rsync', inplace=True,
Satoru Takabayashif2893002017-06-20 14:52:48 +0900337 compress=self._ShouldUseCompression(),
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700338 debug_level=logging.INFO,
Robert Flack1dc7ea82014-11-26 13:50:24 -0500339 verbose=self.options.verbose)
Ben Pastene5f03b052019-08-12 18:03:24 -0700340
341 # Set the security context on the default Chrome dir if that's where it's
342 # getting deployed, and only on SELinux supported devices.
Erik Chen75a2f492020-08-06 19:15:11 -0700343 if not self.options.lacros and self.device.IsSELinuxAvailable() and (
Ben Pastene5f03b052019-08-12 18:03:24 -0700344 _CHROME_DIR in (self.options.target_dir, self.options.mount_dir)):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400345 self.device.run(['restorecon', '-R', _CHROME_DIR])
Steve Funge984a532013-11-25 17:09:25 -0800346
347 for p in self.copy_paths:
Steve Funge984a532013-11-25 17:09:25 -0800348 if p.mode:
349 # Set mode if necessary.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400350 self.device.run('chmod %o %s/%s' % (
David Haddock3151d912017-10-24 03:50:32 +0000351 p.mode, self.options.target_dir, p.src if not p.dest else p.dest))
Steve Funge984a532013-11-25 17:09:25 -0800352
Erik Chen75a2f492020-08-06 19:15:11 -0700353 if self.options.lacros:
354 self.device.run(['chown', '-R', 'chronos:chronos',
355 self.options.target_dir])
356
Daniel Erat0fce5bf2017-09-28 17:29:23 -0700357 # Send SIGHUP to dbus-daemon to tell it to reload its configs. This won't
358 # pick up major changes (bus type, logging, etc.), but all we care about is
359 # getting the latest policy from /opt/google/chrome/dbus so that Chrome will
360 # be authorized to take ownership of its service names.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400361 self.device.run(DBUS_RELOAD_COMMAND, check=False)
Justin TerAvestfac210e2017-04-13 11:39:00 -0600362
Erik Chen75a2f492020-08-06 19:15:11 -0700363 if self.options.startui and self._stopped_ui:
Steve Funge984a532013-11-25 17:09:25 -0800364 logging.info('Starting UI...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400365 self.device.run('start ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700366
Ben Pastenee484b342020-06-30 18:29:27 -0700367 def _DeployTestBinaries(self):
368 """Deploys any local test binary to _CHROME_TEST_BIN_DIR on the device.
369
370 There could be several binaries located in the local build dir, so compare
371 what's already present on the device in _CHROME_TEST_BIN_DIR , and copy
372 over any that we also built ourselves.
373 """
374 r = self.device.run(_FIND_TEST_BIN_CMD, check=False)
375 if r.returncode != 0:
376 raise DeployFailure('Unable to ls contents of %s' % _CHROME_TEST_BIN_DIR)
377 binaries_to_copy = []
378 for f in r.output.splitlines():
379 binaries_to_copy.append(
380 chrome_util.Path(os.path.basename(f), exe=True, optional=True))
381
382 staging_dir = os.path.join(
383 self.tempdir, os.path.basename(_CHROME_TEST_BIN_DIR))
384 _PrepareStagingDir(self.options, self.tempdir, staging_dir,
385 copy_paths=binaries_to_copy)
386 self.device.CopyToDevice(
387 staging_dir, os.path.dirname(_CHROME_TEST_BIN_DIR), mode='rsync')
388
David James88e6f032013-03-02 08:13:20 -0800389 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700390 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800391 logging.info('Testing connection to the device...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400392 self.device.run('true')
David James88e6f032013-03-02 08:13:20 -0800393 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700394 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800395 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700396
Avery Musbach3edff0e2020-03-27 13:35:53 -0700397 def _CheckBoard(self):
398 """Check that the Chrome build is targeted for the device board."""
399 if self.options.board == self.device.board:
400 return
401 logging.warning('Device board is %s whereas target board is %s.',
402 self.device.board, self.options.board)
403 if self.options.force:
404 return
405 if not cros_build_lib.BooleanPrompt('Continue despite board mismatch?',
406 False):
407 raise DeployFailure('Aborted.')
408
Steve Funge984a532013-11-25 17:09:25 -0800409 def _CheckDeployType(self):
Steve Fung63d705d2014-03-16 03:14:03 -0700410 if self.options.build_dir:
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700411 def BinaryExists(filename):
412 """Checks if the passed-in file is present in the build directory."""
413 return os.path.exists(os.path.join(self.options.build_dir, filename))
414
Erik Chen75a2f492020-08-06 19:15:11 -0700415 # In the future, lacros-chrome and ash-chrome will likely be named
416 # something other than 'chrome' to avoid confusion.
Daniel Erat9813f0e2014-11-12 11:00:28 -0700417 # Handle non-Chrome deployments.
418 if not BinaryExists('chrome'):
Daniel Eratf53bd3a2016-12-02 11:28:36 -0700419 if BinaryExists('app_shell'):
Daniel Erat9813f0e2014-11-12 11:00:28 -0700420 self.copy_paths = chrome_util.GetCopyPaths('app_shell')
421
David James88e6f032013-03-02 08:13:20 -0800422 def _PrepareStagingDir(self):
Steve Funge984a532013-11-25 17:09:25 -0800423 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir,
424 self.copy_paths, self.chrome_dir)
David James88e6f032013-03-02 08:13:20 -0800425
Thiago Goncales12793312013-05-23 11:26:17 -0700426 def _MountTarget(self):
427 logging.info('Mounting Chrome...')
428
Anushruth8d797672019-10-17 12:22:31 -0700429 # Create directory if does not exist.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400430 self.device.run(_MKDIR_P_CMD % self.options.mount_dir)
Anushruth8d797672019-10-17 12:22:31 -0700431 try:
432 # Umount the existing mount on mount_dir if present first.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400433 self.device.run(_UMOUNT_DIR_IF_MOUNTPOINT_CMD %
434 {'dir': self.options.mount_dir})
Anushruth8d797672019-10-17 12:22:31 -0700435 except cros_build_lib.RunCommandError as e:
436 logging.error('Failed to umount %s', self.options.mount_dir)
437 # If there is a failure, check if some processs is using the mount_dir.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400438 result = self.device.run(LSOF_COMMAND % (self.options.mount_dir,),
439 check=False, capture_output=True,
440 encoding='utf-8')
Anushruth8d797672019-10-17 12:22:31 -0700441 logging.error('lsof %s -->', self.options.mount_dir)
442 logging.error(result.stdout)
443 raise e
444
Mike Frysinger3459bf52020-03-31 00:52:11 -0400445 self.device.run(_BIND_TO_FINAL_DIR_CMD % (self.options.target_dir,
446 self.options.mount_dir))
Anushruth8d797672019-10-17 12:22:31 -0700447
Thiago Goncales12793312013-05-23 11:26:17 -0700448 # Chrome needs partition to have exec and suid flags set
Mike Frysinger3459bf52020-03-31 00:52:11 -0400449 self.device.run(_SET_MOUNT_FLAGS_CMD % (self.options.mount_dir,))
Robert Flack1dc7ea82014-11-26 13:50:24 -0500450
451 def Cleanup(self):
452 """Clean up RemoteDevice."""
453 if not self.options.staging_only:
454 self.device.Cleanup()
Thiago Goncales12793312013-05-23 11:26:17 -0700455
David James88e6f032013-03-02 08:13:20 -0800456 def Perform(self):
Steve Funge984a532013-11-25 17:09:25 -0800457 self._CheckDeployType()
458
David James88e6f032013-03-02 08:13:20 -0800459 # If requested, just do the staging step.
460 if self.options.staging_only:
461 self._PrepareStagingDir()
462 return 0
463
Erik Chen75a2f492020-08-06 19:15:11 -0700464 # Check that the build matches the device. Lacros-chrome skips this check as
465 # it's currently board independent. This means that it's possible to deploy
466 # a build of lacros-chrome with a mismatched architecture. We don't try to
467 # prevent this developer foot-gun.
468 if not self.options.lacros:
469 self._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700470
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800471 # Ensure that the target directory exists before running parallel steps.
472 self._EnsureTargetDir()
473
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800474 logging.info('Preparing device')
Steve Funge984a532013-11-25 17:09:25 -0800475 steps = [self._GetDeviceInfo, self._CheckConnection,
Erik Chen75a2f492020-08-06 19:15:11 -0700476 self._MountRootfsAsWritable,
Steve Funge984a532013-11-25 17:09:25 -0800477 self._PrepareStagingDir]
Erik Chen75a2f492020-08-06 19:15:11 -0700478
479 # If this is a lacros build, we only want to restart ash-chrome if
480 # necessary, which is done below.
ChromeOS Developer78345852020-08-11 23:09:48 -0700481 if not self.options.lacros:
482 self._stopped_ui = True
Erik Chen75a2f492020-08-06 19:15:11 -0700483 steps += ([self._KillLacrosChrome] if self.options.lacros else
484 [self._KillAshChromeIfNeeded])
Ryan Cui7193a7e2013-04-26 14:15:19 -0700485 ret = parallel.RunParallelSteps(steps, halt_on_error=True,
486 return_values=True)
487 self._CheckDeviceFreeSpace(ret[0])
David James88e6f032013-03-02 08:13:20 -0800488
Steven Bennettsca73efa2018-07-10 13:36:56 -0700489 # If the root dir is not writable, try disabling rootfs verification.
490 # (We always do this by default so that developers can write to
491 # /etc/chriome_dev.conf and other directories in the rootfs).
492 if self._root_dir_is_still_readonly.is_set():
493 if self.options.noremove_rootfs_verification:
494 logging.warning('Skipping disable rootfs verification.')
495 elif not self._DisableRootfsVerification():
496 logging.warning('Failed to disable rootfs verification.')
497
498 # If the target dir is still not writable (i.e. the user opted out or the
499 # command failed), abort.
500 if not self.device.IsDirWritable(self.options.target_dir):
Erik Chen75a2f492020-08-06 19:15:11 -0700501 if self.options.startui and self._stopped_ui:
Steven Bennettsca73efa2018-07-10 13:36:56 -0700502 logging.info('Restarting Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400503 self.device.run('start ui')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700504 raise DeployFailure('Target location is not writable. Aborting.')
David James88e6f032013-03-02 08:13:20 -0800505
Thiago Goncales12793312013-05-23 11:26:17 -0700506 if self.options.mount_dir is not None:
507 self._MountTarget()
508
Erik Chen75a2f492020-08-06 19:15:11 -0700509 if self.options.lacros:
510 # Update /etc/chrome_dev.conf to include appropriate flags.
511 restart_ui = False
512 result = self.device.run(ENABLE_LACROS_VIA_CONF_COMMAND, shell=True)
513 if result.stdout.strip() == MODIFIED_CONF_FILE:
514 restart_ui = True
515 result = self.device.run(_SET_LACROS_PATH_VIA_CONF_COMMAND % {
516 'conf_file': _CONF_FILE, 'lacros_path': self.options.target_dir,
517 'modified_conf_file': MODIFIED_CONF_FILE}, shell=True)
518 if result.stdout.strip() == MODIFIED_CONF_FILE:
519 restart_ui = True
520
521 if restart_ui:
522 self._KillAshChromeIfNeeded()
523
David James88e6f032013-03-02 08:13:20 -0800524 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700525 self._Deploy()
Ben Pastenee484b342020-06-30 18:29:27 -0700526 if self.options.deploy_test_binaries:
527 self._DeployTestBinaries()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700528
529
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700530def ValidateStagingFlags(value):
531 """Convert formatted string to dictionary."""
532 return chrome_util.ProcessShellFlags(value)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700533
534
Steven Bennetts368c3e52016-09-23 13:05:21 -0700535def ValidateGnArgs(value):
536 """Convert GN_ARGS-formatted string to dictionary."""
537 return gn_helpers.FromGNArgs(value)
538
539
Ryan Cuie535b172012-10-19 18:25:03 -0700540def _CreateParser():
541 """Create our custom parser."""
Mike Frysingerc3061a62015-06-04 04:16:18 -0400542 parser = commandline.ArgumentParser(description=__doc__, caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700543
Ryan Cuia56a71e2012-10-18 18:40:35 -0700544 # TODO(rcui): Have this use the UI-V2 format of having source and target
545 # device be specified as positional arguments.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400546 parser.add_argument('--force', action='store_true', default=False,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700547 help='Skip all prompts (such as the prompt for disabling '
548 'of rootfs verification). This may result in the '
549 'target machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800550 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
Mike Frysingerc3061a62015-06-04 04:16:18 -0400551 parser.add_argument('--board', default=sdk_board_env,
Mike Frysinger80de5012019-08-01 14:10:53 -0400552 help='The board the Chrome build is targeted for. When '
Mike Frysingerc3061a62015-06-04 04:16:18 -0400553 "in a 'cros chrome-sdk' shell, defaults to the SDK "
Mike Frysinger80de5012019-08-01 14:10:53 -0400554 'board.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400555 parser.add_argument('--build-dir', type='path',
556 help='The directory with Chrome build artifacts to '
557 'deploy from. Typically of format '
558 '<chrome_root>/out/Debug. When this option is used, '
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700559 'the GN_ARGS environment variable must be set.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400560 parser.add_argument('--target-dir', type='path',
561 default=None,
562 help='Target directory on device to deploy Chrome into.')
563 parser.add_argument('-g', '--gs-path', type='gs_path',
564 help='GS path that contains the chrome to deploy.')
Adrian Eldera2c548a2017-11-07 19:01:29 -0500565 parser.add_argument('--private-key', type='path', default=None,
566 help='An ssh private key to use when deploying to '
567 'a CrOS device.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400568 parser.add_argument('--nostartui', action='store_false', dest='startui',
569 default=True,
570 help="Don't restart the ui daemon after deployment.")
571 parser.add_argument('--nostrip', action='store_false', dest='dostrip',
572 default=True,
573 help="Don't strip binaries during deployment. Warning: "
574 'the resulting binaries will be very large!')
575 parser.add_argument('-p', '--port', type=int, default=remote.DEFAULT_SSH_PORT,
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700576 help='This arg is deprecated. Please use --device '
577 'instead.')
578 parser.add_argument('-t', '--to', deprecated='Use --device instead',
579 help='This arg is deprecated. Please use --device '
580 'instead.')
581 parser.add_argument(
582 '-d', '--device',
583 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
584 help='Device hostname or IP in the format hostname[:port].')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400585 parser.add_argument('--mount-dir', type='path', default=None,
586 help='Deploy Chrome in target directory and bind it '
587 'to the directory specified by this flag.'
588 'Any existing mount on this directory will be '
589 'umounted first.')
590 parser.add_argument('--mount', action='store_true', default=False,
591 help='Deploy Chrome to default target directory and bind '
592 'it to the default mount directory.'
593 'Any existing mount on this directory will be '
594 'umounted first.')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700595 parser.add_argument('--noremove-rootfs-verification', action='store_true',
596 default=False, help='Never remove rootfs verification.')
Ben Pastenee484b342020-06-30 18:29:27 -0700597 parser.add_argument('--deploy-test-binaries', action='store_true',
598 default=False,
599 help='Also deploy any test binaries to %s. Useful for '
600 'running any Tast tests that execute these '
601 'binaries.' % _CHROME_TEST_BIN_DIR)
Erik Chen75a2f492020-08-06 19:15:11 -0700602 parser.add_argument('--lacros', action='store_true', default=False,
603 help='Deploys lacros-chrome rather than ash-chrome.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700604
Mike Frysingerc3061a62015-06-04 04:16:18 -0400605 group = parser.add_argument_group('Advanced Options')
606 group.add_argument('-l', '--local-pkg-path', type='path',
607 help='Path to local chrome prebuilt package to deploy.')
608 group.add_argument('--sloppy', action='store_true', default=False,
609 help='Ignore when mandatory artifacts are missing.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700610 group.add_argument('--staging-flags', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400611 help=('Extra flags to control staging. Valid flags are - '
612 '%s' % ', '.join(chrome_util.STAGING_FLAGS)))
Steven Bennetts46a84c32016-08-12 15:20:46 -0700613 # TODO(stevenjb): Remove --strict entirely once removed from the ebuild.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400614 group.add_argument('--strict', action='store_true', default=False,
Steven Bennetts46a84c32016-08-12 15:20:46 -0700615 help='Deprecated. Default behavior is "strict". Use '
616 '--sloppy to omit warnings for missing optional '
617 'files.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400618 group.add_argument('--strip-flags', default=None,
619 help="Flags to call the 'strip' binutil tool with. "
Mike Frysinger80de5012019-08-01 14:10:53 -0400620 'Overrides the default arguments.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400621 group.add_argument('--ping', action='store_true', default=False,
622 help='Ping the device before connection attempt.')
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700623 group.add_argument('--process-timeout', type=int,
624 default=KILL_PROC_MAX_WAIT,
625 help='Timeout for process shutdown.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700626
Mike Frysingerc3061a62015-06-04 04:16:18 -0400627 group = parser.add_argument_group(
628 'Metadata Overrides (Advanced)',
629 description='Provide all of these overrides in order to remove '
630 'dependencies on metadata.json existence.')
631 group.add_argument('--target-tc', action='store', default=None,
632 help='Override target toolchain name, e.g. '
633 'x86_64-cros-linux-gnu')
634 group.add_argument('--toolchain-url', action='store', default=None,
635 help='Override toolchain url format pattern, e.g. '
636 '2014/04/%%(target)s-2014.04.23.220740.tar.xz')
Aviv Keshet1c986f32014-04-24 13:20:49 -0700637
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700638 # DEPRECATED: --gyp-defines is ignored, but retained for backwards
639 # compatibility. TODO(stevenjb): Remove once eliminated from the ebuild.
640 parser.add_argument('--gyp-defines', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400641 help=argparse.SUPPRESS)
Steven Bennetts368c3e52016-09-23 13:05:21 -0700642
643 # GN_ARGS (args.gn) used to build Chrome. Influences which files are staged
644 # when --build-dir is set. Defaults to reading from the GN_ARGS env variable.
645 # CURRENLY IGNORED, ADDED FOR FORWARD COMPATABILITY.
646 parser.add_argument('--gn-args', default=None, type=ValidateGnArgs,
647 help=argparse.SUPPRESS)
648
Ryan Cuia56a71e2012-10-18 18:40:35 -0700649 # Path of an empty directory to stage chrome artifacts to. Defaults to a
650 # temporary directory that is removed when the script finishes. If the path
651 # is specified, then it will not be removed.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400652 parser.add_argument('--staging-dir', type='path', default=None,
653 help=argparse.SUPPRESS)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700654 # Only prepare the staging directory, and skip deploying to the device.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400655 parser.add_argument('--staging-only', action='store_true', default=False,
656 help=argparse.SUPPRESS)
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700657 # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
658 # is used as-is, and not normalized. Used by the Chrome ebuild to skip
659 # fetching the SDK toolchain.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400660 parser.add_argument('--strip-bin', default=None, help=argparse.SUPPRESS)
Satoru Takabayashif2893002017-06-20 14:52:48 +0900661 parser.add_argument('--compress', action='store', default='auto',
662 choices=('always', 'never', 'auto'),
663 help='Choose the data compression behavior. Default '
664 'is set to "auto", that disables compression if '
665 'the target device has a gigabit ethernet port.')
Ryan Cuie535b172012-10-19 18:25:03 -0700666 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700667
Ryan Cuie535b172012-10-19 18:25:03 -0700668
669def _ParseCommandLine(argv):
670 """Parse args, and run environment-independent checks."""
671 parser = _CreateParser()
Mike Frysingerc3061a62015-06-04 04:16:18 -0400672 options = parser.parse_args(argv)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700673
Ryan Cuia56a71e2012-10-18 18:40:35 -0700674 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
675 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800676 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700677 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
678 parser.error('Cannot specify both --build_dir and '
679 '--gs-path/--local-pkg-patch')
Erik Chen75a2f492020-08-06 19:15:11 -0700680 if options.lacros:
681 if options.board:
682 parser.error('--board is not supported with --lacros')
683 # The stripping implemented in this file rely on the cros-chrome-sdk, which
684 # is inappropriate for Lacros. Lacros stripping is currently not
685 # implemented.
686 if options.dostrip:
687 parser.error('--lacros requires --nostrip')
688 if options.mount_dir or options.mount:
689 parser.error('--lacros does not support --mount or --mount-dir')
690 if options.deploy_test_binaries:
691 parser.error('--lacros does not support --deploy-test-binaries')
692 if options.local_pkg_path:
693 parser.error('--lacros does not support --local-pkg-path')
694 else:
695 if not options.board:
696 parser.error('--board is required')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700697 if options.gs_path and options.local_pkg_path:
698 parser.error('Cannot specify both --gs-path and --local-pkg-path')
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700699 if not (options.staging_only or options.to or options.device):
700 parser.error('Need to specify --device')
Steven Bennetts46a84c32016-08-12 15:20:46 -0700701 if options.staging_flags and not options.build_dir:
702 parser.error('--staging-flags require --build-dir to be set.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700703
Steven Bennetts46a84c32016-08-12 15:20:46 -0700704 if options.strict:
705 logging.warning('--strict is deprecated.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700706 if options.gyp_defines:
707 logging.warning('--gyp-defines is deprecated.')
Thiago Goncales12793312013-05-23 11:26:17 -0700708
709 if options.mount or options.mount_dir:
710 if not options.target_dir:
711 options.target_dir = _CHROME_DIR_MOUNT
712 else:
713 if not options.target_dir:
Erik Chen75a2f492020-08-06 19:15:11 -0700714 options.target_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Thiago Goncales12793312013-05-23 11:26:17 -0700715
716 if options.mount and not options.mount_dir:
717 options.mount_dir = _CHROME_DIR
718
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700719 if options.to:
720 if options.device:
721 parser.error('--to and --device are mutually exclusive.')
722 else:
723 logging.warning(
724 "The args '--to' & '--port' are deprecated. Please use '--device' "
725 'instead.')
726
Mike Frysingerc3061a62015-06-04 04:16:18 -0400727 return options
Ryan Cui3045c5d2012-07-13 18:00:33 -0700728
729
Mike Frysingerc3061a62015-06-04 04:16:18 -0400730def _PostParseCheck(options):
Steve Funge984a532013-11-25 17:09:25 -0800731 """Perform some usage validation (after we've parsed the arguments).
Ryan Cui3045c5d2012-07-13 18:00:33 -0700732
733 Args:
Mike Frysinger5fe3f222016-09-01 00:14:16 -0400734 options: The options object returned by the cli parser.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700735 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700736 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
737 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
738
Steven Bennetts368c3e52016-09-23 13:05:21 -0700739 if not options.gn_args:
740 gn_env = os.getenv('GN_ARGS')
741 if gn_env is not None:
742 options.gn_args = gn_helpers.FromGNArgs(gn_env)
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800743 logging.debug('GN_ARGS taken from environment: %s', options.gn_args)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700744
Steven Bennetts60600462016-05-12 10:40:20 -0700745 if not options.staging_flags:
746 use_env = os.getenv('USE')
747 if use_env is not None:
748 options.staging_flags = ' '.join(set(use_env.split()).intersection(
749 chrome_util.STAGING_FLAGS))
750 logging.info('Staging flags taken from USE in environment: %s',
751 options.staging_flags)
752
Ryan Cuia56a71e2012-10-18 18:40:35 -0700753
Ryan Cui504db722013-01-22 11:48:01 -0800754def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700755 """Get the chrome prebuilt tarball from GS.
756
Mike Frysinger02e1e072013-11-10 22:11:34 -0500757 Returns:
758 Path to the fetched chrome tarball.
Ryan Cuia56a71e2012-10-18 18:40:35 -0700759 """
David James9374aac2013-10-08 16:00:17 -0700760 gs_ctx = gs.GSContext(cache_dir=cache_dir, init_boto=True)
Mike Frysinger7ad4fd62014-02-11 16:53:56 -0500761 files = gs_ctx.LS(gs_path)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800762 files = [found for found in files if
763 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
764 if not files:
765 raise Exception('No chrome package found at %s' % gs_path)
766 elif len(files) > 1:
767 # - Users should provide us with a direct link to either a stripped or
768 # unstripped chrome package.
769 # - In the case of being provided with an archive directory, where both
770 # stripped and unstripped chrome available, use the stripped chrome
771 # package.
772 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
773 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
774 files = [f for f in files if not 'unstripped' in f]
775 assert len(files) == 1
776 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800777
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800778 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800779 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800780 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
781 chrome_path = os.path.join(tempdir, filename)
782 assert os.path.exists(chrome_path)
783 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700784
785
Ryan Cuif890a3e2013-03-07 18:57:06 -0800786@contextlib.contextmanager
787def _StripBinContext(options):
788 if not options.dostrip:
789 yield None
790 elif options.strip_bin:
791 yield options.strip_bin
792 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800793 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800794 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
Aviv Keshet1c986f32014-04-24 13:20:49 -0700795 with sdk.Prepare(components=components, target_tc=options.target_tc,
796 toolchain_url=options.toolchain_url) as ctx:
Ryan Cui686ec052013-02-12 16:39:41 -0800797 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
798 constants.CHROME_ENV_FILE)
799 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
800 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
801 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800802 yield strip_bin
803
804
Steve Funge984a532013-11-25 17:09:25 -0800805def _PrepareStagingDir(options, tempdir, staging_dir, copy_paths=None,
Erik Chen75a2f492020-08-06 19:15:11 -0700806 chrome_dir=None):
Ryan Cuif890a3e2013-03-07 18:57:06 -0800807 """Place the necessary files in the staging directory.
808
809 The staging directory is the directory used to rsync the build artifacts over
810 to the device. Only the necessary Chrome build artifacts are put into the
811 staging directory.
812 """
Erik Chen75a2f492020-08-06 19:15:11 -0700813 if chrome_dir is None:
814 chrome_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Ryan Cui5866be02013-03-18 14:12:00 -0700815 osutils.SafeMakedirs(staging_dir)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400816 os.chmod(staging_dir, 0o755)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800817 if options.build_dir:
818 with _StripBinContext(options) as strip_bin:
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700819 strip_flags = (None if options.strip_flags is None else
820 shlex.split(options.strip_flags))
Ryan Cui686ec052013-02-12 16:39:41 -0800821 chrome_util.StageChromeFromBuildDir(
Steven Bennetts46a84c32016-08-12 15:20:46 -0700822 staging_dir, options.build_dir, strip_bin,
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700823 sloppy=options.sloppy, gn_args=options.gn_args,
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700824 staging_flags=options.staging_flags,
Steve Funge984a532013-11-25 17:09:25 -0800825 strip_flags=strip_flags, copy_paths=copy_paths)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700826 else:
827 pkg_path = options.local_pkg_path
828 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800829 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
830 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700831
832 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800833 logging.info('Extracting %s...', pkg_path)
Ryan Cui5866be02013-03-18 14:12:00 -0700834 # Extract only the ./opt/google/chrome contents, directly into the staging
835 # dir, collapsing the directory hierarchy.
Steve Funge984a532013-11-25 17:09:25 -0800836 if pkg_path[-4:] == '.zip':
Mike Frysinger45602c72019-09-22 02:15:11 -0400837 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800838 ['unzip', '-X', pkg_path, _ANDROID_DIR_EXTRACT_PATH, '-d',
839 staging_dir])
840 for filename in glob.glob(os.path.join(staging_dir, 'system/chrome/*')):
841 shutil.move(filename, staging_dir)
842 osutils.RmDir(os.path.join(staging_dir, 'system'), ignore_missing=True)
843 else:
Mike Frysinger45602c72019-09-22 02:15:11 -0400844 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800845 ['tar', '--strip-components', '4', '--extract',
846 '--preserve-permissions', '--file', pkg_path, '.%s' % chrome_dir],
847 cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700848
Ryan Cui71aa8de2013-04-19 16:12:55 -0700849
Ryan Cui3045c5d2012-07-13 18:00:33 -0700850def main(argv):
Mike Frysingerc3061a62015-06-04 04:16:18 -0400851 options = _ParseCommandLine(argv)
852 _PostParseCheck(options)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700853
Aviv Keshet01a82e92017-03-02 17:39:59 -0800854 with osutils.TempDir(set_global=True) as tempdir:
855 staging_dir = options.staging_dir
856 if not staging_dir:
857 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700858
Aviv Keshet01a82e92017-03-02 17:39:59 -0800859 deploy = DeployChrome(options, tempdir, staging_dir)
860 try:
861 deploy.Perform()
862 except failures_lib.StepFailure as ex:
863 raise SystemExit(str(ex).strip())
864 deploy.Cleanup()