blob: c12ae9b5e77f0c223c52151191b78adfd3661456 [file] [log] [blame]
Ryan Cui3045c5d2012-07-13 18:00:33 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Steve Funge984a532013-11-25 17:09:25 -08005"""Script that deploys a Chrome build to a device.
Ryan Cuia56a71e2012-10-18 18:40:35 -07006
7The script supports deploying Chrome from these sources:
8
91. A local build output directory, such as chromium/src/out/[Debug|Release].
102. A Chrome tarball uploaded by a trybot/official-builder to GoogleStorage.
113. A Chrome tarball existing locally.
12
13The script copies the necessary contents of the source location (tarball or
14build directory) and rsyncs the contents of the staging directory onto your
15device's rootfs.
16"""
Ryan Cui3045c5d2012-07-13 18:00:33 -070017
Mike Frysingerc3061a62015-06-04 04:16:18 -040018import argparse
Ryan Cui7193a7e2013-04-26 14:15:19 -070019import collections
Ryan Cuif890a3e2013-03-07 18:57:06 -080020import contextlib
Ryan Cui7193a7e2013-04-26 14:15:19 -070021import functools
Steve Funge984a532013-11-25 17:09:25 -080022import glob
David James88e6f032013-03-02 08:13:20 -080023import multiprocessing
Ryan Cui3045c5d2012-07-13 18:00:33 -070024import os
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090025import re
Ryan Cui5b7c2ed2013-03-18 18:45:46 -070026import shlex
Steve Funge984a532013-11-25 17:09:25 -080027import shutil
Ryan Cui3045c5d2012-07-13 18:00:33 -070028import time
Ryan Cui3045c5d2012-07-13 18:00:33 -070029
David Pursellcfd58872015-03-19 09:15:48 -070030from chromite.cli.cros import cros_chrome_sdk
Ryan Cuia56a71e2012-10-18 18:40:35 -070031from chromite.lib import chrome_util
Ryan Cuie535b172012-10-19 18:25:03 -070032from chromite.lib import commandline
Mike Frysinger47b10992021-01-23 00:23:25 -050033from chromite.lib import constants
Ralph Nathan91874ca2015-03-19 13:29:41 -070034from chromite.lib import cros_build_lib
35from chromite.lib import cros_logging as logging
Mike Frysinger47b10992021-01-23 00:23:25 -050036from chromite.lib import failures_lib
Ryan Cui777ff422012-12-07 13:12:54 -080037from chromite.lib import gs
Ryan Cui3045c5d2012-07-13 18:00:33 -070038from chromite.lib import osutils
David James88e6f032013-03-02 08:13:20 -080039from chromite.lib import parallel
Ryan Cui3045c5d2012-07-13 18:00:33 -070040from chromite.lib import remote_access as remote
Brian Sheedy86f12342020-10-29 15:30:02 -070041from chromite.lib import retry_util
David James3432acd2013-11-27 10:02:18 -080042from chromite.lib import timeout_util
Mike Frysinger47b10992021-01-23 00:23:25 -050043from chromite.third_party.gn_helpers import gn_helpers
Ryan Cui3045c5d2012-07-13 18:00:33 -070044
45
Ryan Cui3045c5d2012-07-13 18:00:33 -070046KERNEL_A_PARTITION = 2
47KERNEL_B_PARTITION = 4
48
49KILL_PROC_MAX_WAIT = 10
50POST_KILL_WAIT = 2
51
David Haddock3151d912017-10-24 03:50:32 +000052MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Anushruth8d797672019-10-17 12:22:31 -070053LSOF_COMMAND_CHROME = 'lsof %s/chrome'
54LSOF_COMMAND = 'lsof %s'
David Haddock3151d912017-10-24 03:50:32 +000055DBUS_RELOAD_COMMAND = 'killall -HUP dbus-daemon'
Ryan Cui3045c5d2012-07-13 18:00:33 -070056
Steve Funge984a532013-11-25 17:09:25 -080057_ANDROID_DIR = '/system/chrome'
58_ANDROID_DIR_EXTRACT_PATH = 'system/chrome/*'
59
David James2cb34002013-03-01 18:42:40 -080060_CHROME_DIR = '/opt/google/chrome'
Thiago Goncales12793312013-05-23 11:26:17 -070061_CHROME_DIR_MOUNT = '/mnt/stateful_partition/deploy_rootfs/opt/google/chrome'
Ben Pastenee484b342020-06-30 18:29:27 -070062_CHROME_TEST_BIN_DIR = '/usr/local/libexec/chrome-binary-tests'
David James2cb34002013-03-01 18:42:40 -080063
David Haddock3151d912017-10-24 03:50:32 +000064_UMOUNT_DIR_IF_MOUNTPOINT_CMD = (
65 'if mountpoint -q %(dir)s; then umount %(dir)s; fi')
66_BIND_TO_FINAL_DIR_CMD = 'mount --rbind %s %s'
67_SET_MOUNT_FLAGS_CMD = 'mount -o remount,exec,suid %s'
Anushruth8d797672019-10-17 12:22:31 -070068_MKDIR_P_CMD = 'mkdir -p --mode 0775 %s'
Ben Pastenee484b342020-06-30 18:29:27 -070069_FIND_TEST_BIN_CMD = 'find %s -maxdepth 1 -executable -type f' % (
70 _CHROME_TEST_BIN_DIR)
David Haddock3151d912017-10-24 03:50:32 +000071
72DF_COMMAND = 'df -k %s'
73
ChromeOS Developer536f59a2020-08-10 14:39:27 -070074LACROS_DIR = '/usr/local/lacros-chrome'
Erik Chen75a2f492020-08-06 19:15:11 -070075_CONF_FILE = '/etc/chrome_dev.conf'
76_KILL_LACROS_CHROME_CMD = 'pkill -f %(lacros_dir)s/chrome'
77MODIFIED_CONF_FILE = f'modified {_CONF_FILE}'
78
79# This command checks if "--enable-features=LacrosSupport" is present in
80# /etc/chrome_dev.conf. If it is not, then it is added.
81# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
82# to modify chrome_dev.conf. Either revisit this policy or find another
83# mechanism to pass configuration to ash-chrome.
84ENABLE_LACROS_VIA_CONF_COMMAND = f"""
85 if ! grep -q "^--enable-features=LacrosSupport$" {_CONF_FILE}; then
86 echo "--enable-features=LacrosSupport" >> {_CONF_FILE};
87 echo {MODIFIED_CONF_FILE};
88 fi
89"""
90
91# This command checks if "--lacros-chrome-path=" is present with the right value
92# in /etc/chrome_dev.conf. If it is not, then all previous instances are removed
93# and the new one is added.
94# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
95# to modify chrome_dev.conf. Either revisit this policy or find another
96# mechanism to pass configuration to ash-chrome.
97_SET_LACROS_PATH_VIA_CONF_COMMAND = """
98 if ! grep -q "^--lacros-chrome-path=%(lacros_path)s$" %(conf_file)s; then
99 sed 's/--lacros-chrome-path/#--lacros-chrome-path/' %(conf_file)s;
100 echo "--lacros-chrome-path=%(lacros_path)s" >> %(conf_file)s;
101 echo %(modified_conf_file)s;
102 fi
103"""
Mike Frysingere65f3752014-12-08 00:46:39 -0500104
Ryan Cui3045c5d2012-07-13 18:00:33 -0700105def _UrlBaseName(url):
106 """Return the last component of the URL."""
107 return url.rstrip('/').rpartition('/')[-1]
108
109
Yu-Ju Hongc54d3342014-05-14 12:42:06 -0700110class DeployFailure(failures_lib.StepFailure):
David James88e6f032013-03-02 08:13:20 -0800111 """Raised whenever the deploy fails."""
112
113
Ryan Cui7193a7e2013-04-26 14:15:19 -0700114DeviceInfo = collections.namedtuple(
115 'DeviceInfo', ['target_dir_size', 'target_fs_free'])
116
117
Ryan Cui3045c5d2012-07-13 18:00:33 -0700118class DeployChrome(object):
119 """Wraps the core deployment functionality."""
Mike Frysingere65f3752014-12-08 00:46:39 -0500120
Ryan Cuia56a71e2012-10-18 18:40:35 -0700121 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -0700122 """Initialize the class.
123
Mike Frysinger02e1e072013-11-10 22:11:34 -0500124 Args:
Mike Frysingerc3061a62015-06-04 04:16:18 -0400125 options: options object.
Ryan Cuie535b172012-10-19 18:25:03 -0700126 tempdir: Scratch space for the class. Caller has responsibility to clean
127 it up.
Steve Funge984a532013-11-25 17:09:25 -0800128 staging_dir: Directory to stage the files to.
Ryan Cuie535b172012-10-19 18:25:03 -0700129 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700130 self.tempdir = tempdir
131 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -0700132 self.staging_dir = staging_dir
Robert Flack1dc7ea82014-11-26 13:50:24 -0500133 if not self.options.staging_only:
Ben Pasteneb44d8e72021-02-19 13:56:43 -0800134 hostname = options.device.hostname
135 port = options.device.port
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700136 self.device = remote.ChromiumOSDevice(hostname, port=port,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700137 ping=options.ping,
138 private_key=options.private_key,
139 include_dev_paths=False)
Steven Bennettsca73efa2018-07-10 13:36:56 -0700140 self._root_dir_is_still_readonly = multiprocessing.Event()
Steven Bennetts5a7c72d2016-10-17 20:04:46 +0000141
Erik Chen75a2f492020-08-06 19:15:11 -0700142 self._deployment_name = 'lacros' if options.lacros else 'chrome'
143 self.copy_paths = chrome_util.GetCopyPaths(self._deployment_name)
144
145 self.chrome_dir = LACROS_DIR if self.options.lacros else _CHROME_DIR
146
147 # Whether UI was stopped during setup.
148 self._stopped_ui = False
Steve Funge984a532013-11-25 17:09:25 -0800149
Ryan Cui7193a7e2013-04-26 14:15:19 -0700150 def _GetRemoteMountFree(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400151 result = self.device.run(DF_COMMAND % remote_dir)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700152 line = result.output.splitlines()[1]
Steve Funge984a532013-11-25 17:09:25 -0800153 value = line.split()[3]
154 multipliers = {
155 'G': 1024 * 1024 * 1024,
156 'M': 1024 * 1024,
157 'K': 1024,
158 }
159 return int(value.rstrip('GMK')) * multipliers.get(value[-1], 1)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700160
161 def _GetRemoteDirSize(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400162 result = self.device.run('du -ks %s' % remote_dir,
163 capture_output=True, encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700164 return int(result.output.split()[0])
165
166 def _GetStagingDirSize(self):
Mike Frysinger45602c72019-09-22 02:15:11 -0400167 result = cros_build_lib.dbg_run(['du', '-ks', self.staging_dir],
Mike Frysinger0282d222019-12-17 17:15:48 -0500168 stdout=True, capture_output=True,
Mike Frysingerb88d5fb2019-10-23 00:38:45 -0400169 encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700170 return int(result.output.split()[0])
171
Ryan Cui3045c5d2012-07-13 18:00:33 -0700172 def _ChromeFileInUse(self):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400173 result = self.device.run(LSOF_COMMAND_CHROME % (self.options.target_dir,),
174 check=False, capture_output=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700175 return result.returncode == 0
176
Justin TerAvestfac210e2017-04-13 11:39:00 -0600177 def _Reboot(self):
178 # A reboot in developer mode takes a while (and has delays), so the user
179 # will have time to read and act on the USB boot instructions below.
180 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
181 self.device.Reboot()
182
Ryan Cui3045c5d2012-07-13 18:00:33 -0700183 def _DisableRootfsVerification(self):
184 if not self.options.force:
185 logging.error('Detected that the device has rootfs verification enabled.')
186 logging.info('This script can automatically remove the rootfs '
187 'verification, which requires that it reboot the device.')
188 logging.info('Make sure the device is in developer mode!')
189 logging.info('Skip this prompt by specifying --force.')
Ben Chan37d64e52018-10-05 15:35:02 -0700190 if not cros_build_lib.BooleanPrompt('Remove rootfs verification?', False):
Steven Bennettsca73efa2018-07-10 13:36:56 -0700191 return False
Ryan Cui3045c5d2012-07-13 18:00:33 -0700192
Ben Pasteneb44d8e72021-02-19 13:56:43 -0800193 logging.info('Removing rootfs verification from %s', self.options.device)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700194 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
195 # Use --force to bypass the checks.
David Haddock3151d912017-10-24 03:50:32 +0000196 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
197 '--remove_rootfs_verification --force')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700198 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400199 self.device.run(cmd % partition, check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700200
Justin TerAvestfac210e2017-04-13 11:39:00 -0600201 self._Reboot()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700202
David James88e6f032013-03-02 08:13:20 -0800203 # Now that the machine has been rebooted, we need to kill Chrome again.
Erik Chen75a2f492020-08-06 19:15:11 -0700204 self._KillAshChromeIfNeeded()
David James88e6f032013-03-02 08:13:20 -0800205
206 # Make sure the rootfs is writable now.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500207 self._MountRootfsAsWritable(check=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700208
Steven Bennettsca73efa2018-07-10 13:36:56 -0700209 return True
210
Ryan Cui3045c5d2012-07-13 18:00:33 -0700211 def _CheckUiJobStarted(self):
212 # status output is in the format:
213 # <job_name> <status> ['process' <pid>].
214 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800215 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400216 result = self.device.run('status ui', capture_output=True,
217 encoding='utf-8')
Ryan Cuif2d1a582013-02-19 14:08:13 -0800218 except cros_build_lib.RunCommandError as e:
219 if 'Unknown job' in e.result.error:
220 return False
221 else:
222 raise e
223
Ryan Cui3045c5d2012-07-13 18:00:33 -0700224 return result.output.split()[1].split('/')[0] == 'start'
225
Erik Chen75a2f492020-08-06 19:15:11 -0700226 def _KillLacrosChrome(self):
227 """This method kills lacros-chrome on the device, if it's running."""
228 self.device.run(_KILL_LACROS_CHROME_CMD %
229 {'lacros_dir': self.options.target_dir}, check=False)
230
231 def _KillAshChromeIfNeeded(self):
232 """This method kills ash-chrome on the device, if it's running.
233
234 This method calls 'stop ui', and then also manually pkills both ash-chrome
235 and the session manager.
236 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700237 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800238 logging.info('Shutting down Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400239 self.device.run('stop ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700240
241 # Developers sometimes run session_manager manually, in which case we'll
242 # need to help shut the chrome processes down.
243 try:
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700244 with timeout_util.Timeout(self.options.process_timeout):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700245 while self._ChromeFileInUse():
246 logging.warning('The chrome binary on the device is in use.')
247 logging.warning('Killing chrome and session_manager processes...\n')
248
Mike Frysinger3459bf52020-03-31 00:52:11 -0400249 self.device.run("pkill 'chrome|session_manager'", check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700250 # Wait for processes to actually terminate
251 time.sleep(POST_KILL_WAIT)
252 logging.info('Rechecking the chrome binary...')
David James3432acd2013-11-27 10:02:18 -0800253 except timeout_util.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800254 msg = ('Could not kill processes after %s seconds. Please exit any '
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700255 'running chrome processes and try again.'
256 % self.options.process_timeout)
David James88e6f032013-03-02 08:13:20 -0800257 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700258
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500259 def _MountRootfsAsWritable(self, check=False):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800260 """Mounts the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700261
Steven Bennettsca73efa2018-07-10 13:36:56 -0700262 If the command fails and the root dir is not writable then this function
263 sets self._root_dir_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700264
Mike Frysinger02e1e072013-11-10 22:11:34 -0500265 Args:
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500266 check: See remote.RemoteAccess.RemoteSh for details.
David James88e6f032013-03-02 08:13:20 -0800267 """
Yu-Ju Hong7e116ac2014-01-03 17:08:26 -0800268 # TODO: Should migrate to use the remount functions in remote_access.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400269 result = self.device.run(MOUNT_RW_COMMAND, check=check,
270 capture_output=True, encoding='utf-8')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700271 if result.returncode and not self.device.IsDirWritable('/'):
272 self._root_dir_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700273
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800274 def _EnsureTargetDir(self):
275 """Ensures that the target directory exists on the remote device."""
276 target_dir = self.options.target_dir
277 # Any valid /opt directory should already exist so avoid the remote call.
278 if os.path.commonprefix([target_dir, '/opt']) == '/opt':
279 return
Mike Frysinger3459bf52020-03-31 00:52:11 -0400280 self.device.run(['mkdir', '-p', '--mode', '0775', target_dir])
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800281
Ryan Cui7193a7e2013-04-26 14:15:19 -0700282 def _GetDeviceInfo(self):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800283 """Returns the disk space used and available for the target diectory."""
Ryan Cui7193a7e2013-04-26 14:15:19 -0700284 steps = [
285 functools.partial(self._GetRemoteDirSize, self.options.target_dir),
286 functools.partial(self._GetRemoteMountFree, self.options.target_dir)
287 ]
288 return_values = parallel.RunParallelSteps(steps, return_values=True)
289 return DeviceInfo(*return_values)
290
291 def _CheckDeviceFreeSpace(self, device_info):
292 """See if target device has enough space for Chrome.
293
Mike Frysinger02e1e072013-11-10 22:11:34 -0500294 Args:
Ryan Cui7193a7e2013-04-26 14:15:19 -0700295 device_info: A DeviceInfo named tuple.
296 """
297 effective_free = device_info.target_dir_size + device_info.target_fs_free
298 staging_size = self._GetStagingDirSize()
299 if effective_free < staging_size:
Daniel Erat1ae46382014-08-14 10:23:39 -0700300 raise DeployFailure(
301 'Not enough free space on the device. Required: %s MiB, '
Mike Frysinger93e8ffa2019-07-03 20:24:18 -0400302 'actual: %s MiB.' % (staging_size // 1024, effective_free // 1024))
Ryan Cui7193a7e2013-04-26 14:15:19 -0700303 if device_info.target_fs_free < (100 * 1024):
304 logging.warning('The device has less than 100MB free. deploy_chrome may '
305 'hang during the transfer.')
306
Satoru Takabayashif2893002017-06-20 14:52:48 +0900307 def _ShouldUseCompression(self):
308 """Checks if compression should be used for rsync."""
309 if self.options.compress == 'always':
310 return True
311 elif self.options.compress == 'never':
312 return False
313 elif self.options.compress == 'auto':
314 return not self.device.HasGigabitEthernet()
315
Ryan Cui3045c5d2012-07-13 18:00:33 -0700316 def _Deploy(self):
Erik Chen75a2f492020-08-06 19:15:11 -0700317 logging.info('Copying %s to %s on device...', self._deployment_name,
318 self.options.target_dir)
Ilja H. Friedel0ab63e12017-03-28 13:29:48 -0700319 # CopyToDevice will fall back to scp if rsync is corrupted on stateful.
320 # This does not work for deploy.
Satoru Takabayashiab380bc2015-01-15 16:00:41 +0900321 if not self.device.HasRsync():
322 raise DeployFailure(
David Pursellcfd58872015-03-19 09:15:48 -0700323 'rsync is not found on the device.\n'
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500324 'Run dev_install on the device to get rsync installed.')
Robert Flack1dc7ea82014-11-26 13:50:24 -0500325 self.device.CopyToDevice('%s/' % os.path.abspath(self.staging_dir),
326 self.options.target_dir,
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700327 mode='rsync', inplace=True,
Satoru Takabayashif2893002017-06-20 14:52:48 +0900328 compress=self._ShouldUseCompression(),
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700329 debug_level=logging.INFO,
Robert Flack1dc7ea82014-11-26 13:50:24 -0500330 verbose=self.options.verbose)
Ben Pastene5f03b052019-08-12 18:03:24 -0700331
332 # Set the security context on the default Chrome dir if that's where it's
333 # getting deployed, and only on SELinux supported devices.
Erik Chen75a2f492020-08-06 19:15:11 -0700334 if not self.options.lacros and self.device.IsSELinuxAvailable() and (
Ben Pastene5f03b052019-08-12 18:03:24 -0700335 _CHROME_DIR in (self.options.target_dir, self.options.mount_dir)):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400336 self.device.run(['restorecon', '-R', _CHROME_DIR])
Steve Funge984a532013-11-25 17:09:25 -0800337
338 for p in self.copy_paths:
Steve Funge984a532013-11-25 17:09:25 -0800339 if p.mode:
340 # Set mode if necessary.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400341 self.device.run('chmod %o %s/%s' % (
David Haddock3151d912017-10-24 03:50:32 +0000342 p.mode, self.options.target_dir, p.src if not p.dest else p.dest))
Steve Funge984a532013-11-25 17:09:25 -0800343
Erik Chen75a2f492020-08-06 19:15:11 -0700344 if self.options.lacros:
345 self.device.run(['chown', '-R', 'chronos:chronos',
346 self.options.target_dir])
347
Daniel Erat0fce5bf2017-09-28 17:29:23 -0700348 # Send SIGHUP to dbus-daemon to tell it to reload its configs. This won't
349 # pick up major changes (bus type, logging, etc.), but all we care about is
350 # getting the latest policy from /opt/google/chrome/dbus so that Chrome will
351 # be authorized to take ownership of its service names.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400352 self.device.run(DBUS_RELOAD_COMMAND, check=False)
Justin TerAvestfac210e2017-04-13 11:39:00 -0600353
Erik Chen75a2f492020-08-06 19:15:11 -0700354 if self.options.startui and self._stopped_ui:
Steve Funge984a532013-11-25 17:09:25 -0800355 logging.info('Starting UI...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400356 self.device.run('start ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700357
Ben Pastenee484b342020-06-30 18:29:27 -0700358 def _DeployTestBinaries(self):
359 """Deploys any local test binary to _CHROME_TEST_BIN_DIR on the device.
360
361 There could be several binaries located in the local build dir, so compare
362 what's already present on the device in _CHROME_TEST_BIN_DIR , and copy
363 over any that we also built ourselves.
364 """
365 r = self.device.run(_FIND_TEST_BIN_CMD, check=False)
366 if r.returncode != 0:
367 raise DeployFailure('Unable to ls contents of %s' % _CHROME_TEST_BIN_DIR)
368 binaries_to_copy = []
369 for f in r.output.splitlines():
370 binaries_to_copy.append(
371 chrome_util.Path(os.path.basename(f), exe=True, optional=True))
372
373 staging_dir = os.path.join(
374 self.tempdir, os.path.basename(_CHROME_TEST_BIN_DIR))
375 _PrepareStagingDir(self.options, self.tempdir, staging_dir,
376 copy_paths=binaries_to_copy)
Brian Sheedy86f12342020-10-29 15:30:02 -0700377 # Deploying can occasionally run into issues with rsync getting a broken
378 # pipe, so retry several times. See crbug.com/1141618 for more
379 # information.
380 retry_util.RetryException(
381 None, 3, self.device.CopyToDevice, staging_dir,
382 os.path.dirname(_CHROME_TEST_BIN_DIR), mode='rsync')
Ben Pastenee484b342020-06-30 18:29:27 -0700383
David James88e6f032013-03-02 08:13:20 -0800384 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700385 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800386 logging.info('Testing connection to the device...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400387 self.device.run('true')
David James88e6f032013-03-02 08:13:20 -0800388 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700389 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800390 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700391
Avery Musbach3edff0e2020-03-27 13:35:53 -0700392 def _CheckBoard(self):
393 """Check that the Chrome build is targeted for the device board."""
394 if self.options.board == self.device.board:
395 return
396 logging.warning('Device board is %s whereas target board is %s.',
397 self.device.board, self.options.board)
398 if self.options.force:
399 return
400 if not cros_build_lib.BooleanPrompt('Continue despite board mismatch?',
401 False):
402 raise DeployFailure('Aborted.')
403
Steve Funge984a532013-11-25 17:09:25 -0800404 def _CheckDeployType(self):
Steve Fung63d705d2014-03-16 03:14:03 -0700405 if self.options.build_dir:
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700406 def BinaryExists(filename):
407 """Checks if the passed-in file is present in the build directory."""
408 return os.path.exists(os.path.join(self.options.build_dir, filename))
409
Erik Chen75a2f492020-08-06 19:15:11 -0700410 # In the future, lacros-chrome and ash-chrome will likely be named
411 # something other than 'chrome' to avoid confusion.
Daniel Erat9813f0e2014-11-12 11:00:28 -0700412 # Handle non-Chrome deployments.
413 if not BinaryExists('chrome'):
Daniel Eratf53bd3a2016-12-02 11:28:36 -0700414 if BinaryExists('app_shell'):
Daniel Erat9813f0e2014-11-12 11:00:28 -0700415 self.copy_paths = chrome_util.GetCopyPaths('app_shell')
416
David James88e6f032013-03-02 08:13:20 -0800417 def _PrepareStagingDir(self):
Steve Funge984a532013-11-25 17:09:25 -0800418 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir,
419 self.copy_paths, self.chrome_dir)
David James88e6f032013-03-02 08:13:20 -0800420
Thiago Goncales12793312013-05-23 11:26:17 -0700421 def _MountTarget(self):
422 logging.info('Mounting Chrome...')
423
Anushruth8d797672019-10-17 12:22:31 -0700424 # Create directory if does not exist.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400425 self.device.run(_MKDIR_P_CMD % self.options.mount_dir)
Anushruth8d797672019-10-17 12:22:31 -0700426 try:
427 # Umount the existing mount on mount_dir if present first.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400428 self.device.run(_UMOUNT_DIR_IF_MOUNTPOINT_CMD %
429 {'dir': self.options.mount_dir})
Anushruth8d797672019-10-17 12:22:31 -0700430 except cros_build_lib.RunCommandError as e:
431 logging.error('Failed to umount %s', self.options.mount_dir)
432 # If there is a failure, check if some processs is using the mount_dir.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400433 result = self.device.run(LSOF_COMMAND % (self.options.mount_dir,),
434 check=False, capture_output=True,
435 encoding='utf-8')
Anushruth8d797672019-10-17 12:22:31 -0700436 logging.error('lsof %s -->', self.options.mount_dir)
437 logging.error(result.stdout)
438 raise e
439
Mike Frysinger3459bf52020-03-31 00:52:11 -0400440 self.device.run(_BIND_TO_FINAL_DIR_CMD % (self.options.target_dir,
441 self.options.mount_dir))
Anushruth8d797672019-10-17 12:22:31 -0700442
Thiago Goncales12793312013-05-23 11:26:17 -0700443 # Chrome needs partition to have exec and suid flags set
Mike Frysinger3459bf52020-03-31 00:52:11 -0400444 self.device.run(_SET_MOUNT_FLAGS_CMD % (self.options.mount_dir,))
Robert Flack1dc7ea82014-11-26 13:50:24 -0500445
446 def Cleanup(self):
447 """Clean up RemoteDevice."""
448 if not self.options.staging_only:
449 self.device.Cleanup()
Thiago Goncales12793312013-05-23 11:26:17 -0700450
David James88e6f032013-03-02 08:13:20 -0800451 def Perform(self):
Steve Funge984a532013-11-25 17:09:25 -0800452 self._CheckDeployType()
453
David James88e6f032013-03-02 08:13:20 -0800454 # If requested, just do the staging step.
455 if self.options.staging_only:
456 self._PrepareStagingDir()
457 return 0
458
Erik Chen75a2f492020-08-06 19:15:11 -0700459 # Check that the build matches the device. Lacros-chrome skips this check as
460 # it's currently board independent. This means that it's possible to deploy
461 # a build of lacros-chrome with a mismatched architecture. We don't try to
462 # prevent this developer foot-gun.
463 if not self.options.lacros:
464 self._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700465
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800466 # Ensure that the target directory exists before running parallel steps.
467 self._EnsureTargetDir()
468
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800469 logging.info('Preparing device')
Steve Funge984a532013-11-25 17:09:25 -0800470 steps = [self._GetDeviceInfo, self._CheckConnection,
Erik Chen75a2f492020-08-06 19:15:11 -0700471 self._MountRootfsAsWritable,
Steve Funge984a532013-11-25 17:09:25 -0800472 self._PrepareStagingDir]
Erik Chen75a2f492020-08-06 19:15:11 -0700473
Yuke Liaobe6bac32020-12-26 22:16:49 -0800474 restart_ui = True
475 if self.options.lacros:
Yuke Liao24fc60c2020-12-26 22:16:49 -0800476 # If this is a lacros build, we only want to restart ash-chrome if needed.
477 restart_ui = False
Yuke Liaobe6bac32020-12-26 22:16:49 -0800478 steps.append(self._KillLacrosChrome)
Yuke Liao24fc60c2020-12-26 22:16:49 -0800479 if self.options.modify_config_file:
480 restart_ui = self._ModifyConfigFileIfNeededForLacros()
481
Yuke Liaobe6bac32020-12-26 22:16:49 -0800482 if restart_ui:
483 steps.append(self._KillAshChromeIfNeeded)
ChromeOS Developer78345852020-08-11 23:09:48 -0700484 self._stopped_ui = True
Yuke Liaobe6bac32020-12-26 22:16:49 -0800485
Ryan Cui7193a7e2013-04-26 14:15:19 -0700486 ret = parallel.RunParallelSteps(steps, halt_on_error=True,
487 return_values=True)
488 self._CheckDeviceFreeSpace(ret[0])
David James88e6f032013-03-02 08:13:20 -0800489
Steven Bennettsca73efa2018-07-10 13:36:56 -0700490 # If the root dir is not writable, try disabling rootfs verification.
491 # (We always do this by default so that developers can write to
492 # /etc/chriome_dev.conf and other directories in the rootfs).
493 if self._root_dir_is_still_readonly.is_set():
494 if self.options.noremove_rootfs_verification:
495 logging.warning('Skipping disable rootfs verification.')
496 elif not self._DisableRootfsVerification():
497 logging.warning('Failed to disable rootfs verification.')
498
499 # If the target dir is still not writable (i.e. the user opted out or the
500 # command failed), abort.
501 if not self.device.IsDirWritable(self.options.target_dir):
Erik Chen75a2f492020-08-06 19:15:11 -0700502 if self.options.startui and self._stopped_ui:
Steven Bennettsca73efa2018-07-10 13:36:56 -0700503 logging.info('Restarting Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400504 self.device.run('start ui')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700505 raise DeployFailure('Target location is not writable. Aborting.')
David James88e6f032013-03-02 08:13:20 -0800506
Thiago Goncales12793312013-05-23 11:26:17 -0700507 if self.options.mount_dir is not None:
508 self._MountTarget()
509
David James88e6f032013-03-02 08:13:20 -0800510 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700511 self._Deploy()
Ben Pastenee484b342020-06-30 18:29:27 -0700512 if self.options.deploy_test_binaries:
513 self._DeployTestBinaries()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700514
515
Yuke Liaobe6bac32020-12-26 22:16:49 -0800516 def _ModifyConfigFileIfNeededForLacros(self):
517 """Modifies the /etc/chrome_dev.conf file for lacros-chrome.
518
519 Returns:
520 True if the file is modified, and the return value is usually used to
521 determine whether restarting ash-chrome is needed.
522 """
523 assert self.options.lacros, (
524 'Only deploying lacros-chrome needs to modify the config file.')
525 # Update /etc/chrome_dev.conf to include appropriate flags.
526 modified = False
527 result = self.device.run(ENABLE_LACROS_VIA_CONF_COMMAND, shell=True)
528 if result.stdout.strip() == MODIFIED_CONF_FILE:
529 modified = True
530 result = self.device.run(
531 _SET_LACROS_PATH_VIA_CONF_COMMAND % {
532 'conf_file': _CONF_FILE,
533 'lacros_path': self.options.target_dir,
534 'modified_conf_file': MODIFIED_CONF_FILE
535 },
536 shell=True)
537 if result.stdout.strip() == MODIFIED_CONF_FILE:
538 modified = True
539
540 return modified
541
542
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700543def ValidateStagingFlags(value):
544 """Convert formatted string to dictionary."""
545 return chrome_util.ProcessShellFlags(value)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700546
547
Steven Bennetts368c3e52016-09-23 13:05:21 -0700548def ValidateGnArgs(value):
549 """Convert GN_ARGS-formatted string to dictionary."""
550 return gn_helpers.FromGNArgs(value)
551
552
Ryan Cuie535b172012-10-19 18:25:03 -0700553def _CreateParser():
554 """Create our custom parser."""
Mike Frysingerc3061a62015-06-04 04:16:18 -0400555 parser = commandline.ArgumentParser(description=__doc__, caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700556
Ryan Cuia56a71e2012-10-18 18:40:35 -0700557 # TODO(rcui): Have this use the UI-V2 format of having source and target
558 # device be specified as positional arguments.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400559 parser.add_argument('--force', action='store_true', default=False,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700560 help='Skip all prompts (such as the prompt for disabling '
561 'of rootfs verification). This may result in the '
562 'target machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800563 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
Mike Frysingerc3061a62015-06-04 04:16:18 -0400564 parser.add_argument('--board', default=sdk_board_env,
Mike Frysinger80de5012019-08-01 14:10:53 -0400565 help='The board the Chrome build is targeted for. When '
Mike Frysingerc3061a62015-06-04 04:16:18 -0400566 "in a 'cros chrome-sdk' shell, defaults to the SDK "
Mike Frysinger80de5012019-08-01 14:10:53 -0400567 'board.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400568 parser.add_argument('--build-dir', type='path',
569 help='The directory with Chrome build artifacts to '
570 'deploy from. Typically of format '
571 '<chrome_root>/out/Debug. When this option is used, '
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700572 'the GN_ARGS environment variable must be set.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400573 parser.add_argument('--target-dir', type='path',
574 default=None,
575 help='Target directory on device to deploy Chrome into.')
576 parser.add_argument('-g', '--gs-path', type='gs_path',
577 help='GS path that contains the chrome to deploy.')
Adrian Eldera2c548a2017-11-07 19:01:29 -0500578 parser.add_argument('--private-key', type='path', default=None,
579 help='An ssh private key to use when deploying to '
580 'a CrOS device.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400581 parser.add_argument('--nostartui', action='store_false', dest='startui',
582 default=True,
583 help="Don't restart the ui daemon after deployment.")
584 parser.add_argument('--nostrip', action='store_false', dest='dostrip',
585 default=True,
586 help="Don't strip binaries during deployment. Warning: "
587 'the resulting binaries will be very large!')
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700588 parser.add_argument(
589 '-d', '--device',
590 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
591 help='Device hostname or IP in the format hostname[:port].')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400592 parser.add_argument('--mount-dir', type='path', default=None,
593 help='Deploy Chrome in target directory and bind it '
594 'to the directory specified by this flag.'
595 'Any existing mount on this directory will be '
596 'umounted first.')
597 parser.add_argument('--mount', action='store_true', default=False,
598 help='Deploy Chrome to default target directory and bind '
599 'it to the default mount directory.'
600 'Any existing mount on this directory will be '
601 'umounted first.')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700602 parser.add_argument('--noremove-rootfs-verification', action='store_true',
603 default=False, help='Never remove rootfs verification.')
Ben Pastenee484b342020-06-30 18:29:27 -0700604 parser.add_argument('--deploy-test-binaries', action='store_true',
605 default=False,
606 help='Also deploy any test binaries to %s. Useful for '
607 'running any Tast tests that execute these '
608 'binaries.' % _CHROME_TEST_BIN_DIR)
Erik Chen75a2f492020-08-06 19:15:11 -0700609 parser.add_argument('--lacros', action='store_true', default=False,
610 help='Deploys lacros-chrome rather than ash-chrome.')
Yuke Liao24fc60c2020-12-26 22:16:49 -0800611 parser.add_argument('--skip-modifying-config-file', action='store_false',
612 dest='modify_config_file',
613 help='By default, deploying lacros-chrome modifies the '
614 '/etc/chrome_dev.conf file, which interferes with '
615 'automated testing, and this argument disables it.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700616
Mike Frysingerc3061a62015-06-04 04:16:18 -0400617 group = parser.add_argument_group('Advanced Options')
618 group.add_argument('-l', '--local-pkg-path', type='path',
619 help='Path to local chrome prebuilt package to deploy.')
620 group.add_argument('--sloppy', action='store_true', default=False,
621 help='Ignore when mandatory artifacts are missing.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700622 group.add_argument('--staging-flags', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400623 help=('Extra flags to control staging. Valid flags are - '
624 '%s' % ', '.join(chrome_util.STAGING_FLAGS)))
Steven Bennetts46a84c32016-08-12 15:20:46 -0700625 # TODO(stevenjb): Remove --strict entirely once removed from the ebuild.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400626 group.add_argument('--strict', action='store_true', default=False,
Steven Bennetts46a84c32016-08-12 15:20:46 -0700627 help='Deprecated. Default behavior is "strict". Use '
628 '--sloppy to omit warnings for missing optional '
629 'files.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400630 group.add_argument('--strip-flags', default=None,
631 help="Flags to call the 'strip' binutil tool with. "
Mike Frysinger80de5012019-08-01 14:10:53 -0400632 'Overrides the default arguments.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400633 group.add_argument('--ping', action='store_true', default=False,
634 help='Ping the device before connection attempt.')
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700635 group.add_argument('--process-timeout', type=int,
636 default=KILL_PROC_MAX_WAIT,
637 help='Timeout for process shutdown.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700638
Mike Frysingerc3061a62015-06-04 04:16:18 -0400639 group = parser.add_argument_group(
640 'Metadata Overrides (Advanced)',
641 description='Provide all of these overrides in order to remove '
642 'dependencies on metadata.json existence.')
643 group.add_argument('--target-tc', action='store', default=None,
644 help='Override target toolchain name, e.g. '
645 'x86_64-cros-linux-gnu')
646 group.add_argument('--toolchain-url', action='store', default=None,
647 help='Override toolchain url format pattern, e.g. '
648 '2014/04/%%(target)s-2014.04.23.220740.tar.xz')
Aviv Keshet1c986f32014-04-24 13:20:49 -0700649
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700650 # DEPRECATED: --gyp-defines is ignored, but retained for backwards
651 # compatibility. TODO(stevenjb): Remove once eliminated from the ebuild.
652 parser.add_argument('--gyp-defines', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400653 help=argparse.SUPPRESS)
Steven Bennetts368c3e52016-09-23 13:05:21 -0700654
655 # GN_ARGS (args.gn) used to build Chrome. Influences which files are staged
656 # when --build-dir is set. Defaults to reading from the GN_ARGS env variable.
657 # CURRENLY IGNORED, ADDED FOR FORWARD COMPATABILITY.
658 parser.add_argument('--gn-args', default=None, type=ValidateGnArgs,
659 help=argparse.SUPPRESS)
660
Ryan Cuia56a71e2012-10-18 18:40:35 -0700661 # Path of an empty directory to stage chrome artifacts to. Defaults to a
662 # temporary directory that is removed when the script finishes. If the path
663 # is specified, then it will not be removed.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400664 parser.add_argument('--staging-dir', type='path', default=None,
665 help=argparse.SUPPRESS)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700666 # Only prepare the staging directory, and skip deploying to the device.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400667 parser.add_argument('--staging-only', action='store_true', default=False,
668 help=argparse.SUPPRESS)
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700669 # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
670 # is used as-is, and not normalized. Used by the Chrome ebuild to skip
671 # fetching the SDK toolchain.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400672 parser.add_argument('--strip-bin', default=None, help=argparse.SUPPRESS)
Satoru Takabayashif2893002017-06-20 14:52:48 +0900673 parser.add_argument('--compress', action='store', default='auto',
674 choices=('always', 'never', 'auto'),
675 help='Choose the data compression behavior. Default '
676 'is set to "auto", that disables compression if '
677 'the target device has a gigabit ethernet port.')
Ryan Cuie535b172012-10-19 18:25:03 -0700678 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700679
Ryan Cuie535b172012-10-19 18:25:03 -0700680
681def _ParseCommandLine(argv):
682 """Parse args, and run environment-independent checks."""
683 parser = _CreateParser()
Mike Frysingerc3061a62015-06-04 04:16:18 -0400684 options = parser.parse_args(argv)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700685
Ryan Cuia56a71e2012-10-18 18:40:35 -0700686 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
687 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800688 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700689 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
690 parser.error('Cannot specify both --build_dir and '
691 '--gs-path/--local-pkg-patch')
Erik Chen75a2f492020-08-06 19:15:11 -0700692 if options.lacros:
693 if options.board:
694 parser.error('--board is not supported with --lacros')
695 # The stripping implemented in this file rely on the cros-chrome-sdk, which
696 # is inappropriate for Lacros. Lacros stripping is currently not
697 # implemented.
698 if options.dostrip:
699 parser.error('--lacros requires --nostrip')
700 if options.mount_dir or options.mount:
701 parser.error('--lacros does not support --mount or --mount-dir')
702 if options.deploy_test_binaries:
703 parser.error('--lacros does not support --deploy-test-binaries')
704 if options.local_pkg_path:
705 parser.error('--lacros does not support --local-pkg-path')
706 else:
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +0900707 if not options.board and options.build_dir:
708 match = re.search(r'out_([^/]+)/Release$', options.build_dir)
709 if match:
710 options.board = match.group(1)
711 logging.info('--board is set to %s', options.board)
Erik Chen75a2f492020-08-06 19:15:11 -0700712 if not options.board:
713 parser.error('--board is required')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700714 if options.gs_path and options.local_pkg_path:
715 parser.error('Cannot specify both --gs-path and --local-pkg-path')
Ben Pasteneb44d8e72021-02-19 13:56:43 -0800716 if not (options.staging_only or options.device):
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700717 parser.error('Need to specify --device')
Steven Bennetts46a84c32016-08-12 15:20:46 -0700718 if options.staging_flags and not options.build_dir:
719 parser.error('--staging-flags require --build-dir to be set.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700720
Steven Bennetts46a84c32016-08-12 15:20:46 -0700721 if options.strict:
722 logging.warning('--strict is deprecated.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700723 if options.gyp_defines:
724 logging.warning('--gyp-defines is deprecated.')
Thiago Goncales12793312013-05-23 11:26:17 -0700725
726 if options.mount or options.mount_dir:
727 if not options.target_dir:
728 options.target_dir = _CHROME_DIR_MOUNT
729 else:
730 if not options.target_dir:
Erik Chen75a2f492020-08-06 19:15:11 -0700731 options.target_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Thiago Goncales12793312013-05-23 11:26:17 -0700732
733 if options.mount and not options.mount_dir:
734 options.mount_dir = _CHROME_DIR
735
Mike Frysingerc3061a62015-06-04 04:16:18 -0400736 return options
Ryan Cui3045c5d2012-07-13 18:00:33 -0700737
738
Mike Frysingerc3061a62015-06-04 04:16:18 -0400739def _PostParseCheck(options):
Steve Funge984a532013-11-25 17:09:25 -0800740 """Perform some usage validation (after we've parsed the arguments).
Ryan Cui3045c5d2012-07-13 18:00:33 -0700741
742 Args:
Mike Frysinger5fe3f222016-09-01 00:14:16 -0400743 options: The options object returned by the cli parser.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700744 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700745 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
746 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
747
Steven Bennetts368c3e52016-09-23 13:05:21 -0700748 if not options.gn_args:
749 gn_env = os.getenv('GN_ARGS')
750 if gn_env is not None:
751 options.gn_args = gn_helpers.FromGNArgs(gn_env)
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800752 logging.debug('GN_ARGS taken from environment: %s', options.gn_args)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700753
Steven Bennetts60600462016-05-12 10:40:20 -0700754 if not options.staging_flags:
755 use_env = os.getenv('USE')
756 if use_env is not None:
757 options.staging_flags = ' '.join(set(use_env.split()).intersection(
758 chrome_util.STAGING_FLAGS))
759 logging.info('Staging flags taken from USE in environment: %s',
760 options.staging_flags)
761
Ryan Cuia56a71e2012-10-18 18:40:35 -0700762
Ryan Cui504db722013-01-22 11:48:01 -0800763def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700764 """Get the chrome prebuilt tarball from GS.
765
Mike Frysinger02e1e072013-11-10 22:11:34 -0500766 Returns:
767 Path to the fetched chrome tarball.
Ryan Cuia56a71e2012-10-18 18:40:35 -0700768 """
David James9374aac2013-10-08 16:00:17 -0700769 gs_ctx = gs.GSContext(cache_dir=cache_dir, init_boto=True)
Mike Frysinger7ad4fd62014-02-11 16:53:56 -0500770 files = gs_ctx.LS(gs_path)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800771 files = [found for found in files if
772 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
773 if not files:
774 raise Exception('No chrome package found at %s' % gs_path)
775 elif len(files) > 1:
776 # - Users should provide us with a direct link to either a stripped or
777 # unstripped chrome package.
778 # - In the case of being provided with an archive directory, where both
779 # stripped and unstripped chrome available, use the stripped chrome
780 # package.
781 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
782 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
783 files = [f for f in files if not 'unstripped' in f]
784 assert len(files) == 1
785 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800786
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800787 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800788 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800789 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
790 chrome_path = os.path.join(tempdir, filename)
791 assert os.path.exists(chrome_path)
792 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700793
794
Ryan Cuif890a3e2013-03-07 18:57:06 -0800795@contextlib.contextmanager
796def _StripBinContext(options):
797 if not options.dostrip:
798 yield None
799 elif options.strip_bin:
800 yield options.strip_bin
801 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800802 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800803 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
Aviv Keshet1c986f32014-04-24 13:20:49 -0700804 with sdk.Prepare(components=components, target_tc=options.target_tc,
805 toolchain_url=options.toolchain_url) as ctx:
Ryan Cui686ec052013-02-12 16:39:41 -0800806 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
807 constants.CHROME_ENV_FILE)
808 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
809 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
810 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800811 yield strip_bin
812
813
Steve Funge984a532013-11-25 17:09:25 -0800814def _PrepareStagingDir(options, tempdir, staging_dir, copy_paths=None,
Erik Chen75a2f492020-08-06 19:15:11 -0700815 chrome_dir=None):
Ryan Cuif890a3e2013-03-07 18:57:06 -0800816 """Place the necessary files in the staging directory.
817
818 The staging directory is the directory used to rsync the build artifacts over
819 to the device. Only the necessary Chrome build artifacts are put into the
820 staging directory.
821 """
Erik Chen75a2f492020-08-06 19:15:11 -0700822 if chrome_dir is None:
823 chrome_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Ryan Cui5866be02013-03-18 14:12:00 -0700824 osutils.SafeMakedirs(staging_dir)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400825 os.chmod(staging_dir, 0o755)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800826 if options.build_dir:
827 with _StripBinContext(options) as strip_bin:
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700828 strip_flags = (None if options.strip_flags is None else
829 shlex.split(options.strip_flags))
Ryan Cui686ec052013-02-12 16:39:41 -0800830 chrome_util.StageChromeFromBuildDir(
Steven Bennetts46a84c32016-08-12 15:20:46 -0700831 staging_dir, options.build_dir, strip_bin,
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700832 sloppy=options.sloppy, gn_args=options.gn_args,
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700833 staging_flags=options.staging_flags,
Steve Funge984a532013-11-25 17:09:25 -0800834 strip_flags=strip_flags, copy_paths=copy_paths)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700835 else:
836 pkg_path = options.local_pkg_path
837 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800838 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
839 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700840
841 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800842 logging.info('Extracting %s...', pkg_path)
Ryan Cui5866be02013-03-18 14:12:00 -0700843 # Extract only the ./opt/google/chrome contents, directly into the staging
844 # dir, collapsing the directory hierarchy.
Steve Funge984a532013-11-25 17:09:25 -0800845 if pkg_path[-4:] == '.zip':
Mike Frysinger45602c72019-09-22 02:15:11 -0400846 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800847 ['unzip', '-X', pkg_path, _ANDROID_DIR_EXTRACT_PATH, '-d',
848 staging_dir])
849 for filename in glob.glob(os.path.join(staging_dir, 'system/chrome/*')):
850 shutil.move(filename, staging_dir)
851 osutils.RmDir(os.path.join(staging_dir, 'system'), ignore_missing=True)
852 else:
Mike Frysinger45602c72019-09-22 02:15:11 -0400853 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800854 ['tar', '--strip-components', '4', '--extract',
855 '--preserve-permissions', '--file', pkg_path, '.%s' % chrome_dir],
856 cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700857
Ryan Cui71aa8de2013-04-19 16:12:55 -0700858
Ryan Cui3045c5d2012-07-13 18:00:33 -0700859def main(argv):
Mike Frysingerc3061a62015-06-04 04:16:18 -0400860 options = _ParseCommandLine(argv)
861 _PostParseCheck(options)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700862
Aviv Keshet01a82e92017-03-02 17:39:59 -0800863 with osutils.TempDir(set_global=True) as tempdir:
864 staging_dir = options.staging_dir
865 if not staging_dir:
866 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700867
Aviv Keshet01a82e92017-03-02 17:39:59 -0800868 deploy = DeployChrome(options, tempdir, staging_dir)
869 try:
870 deploy.Perform()
871 except failures_lib.StepFailure as ex:
872 raise SystemExit(str(ex).strip())
873 deploy.Cleanup()