blob: ffdb53d5f96a0b3fefab3f94a4cbf4397762462c [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
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090028import re
Ryan Cui5b7c2ed2013-03-18 18:45:46 -070029import shlex
Steve Funge984a532013-11-25 17:09:25 -080030import shutil
Mike Frysinger6165cdc2020-02-21 02:38:07 -050031import sys
Ryan Cui3045c5d2012-07-13 18:00:33 -070032import time
Ryan Cui3045c5d2012-07-13 18:00:33 -070033
David Pursellcfd58872015-03-19 09:15:48 -070034from chromite.cli.cros import cros_chrome_sdk
Ryan Cuia56a71e2012-10-18 18:40:35 -070035from chromite.lib import chrome_util
Ryan Cuie535b172012-10-19 18:25:03 -070036from chromite.lib import commandline
Mike Frysinger47b10992021-01-23 00:23:25 -050037from chromite.lib import constants
Ralph Nathan91874ca2015-03-19 13:29:41 -070038from chromite.lib import cros_build_lib
39from chromite.lib import cros_logging as logging
Mike Frysinger47b10992021-01-23 00:23:25 -050040from chromite.lib import failures_lib
Ryan Cui777ff422012-12-07 13:12:54 -080041from chromite.lib import gs
Ryan Cui3045c5d2012-07-13 18:00:33 -070042from chromite.lib import osutils
David James88e6f032013-03-02 08:13:20 -080043from chromite.lib import parallel
Ryan Cui3045c5d2012-07-13 18:00:33 -070044from chromite.lib import remote_access as remote
Brian Sheedy86f12342020-10-29 15:30:02 -070045from chromite.lib import retry_util
David James3432acd2013-11-27 10:02:18 -080046from chromite.lib import timeout_util
Mike Frysinger47b10992021-01-23 00:23:25 -050047from chromite.third_party.gn_helpers import gn_helpers
Ryan Cui3045c5d2012-07-13 18:00:33 -070048
49
Mike Frysinger6165cdc2020-02-21 02:38:07 -050050assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
51
52
Ryan Cui3045c5d2012-07-13 18:00:33 -070053KERNEL_A_PARTITION = 2
54KERNEL_B_PARTITION = 4
55
56KILL_PROC_MAX_WAIT = 10
57POST_KILL_WAIT = 2
58
David Haddock3151d912017-10-24 03:50:32 +000059MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Anushruth8d797672019-10-17 12:22:31 -070060LSOF_COMMAND_CHROME = 'lsof %s/chrome'
61LSOF_COMMAND = 'lsof %s'
David Haddock3151d912017-10-24 03:50:32 +000062DBUS_RELOAD_COMMAND = 'killall -HUP dbus-daemon'
Ryan Cui3045c5d2012-07-13 18:00:33 -070063
Steve Funge984a532013-11-25 17:09:25 -080064_ANDROID_DIR = '/system/chrome'
65_ANDROID_DIR_EXTRACT_PATH = 'system/chrome/*'
66
David James2cb34002013-03-01 18:42:40 -080067_CHROME_DIR = '/opt/google/chrome'
Thiago Goncales12793312013-05-23 11:26:17 -070068_CHROME_DIR_MOUNT = '/mnt/stateful_partition/deploy_rootfs/opt/google/chrome'
Ben Pastenee484b342020-06-30 18:29:27 -070069_CHROME_TEST_BIN_DIR = '/usr/local/libexec/chrome-binary-tests'
David James2cb34002013-03-01 18:42:40 -080070
David Haddock3151d912017-10-24 03:50:32 +000071_UMOUNT_DIR_IF_MOUNTPOINT_CMD = (
72 'if mountpoint -q %(dir)s; then umount %(dir)s; fi')
73_BIND_TO_FINAL_DIR_CMD = 'mount --rbind %s %s'
74_SET_MOUNT_FLAGS_CMD = 'mount -o remount,exec,suid %s'
Anushruth8d797672019-10-17 12:22:31 -070075_MKDIR_P_CMD = 'mkdir -p --mode 0775 %s'
Ben Pastenee484b342020-06-30 18:29:27 -070076_FIND_TEST_BIN_CMD = 'find %s -maxdepth 1 -executable -type f' % (
77 _CHROME_TEST_BIN_DIR)
David Haddock3151d912017-10-24 03:50:32 +000078
79DF_COMMAND = 'df -k %s'
80
ChromeOS Developer536f59a2020-08-10 14:39:27 -070081LACROS_DIR = '/usr/local/lacros-chrome'
Erik Chen75a2f492020-08-06 19:15:11 -070082_CONF_FILE = '/etc/chrome_dev.conf'
83_KILL_LACROS_CHROME_CMD = 'pkill -f %(lacros_dir)s/chrome'
84MODIFIED_CONF_FILE = f'modified {_CONF_FILE}'
85
86# This command checks if "--enable-features=LacrosSupport" is present in
87# /etc/chrome_dev.conf. If it is not, then it is added.
88# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
89# to modify chrome_dev.conf. Either revisit this policy or find another
90# mechanism to pass configuration to ash-chrome.
91ENABLE_LACROS_VIA_CONF_COMMAND = f"""
92 if ! grep -q "^--enable-features=LacrosSupport$" {_CONF_FILE}; then
93 echo "--enable-features=LacrosSupport" >> {_CONF_FILE};
94 echo {MODIFIED_CONF_FILE};
95 fi
96"""
97
98# This command checks if "--lacros-chrome-path=" is present with the right value
99# in /etc/chrome_dev.conf. If it is not, then all previous instances are removed
100# and the new one is added.
101# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
102# to modify chrome_dev.conf. Either revisit this policy or find another
103# mechanism to pass configuration to ash-chrome.
104_SET_LACROS_PATH_VIA_CONF_COMMAND = """
105 if ! grep -q "^--lacros-chrome-path=%(lacros_path)s$" %(conf_file)s; then
106 sed 's/--lacros-chrome-path/#--lacros-chrome-path/' %(conf_file)s;
107 echo "--lacros-chrome-path=%(lacros_path)s" >> %(conf_file)s;
108 echo %(modified_conf_file)s;
109 fi
110"""
Mike Frysingere65f3752014-12-08 00:46:39 -0500111
Ryan Cui3045c5d2012-07-13 18:00:33 -0700112def _UrlBaseName(url):
113 """Return the last component of the URL."""
114 return url.rstrip('/').rpartition('/')[-1]
115
116
Yu-Ju Hongc54d3342014-05-14 12:42:06 -0700117class DeployFailure(failures_lib.StepFailure):
David James88e6f032013-03-02 08:13:20 -0800118 """Raised whenever the deploy fails."""
119
120
Ryan Cui7193a7e2013-04-26 14:15:19 -0700121DeviceInfo = collections.namedtuple(
122 'DeviceInfo', ['target_dir_size', 'target_fs_free'])
123
124
Ryan Cui3045c5d2012-07-13 18:00:33 -0700125class DeployChrome(object):
126 """Wraps the core deployment functionality."""
Mike Frysingere65f3752014-12-08 00:46:39 -0500127
Ryan Cuia56a71e2012-10-18 18:40:35 -0700128 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -0700129 """Initialize the class.
130
Mike Frysinger02e1e072013-11-10 22:11:34 -0500131 Args:
Mike Frysingerc3061a62015-06-04 04:16:18 -0400132 options: options object.
Ryan Cuie535b172012-10-19 18:25:03 -0700133 tempdir: Scratch space for the class. Caller has responsibility to clean
134 it up.
Steve Funge984a532013-11-25 17:09:25 -0800135 staging_dir: Directory to stage the files to.
Ryan Cuie535b172012-10-19 18:25:03 -0700136 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700137 self.tempdir = tempdir
138 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -0700139 self.staging_dir = staging_dir
Robert Flack1dc7ea82014-11-26 13:50:24 -0500140 if not self.options.staging_only:
Ben Pasteneb44d8e72021-02-19 13:56:43 -0800141 hostname = options.device.hostname
142 port = options.device.port
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700143 self.device = remote.ChromiumOSDevice(hostname, port=port,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700144 ping=options.ping,
145 private_key=options.private_key,
146 include_dev_paths=False)
Steven Bennettsca73efa2018-07-10 13:36:56 -0700147 self._root_dir_is_still_readonly = multiprocessing.Event()
Steven Bennetts5a7c72d2016-10-17 20:04:46 +0000148
Erik Chen75a2f492020-08-06 19:15:11 -0700149 self._deployment_name = 'lacros' if options.lacros else 'chrome'
150 self.copy_paths = chrome_util.GetCopyPaths(self._deployment_name)
151
152 self.chrome_dir = LACROS_DIR if self.options.lacros else _CHROME_DIR
153
154 # Whether UI was stopped during setup.
155 self._stopped_ui = False
Steve Funge984a532013-11-25 17:09:25 -0800156
Ryan Cui7193a7e2013-04-26 14:15:19 -0700157 def _GetRemoteMountFree(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400158 result = self.device.run(DF_COMMAND % remote_dir)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700159 line = result.output.splitlines()[1]
Steve Funge984a532013-11-25 17:09:25 -0800160 value = line.split()[3]
161 multipliers = {
162 'G': 1024 * 1024 * 1024,
163 'M': 1024 * 1024,
164 'K': 1024,
165 }
166 return int(value.rstrip('GMK')) * multipliers.get(value[-1], 1)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700167
168 def _GetRemoteDirSize(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400169 result = self.device.run('du -ks %s' % remote_dir,
170 capture_output=True, encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700171 return int(result.output.split()[0])
172
173 def _GetStagingDirSize(self):
Mike Frysinger45602c72019-09-22 02:15:11 -0400174 result = cros_build_lib.dbg_run(['du', '-ks', self.staging_dir],
Mike Frysinger0282d222019-12-17 17:15:48 -0500175 stdout=True, capture_output=True,
Mike Frysingerb88d5fb2019-10-23 00:38:45 -0400176 encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700177 return int(result.output.split()[0])
178
Ryan Cui3045c5d2012-07-13 18:00:33 -0700179 def _ChromeFileInUse(self):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400180 result = self.device.run(LSOF_COMMAND_CHROME % (self.options.target_dir,),
181 check=False, capture_output=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700182 return result.returncode == 0
183
Justin TerAvestfac210e2017-04-13 11:39:00 -0600184 def _Reboot(self):
185 # A reboot in developer mode takes a while (and has delays), so the user
186 # will have time to read and act on the USB boot instructions below.
187 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
188 self.device.Reboot()
189
Ryan Cui3045c5d2012-07-13 18:00:33 -0700190 def _DisableRootfsVerification(self):
191 if not self.options.force:
192 logging.error('Detected that the device has rootfs verification enabled.')
193 logging.info('This script can automatically remove the rootfs '
194 'verification, which requires that it reboot the device.')
195 logging.info('Make sure the device is in developer mode!')
196 logging.info('Skip this prompt by specifying --force.')
Ben Chan37d64e52018-10-05 15:35:02 -0700197 if not cros_build_lib.BooleanPrompt('Remove rootfs verification?', False):
Steven Bennettsca73efa2018-07-10 13:36:56 -0700198 return False
Ryan Cui3045c5d2012-07-13 18:00:33 -0700199
Ben Pasteneb44d8e72021-02-19 13:56:43 -0800200 logging.info('Removing rootfs verification from %s', self.options.device)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700201 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
202 # Use --force to bypass the checks.
David Haddock3151d912017-10-24 03:50:32 +0000203 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
204 '--remove_rootfs_verification --force')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700205 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400206 self.device.run(cmd % partition, check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700207
Justin TerAvestfac210e2017-04-13 11:39:00 -0600208 self._Reboot()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700209
David James88e6f032013-03-02 08:13:20 -0800210 # Now that the machine has been rebooted, we need to kill Chrome again.
Erik Chen75a2f492020-08-06 19:15:11 -0700211 self._KillAshChromeIfNeeded()
David James88e6f032013-03-02 08:13:20 -0800212
213 # Make sure the rootfs is writable now.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500214 self._MountRootfsAsWritable(check=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700215
Steven Bennettsca73efa2018-07-10 13:36:56 -0700216 return True
217
Ryan Cui3045c5d2012-07-13 18:00:33 -0700218 def _CheckUiJobStarted(self):
219 # status output is in the format:
220 # <job_name> <status> ['process' <pid>].
221 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800222 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400223 result = self.device.run('status ui', capture_output=True,
224 encoding='utf-8')
Ryan Cuif2d1a582013-02-19 14:08:13 -0800225 except cros_build_lib.RunCommandError as e:
226 if 'Unknown job' in e.result.error:
227 return False
228 else:
229 raise e
230
Ryan Cui3045c5d2012-07-13 18:00:33 -0700231 return result.output.split()[1].split('/')[0] == 'start'
232
Erik Chen75a2f492020-08-06 19:15:11 -0700233 def _KillLacrosChrome(self):
234 """This method kills lacros-chrome on the device, if it's running."""
235 self.device.run(_KILL_LACROS_CHROME_CMD %
236 {'lacros_dir': self.options.target_dir}, check=False)
237
238 def _KillAshChromeIfNeeded(self):
239 """This method kills ash-chrome on the device, if it's running.
240
241 This method calls 'stop ui', and then also manually pkills both ash-chrome
242 and the session manager.
243 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700244 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800245 logging.info('Shutting down Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400246 self.device.run('stop ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700247
248 # Developers sometimes run session_manager manually, in which case we'll
249 # need to help shut the chrome processes down.
250 try:
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700251 with timeout_util.Timeout(self.options.process_timeout):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700252 while self._ChromeFileInUse():
253 logging.warning('The chrome binary on the device is in use.')
254 logging.warning('Killing chrome and session_manager processes...\n')
255
Mike Frysinger3459bf52020-03-31 00:52:11 -0400256 self.device.run("pkill 'chrome|session_manager'", check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700257 # Wait for processes to actually terminate
258 time.sleep(POST_KILL_WAIT)
259 logging.info('Rechecking the chrome binary...')
David James3432acd2013-11-27 10:02:18 -0800260 except timeout_util.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800261 msg = ('Could not kill processes after %s seconds. Please exit any '
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700262 'running chrome processes and try again.'
263 % self.options.process_timeout)
David James88e6f032013-03-02 08:13:20 -0800264 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700265
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500266 def _MountRootfsAsWritable(self, check=False):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800267 """Mounts the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700268
Steven Bennettsca73efa2018-07-10 13:36:56 -0700269 If the command fails and the root dir is not writable then this function
270 sets self._root_dir_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700271
Mike Frysinger02e1e072013-11-10 22:11:34 -0500272 Args:
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500273 check: See remote.RemoteAccess.RemoteSh for details.
David James88e6f032013-03-02 08:13:20 -0800274 """
Yu-Ju Hong7e116ac2014-01-03 17:08:26 -0800275 # TODO: Should migrate to use the remount functions in remote_access.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400276 result = self.device.run(MOUNT_RW_COMMAND, check=check,
277 capture_output=True, encoding='utf-8')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700278 if result.returncode and not self.device.IsDirWritable('/'):
279 self._root_dir_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700280
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800281 def _EnsureTargetDir(self):
282 """Ensures that the target directory exists on the remote device."""
283 target_dir = self.options.target_dir
284 # Any valid /opt directory should already exist so avoid the remote call.
285 if os.path.commonprefix([target_dir, '/opt']) == '/opt':
286 return
Mike Frysinger3459bf52020-03-31 00:52:11 -0400287 self.device.run(['mkdir', '-p', '--mode', '0775', target_dir])
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800288
Ryan Cui7193a7e2013-04-26 14:15:19 -0700289 def _GetDeviceInfo(self):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800290 """Returns the disk space used and available for the target diectory."""
Ryan Cui7193a7e2013-04-26 14:15:19 -0700291 steps = [
292 functools.partial(self._GetRemoteDirSize, self.options.target_dir),
293 functools.partial(self._GetRemoteMountFree, self.options.target_dir)
294 ]
295 return_values = parallel.RunParallelSteps(steps, return_values=True)
296 return DeviceInfo(*return_values)
297
298 def _CheckDeviceFreeSpace(self, device_info):
299 """See if target device has enough space for Chrome.
300
Mike Frysinger02e1e072013-11-10 22:11:34 -0500301 Args:
Ryan Cui7193a7e2013-04-26 14:15:19 -0700302 device_info: A DeviceInfo named tuple.
303 """
304 effective_free = device_info.target_dir_size + device_info.target_fs_free
305 staging_size = self._GetStagingDirSize()
306 if effective_free < staging_size:
Daniel Erat1ae46382014-08-14 10:23:39 -0700307 raise DeployFailure(
308 'Not enough free space on the device. Required: %s MiB, '
Mike Frysinger93e8ffa2019-07-03 20:24:18 -0400309 'actual: %s MiB.' % (staging_size // 1024, effective_free // 1024))
Ryan Cui7193a7e2013-04-26 14:15:19 -0700310 if device_info.target_fs_free < (100 * 1024):
311 logging.warning('The device has less than 100MB free. deploy_chrome may '
312 'hang during the transfer.')
313
Satoru Takabayashif2893002017-06-20 14:52:48 +0900314 def _ShouldUseCompression(self):
315 """Checks if compression should be used for rsync."""
316 if self.options.compress == 'always':
317 return True
318 elif self.options.compress == 'never':
319 return False
320 elif self.options.compress == 'auto':
321 return not self.device.HasGigabitEthernet()
322
Ryan Cui3045c5d2012-07-13 18:00:33 -0700323 def _Deploy(self):
Erik Chen75a2f492020-08-06 19:15:11 -0700324 logging.info('Copying %s to %s on device...', self._deployment_name,
325 self.options.target_dir)
Ilja H. Friedel0ab63e12017-03-28 13:29:48 -0700326 # CopyToDevice will fall back to scp if rsync is corrupted on stateful.
327 # This does not work for deploy.
Satoru Takabayashiab380bc2015-01-15 16:00:41 +0900328 if not self.device.HasRsync():
329 raise DeployFailure(
David Pursellcfd58872015-03-19 09:15:48 -0700330 'rsync is not found on the device.\n'
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500331 'Run dev_install on the device to get rsync installed.')
Robert Flack1dc7ea82014-11-26 13:50:24 -0500332 self.device.CopyToDevice('%s/' % os.path.abspath(self.staging_dir),
333 self.options.target_dir,
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700334 mode='rsync', inplace=True,
Satoru Takabayashif2893002017-06-20 14:52:48 +0900335 compress=self._ShouldUseCompression(),
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700336 debug_level=logging.INFO,
Robert Flack1dc7ea82014-11-26 13:50:24 -0500337 verbose=self.options.verbose)
Ben Pastene5f03b052019-08-12 18:03:24 -0700338
339 # Set the security context on the default Chrome dir if that's where it's
340 # getting deployed, and only on SELinux supported devices.
Erik Chen75a2f492020-08-06 19:15:11 -0700341 if not self.options.lacros and self.device.IsSELinuxAvailable() and (
Ben Pastene5f03b052019-08-12 18:03:24 -0700342 _CHROME_DIR in (self.options.target_dir, self.options.mount_dir)):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400343 self.device.run(['restorecon', '-R', _CHROME_DIR])
Steve Funge984a532013-11-25 17:09:25 -0800344
345 for p in self.copy_paths:
Steve Funge984a532013-11-25 17:09:25 -0800346 if p.mode:
347 # Set mode if necessary.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400348 self.device.run('chmod %o %s/%s' % (
David Haddock3151d912017-10-24 03:50:32 +0000349 p.mode, self.options.target_dir, p.src if not p.dest else p.dest))
Steve Funge984a532013-11-25 17:09:25 -0800350
Erik Chen75a2f492020-08-06 19:15:11 -0700351 if self.options.lacros:
352 self.device.run(['chown', '-R', 'chronos:chronos',
353 self.options.target_dir])
354
Daniel Erat0fce5bf2017-09-28 17:29:23 -0700355 # Send SIGHUP to dbus-daemon to tell it to reload its configs. This won't
356 # pick up major changes (bus type, logging, etc.), but all we care about is
357 # getting the latest policy from /opt/google/chrome/dbus so that Chrome will
358 # be authorized to take ownership of its service names.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400359 self.device.run(DBUS_RELOAD_COMMAND, check=False)
Justin TerAvestfac210e2017-04-13 11:39:00 -0600360
Erik Chen75a2f492020-08-06 19:15:11 -0700361 if self.options.startui and self._stopped_ui:
Steve Funge984a532013-11-25 17:09:25 -0800362 logging.info('Starting UI...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400363 self.device.run('start ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700364
Ben Pastenee484b342020-06-30 18:29:27 -0700365 def _DeployTestBinaries(self):
366 """Deploys any local test binary to _CHROME_TEST_BIN_DIR on the device.
367
368 There could be several binaries located in the local build dir, so compare
369 what's already present on the device in _CHROME_TEST_BIN_DIR , and copy
370 over any that we also built ourselves.
371 """
372 r = self.device.run(_FIND_TEST_BIN_CMD, check=False)
373 if r.returncode != 0:
374 raise DeployFailure('Unable to ls contents of %s' % _CHROME_TEST_BIN_DIR)
375 binaries_to_copy = []
376 for f in r.output.splitlines():
377 binaries_to_copy.append(
378 chrome_util.Path(os.path.basename(f), exe=True, optional=True))
379
380 staging_dir = os.path.join(
381 self.tempdir, os.path.basename(_CHROME_TEST_BIN_DIR))
382 _PrepareStagingDir(self.options, self.tempdir, staging_dir,
383 copy_paths=binaries_to_copy)
Brian Sheedy86f12342020-10-29 15:30:02 -0700384 # Deploying can occasionally run into issues with rsync getting a broken
385 # pipe, so retry several times. See crbug.com/1141618 for more
386 # information.
387 retry_util.RetryException(
388 None, 3, self.device.CopyToDevice, staging_dir,
389 os.path.dirname(_CHROME_TEST_BIN_DIR), mode='rsync')
Ben Pastenee484b342020-06-30 18:29:27 -0700390
David James88e6f032013-03-02 08:13:20 -0800391 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700392 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800393 logging.info('Testing connection to the device...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400394 self.device.run('true')
David James88e6f032013-03-02 08:13:20 -0800395 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700396 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800397 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700398
Avery Musbach3edff0e2020-03-27 13:35:53 -0700399 def _CheckBoard(self):
400 """Check that the Chrome build is targeted for the device board."""
401 if self.options.board == self.device.board:
402 return
403 logging.warning('Device board is %s whereas target board is %s.',
404 self.device.board, self.options.board)
405 if self.options.force:
406 return
407 if not cros_build_lib.BooleanPrompt('Continue despite board mismatch?',
408 False):
409 raise DeployFailure('Aborted.')
410
Steve Funge984a532013-11-25 17:09:25 -0800411 def _CheckDeployType(self):
Steve Fung63d705d2014-03-16 03:14:03 -0700412 if self.options.build_dir:
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700413 def BinaryExists(filename):
414 """Checks if the passed-in file is present in the build directory."""
415 return os.path.exists(os.path.join(self.options.build_dir, filename))
416
Erik Chen75a2f492020-08-06 19:15:11 -0700417 # In the future, lacros-chrome and ash-chrome will likely be named
418 # something other than 'chrome' to avoid confusion.
Daniel Erat9813f0e2014-11-12 11:00:28 -0700419 # Handle non-Chrome deployments.
420 if not BinaryExists('chrome'):
Daniel Eratf53bd3a2016-12-02 11:28:36 -0700421 if BinaryExists('app_shell'):
Daniel Erat9813f0e2014-11-12 11:00:28 -0700422 self.copy_paths = chrome_util.GetCopyPaths('app_shell')
423
David James88e6f032013-03-02 08:13:20 -0800424 def _PrepareStagingDir(self):
Steve Funge984a532013-11-25 17:09:25 -0800425 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir,
426 self.copy_paths, self.chrome_dir)
David James88e6f032013-03-02 08:13:20 -0800427
Thiago Goncales12793312013-05-23 11:26:17 -0700428 def _MountTarget(self):
429 logging.info('Mounting Chrome...')
430
Anushruth8d797672019-10-17 12:22:31 -0700431 # Create directory if does not exist.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400432 self.device.run(_MKDIR_P_CMD % self.options.mount_dir)
Anushruth8d797672019-10-17 12:22:31 -0700433 try:
434 # Umount the existing mount on mount_dir if present first.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400435 self.device.run(_UMOUNT_DIR_IF_MOUNTPOINT_CMD %
436 {'dir': self.options.mount_dir})
Anushruth8d797672019-10-17 12:22:31 -0700437 except cros_build_lib.RunCommandError as e:
438 logging.error('Failed to umount %s', self.options.mount_dir)
439 # If there is a failure, check if some processs is using the mount_dir.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400440 result = self.device.run(LSOF_COMMAND % (self.options.mount_dir,),
441 check=False, capture_output=True,
442 encoding='utf-8')
Anushruth8d797672019-10-17 12:22:31 -0700443 logging.error('lsof %s -->', self.options.mount_dir)
444 logging.error(result.stdout)
445 raise e
446
Mike Frysinger3459bf52020-03-31 00:52:11 -0400447 self.device.run(_BIND_TO_FINAL_DIR_CMD % (self.options.target_dir,
448 self.options.mount_dir))
Anushruth8d797672019-10-17 12:22:31 -0700449
Thiago Goncales12793312013-05-23 11:26:17 -0700450 # Chrome needs partition to have exec and suid flags set
Mike Frysinger3459bf52020-03-31 00:52:11 -0400451 self.device.run(_SET_MOUNT_FLAGS_CMD % (self.options.mount_dir,))
Robert Flack1dc7ea82014-11-26 13:50:24 -0500452
453 def Cleanup(self):
454 """Clean up RemoteDevice."""
455 if not self.options.staging_only:
456 self.device.Cleanup()
Thiago Goncales12793312013-05-23 11:26:17 -0700457
David James88e6f032013-03-02 08:13:20 -0800458 def Perform(self):
Steve Funge984a532013-11-25 17:09:25 -0800459 self._CheckDeployType()
460
David James88e6f032013-03-02 08:13:20 -0800461 # If requested, just do the staging step.
462 if self.options.staging_only:
463 self._PrepareStagingDir()
464 return 0
465
Erik Chen75a2f492020-08-06 19:15:11 -0700466 # Check that the build matches the device. Lacros-chrome skips this check as
467 # it's currently board independent. This means that it's possible to deploy
468 # a build of lacros-chrome with a mismatched architecture. We don't try to
469 # prevent this developer foot-gun.
470 if not self.options.lacros:
471 self._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700472
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800473 # Ensure that the target directory exists before running parallel steps.
474 self._EnsureTargetDir()
475
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800476 logging.info('Preparing device')
Steve Funge984a532013-11-25 17:09:25 -0800477 steps = [self._GetDeviceInfo, self._CheckConnection,
Erik Chen75a2f492020-08-06 19:15:11 -0700478 self._MountRootfsAsWritable,
Steve Funge984a532013-11-25 17:09:25 -0800479 self._PrepareStagingDir]
Erik Chen75a2f492020-08-06 19:15:11 -0700480
Yuke Liaobe6bac32020-12-26 22:16:49 -0800481 restart_ui = True
482 if self.options.lacros:
Yuke Liao24fc60c2020-12-26 22:16:49 -0800483 # If this is a lacros build, we only want to restart ash-chrome if needed.
484 restart_ui = False
Yuke Liaobe6bac32020-12-26 22:16:49 -0800485 steps.append(self._KillLacrosChrome)
Yuke Liao24fc60c2020-12-26 22:16:49 -0800486 if self.options.modify_config_file:
487 restart_ui = self._ModifyConfigFileIfNeededForLacros()
488
Yuke Liaobe6bac32020-12-26 22:16:49 -0800489 if restart_ui:
490 steps.append(self._KillAshChromeIfNeeded)
ChromeOS Developer78345852020-08-11 23:09:48 -0700491 self._stopped_ui = True
Yuke Liaobe6bac32020-12-26 22:16:49 -0800492
Ryan Cui7193a7e2013-04-26 14:15:19 -0700493 ret = parallel.RunParallelSteps(steps, halt_on_error=True,
494 return_values=True)
495 self._CheckDeviceFreeSpace(ret[0])
David James88e6f032013-03-02 08:13:20 -0800496
Steven Bennettsca73efa2018-07-10 13:36:56 -0700497 # If the root dir is not writable, try disabling rootfs verification.
498 # (We always do this by default so that developers can write to
499 # /etc/chriome_dev.conf and other directories in the rootfs).
500 if self._root_dir_is_still_readonly.is_set():
501 if self.options.noremove_rootfs_verification:
502 logging.warning('Skipping disable rootfs verification.')
503 elif not self._DisableRootfsVerification():
504 logging.warning('Failed to disable rootfs verification.')
505
506 # If the target dir is still not writable (i.e. the user opted out or the
507 # command failed), abort.
508 if not self.device.IsDirWritable(self.options.target_dir):
Erik Chen75a2f492020-08-06 19:15:11 -0700509 if self.options.startui and self._stopped_ui:
Steven Bennettsca73efa2018-07-10 13:36:56 -0700510 logging.info('Restarting Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400511 self.device.run('start ui')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700512 raise DeployFailure('Target location is not writable. Aborting.')
David James88e6f032013-03-02 08:13:20 -0800513
Thiago Goncales12793312013-05-23 11:26:17 -0700514 if self.options.mount_dir is not None:
515 self._MountTarget()
516
David James88e6f032013-03-02 08:13:20 -0800517 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700518 self._Deploy()
Ben Pastenee484b342020-06-30 18:29:27 -0700519 if self.options.deploy_test_binaries:
520 self._DeployTestBinaries()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700521
522
Yuke Liaobe6bac32020-12-26 22:16:49 -0800523 def _ModifyConfigFileIfNeededForLacros(self):
524 """Modifies the /etc/chrome_dev.conf file for lacros-chrome.
525
526 Returns:
527 True if the file is modified, and the return value is usually used to
528 determine whether restarting ash-chrome is needed.
529 """
530 assert self.options.lacros, (
531 'Only deploying lacros-chrome needs to modify the config file.')
532 # Update /etc/chrome_dev.conf to include appropriate flags.
533 modified = False
534 result = self.device.run(ENABLE_LACROS_VIA_CONF_COMMAND, shell=True)
535 if result.stdout.strip() == MODIFIED_CONF_FILE:
536 modified = True
537 result = self.device.run(
538 _SET_LACROS_PATH_VIA_CONF_COMMAND % {
539 'conf_file': _CONF_FILE,
540 'lacros_path': self.options.target_dir,
541 'modified_conf_file': MODIFIED_CONF_FILE
542 },
543 shell=True)
544 if result.stdout.strip() == MODIFIED_CONF_FILE:
545 modified = True
546
547 return modified
548
549
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700550def ValidateStagingFlags(value):
551 """Convert formatted string to dictionary."""
552 return chrome_util.ProcessShellFlags(value)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700553
554
Steven Bennetts368c3e52016-09-23 13:05:21 -0700555def ValidateGnArgs(value):
556 """Convert GN_ARGS-formatted string to dictionary."""
557 return gn_helpers.FromGNArgs(value)
558
559
Ryan Cuie535b172012-10-19 18:25:03 -0700560def _CreateParser():
561 """Create our custom parser."""
Mike Frysingerc3061a62015-06-04 04:16:18 -0400562 parser = commandline.ArgumentParser(description=__doc__, caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700563
Ryan Cuia56a71e2012-10-18 18:40:35 -0700564 # TODO(rcui): Have this use the UI-V2 format of having source and target
565 # device be specified as positional arguments.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400566 parser.add_argument('--force', action='store_true', default=False,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700567 help='Skip all prompts (such as the prompt for disabling '
568 'of rootfs verification). This may result in the '
569 'target machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800570 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
Mike Frysingerc3061a62015-06-04 04:16:18 -0400571 parser.add_argument('--board', default=sdk_board_env,
Mike Frysinger80de5012019-08-01 14:10:53 -0400572 help='The board the Chrome build is targeted for. When '
Mike Frysingerc3061a62015-06-04 04:16:18 -0400573 "in a 'cros chrome-sdk' shell, defaults to the SDK "
Mike Frysinger80de5012019-08-01 14:10:53 -0400574 'board.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400575 parser.add_argument('--build-dir', type='path',
576 help='The directory with Chrome build artifacts to '
577 'deploy from. Typically of format '
578 '<chrome_root>/out/Debug. When this option is used, '
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700579 'the GN_ARGS environment variable must be set.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400580 parser.add_argument('--target-dir', type='path',
581 default=None,
582 help='Target directory on device to deploy Chrome into.')
583 parser.add_argument('-g', '--gs-path', type='gs_path',
584 help='GS path that contains the chrome to deploy.')
Adrian Eldera2c548a2017-11-07 19:01:29 -0500585 parser.add_argument('--private-key', type='path', default=None,
586 help='An ssh private key to use when deploying to '
587 'a CrOS device.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400588 parser.add_argument('--nostartui', action='store_false', dest='startui',
589 default=True,
590 help="Don't restart the ui daemon after deployment.")
591 parser.add_argument('--nostrip', action='store_false', dest='dostrip',
592 default=True,
593 help="Don't strip binaries during deployment. Warning: "
594 'the resulting binaries will be very large!')
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700595 parser.add_argument(
596 '-d', '--device',
597 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
598 help='Device hostname or IP in the format hostname[:port].')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400599 parser.add_argument('--mount-dir', type='path', default=None,
600 help='Deploy Chrome in target directory and bind it '
601 'to the directory specified by this flag.'
602 'Any existing mount on this directory will be '
603 'umounted first.')
604 parser.add_argument('--mount', action='store_true', default=False,
605 help='Deploy Chrome to default target directory and bind '
606 'it to the default mount directory.'
607 'Any existing mount on this directory will be '
608 'umounted first.')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700609 parser.add_argument('--noremove-rootfs-verification', action='store_true',
610 default=False, help='Never remove rootfs verification.')
Ben Pastenee484b342020-06-30 18:29:27 -0700611 parser.add_argument('--deploy-test-binaries', action='store_true',
612 default=False,
613 help='Also deploy any test binaries to %s. Useful for '
614 'running any Tast tests that execute these '
615 'binaries.' % _CHROME_TEST_BIN_DIR)
Erik Chen75a2f492020-08-06 19:15:11 -0700616 parser.add_argument('--lacros', action='store_true', default=False,
617 help='Deploys lacros-chrome rather than ash-chrome.')
Yuke Liao24fc60c2020-12-26 22:16:49 -0800618 parser.add_argument('--skip-modifying-config-file', action='store_false',
619 dest='modify_config_file',
620 help='By default, deploying lacros-chrome modifies the '
621 '/etc/chrome_dev.conf file, which interferes with '
622 'automated testing, and this argument disables it.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700623
Mike Frysingerc3061a62015-06-04 04:16:18 -0400624 group = parser.add_argument_group('Advanced Options')
625 group.add_argument('-l', '--local-pkg-path', type='path',
626 help='Path to local chrome prebuilt package to deploy.')
627 group.add_argument('--sloppy', action='store_true', default=False,
628 help='Ignore when mandatory artifacts are missing.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700629 group.add_argument('--staging-flags', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400630 help=('Extra flags to control staging. Valid flags are - '
631 '%s' % ', '.join(chrome_util.STAGING_FLAGS)))
Steven Bennetts46a84c32016-08-12 15:20:46 -0700632 # TODO(stevenjb): Remove --strict entirely once removed from the ebuild.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400633 group.add_argument('--strict', action='store_true', default=False,
Steven Bennetts46a84c32016-08-12 15:20:46 -0700634 help='Deprecated. Default behavior is "strict". Use '
635 '--sloppy to omit warnings for missing optional '
636 'files.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400637 group.add_argument('--strip-flags', default=None,
638 help="Flags to call the 'strip' binutil tool with. "
Mike Frysinger80de5012019-08-01 14:10:53 -0400639 'Overrides the default arguments.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400640 group.add_argument('--ping', action='store_true', default=False,
641 help='Ping the device before connection attempt.')
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700642 group.add_argument('--process-timeout', type=int,
643 default=KILL_PROC_MAX_WAIT,
644 help='Timeout for process shutdown.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700645
Mike Frysingerc3061a62015-06-04 04:16:18 -0400646 group = parser.add_argument_group(
647 'Metadata Overrides (Advanced)',
648 description='Provide all of these overrides in order to remove '
649 'dependencies on metadata.json existence.')
650 group.add_argument('--target-tc', action='store', default=None,
651 help='Override target toolchain name, e.g. '
652 'x86_64-cros-linux-gnu')
653 group.add_argument('--toolchain-url', action='store', default=None,
654 help='Override toolchain url format pattern, e.g. '
655 '2014/04/%%(target)s-2014.04.23.220740.tar.xz')
Aviv Keshet1c986f32014-04-24 13:20:49 -0700656
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700657 # DEPRECATED: --gyp-defines is ignored, but retained for backwards
658 # compatibility. TODO(stevenjb): Remove once eliminated from the ebuild.
659 parser.add_argument('--gyp-defines', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400660 help=argparse.SUPPRESS)
Steven Bennetts368c3e52016-09-23 13:05:21 -0700661
662 # GN_ARGS (args.gn) used to build Chrome. Influences which files are staged
663 # when --build-dir is set. Defaults to reading from the GN_ARGS env variable.
664 # CURRENLY IGNORED, ADDED FOR FORWARD COMPATABILITY.
665 parser.add_argument('--gn-args', default=None, type=ValidateGnArgs,
666 help=argparse.SUPPRESS)
667
Ryan Cuia56a71e2012-10-18 18:40:35 -0700668 # Path of an empty directory to stage chrome artifacts to. Defaults to a
669 # temporary directory that is removed when the script finishes. If the path
670 # is specified, then it will not be removed.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400671 parser.add_argument('--staging-dir', type='path', default=None,
672 help=argparse.SUPPRESS)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700673 # Only prepare the staging directory, and skip deploying to the device.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400674 parser.add_argument('--staging-only', action='store_true', default=False,
675 help=argparse.SUPPRESS)
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700676 # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
677 # is used as-is, and not normalized. Used by the Chrome ebuild to skip
678 # fetching the SDK toolchain.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400679 parser.add_argument('--strip-bin', default=None, help=argparse.SUPPRESS)
Satoru Takabayashif2893002017-06-20 14:52:48 +0900680 parser.add_argument('--compress', action='store', default='auto',
681 choices=('always', 'never', 'auto'),
682 help='Choose the data compression behavior. Default '
683 'is set to "auto", that disables compression if '
684 'the target device has a gigabit ethernet port.')
Ryan Cuie535b172012-10-19 18:25:03 -0700685 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700686
Ryan Cuie535b172012-10-19 18:25:03 -0700687
688def _ParseCommandLine(argv):
689 """Parse args, and run environment-independent checks."""
690 parser = _CreateParser()
Mike Frysingerc3061a62015-06-04 04:16:18 -0400691 options = parser.parse_args(argv)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700692
Ryan Cuia56a71e2012-10-18 18:40:35 -0700693 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
694 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800695 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700696 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
697 parser.error('Cannot specify both --build_dir and '
698 '--gs-path/--local-pkg-patch')
Erik Chen75a2f492020-08-06 19:15:11 -0700699 if options.lacros:
700 if options.board:
701 parser.error('--board is not supported with --lacros')
702 # The stripping implemented in this file rely on the cros-chrome-sdk, which
703 # is inappropriate for Lacros. Lacros stripping is currently not
704 # implemented.
705 if options.dostrip:
706 parser.error('--lacros requires --nostrip')
707 if options.mount_dir or options.mount:
708 parser.error('--lacros does not support --mount or --mount-dir')
709 if options.deploy_test_binaries:
710 parser.error('--lacros does not support --deploy-test-binaries')
711 if options.local_pkg_path:
712 parser.error('--lacros does not support --local-pkg-path')
713 else:
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +0900714 if not options.board and options.build_dir:
715 match = re.search(r'out_([^/]+)/Release$', options.build_dir)
716 if match:
717 options.board = match.group(1)
718 logging.info('--board is set to %s', options.board)
Erik Chen75a2f492020-08-06 19:15:11 -0700719 if not options.board:
720 parser.error('--board is required')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700721 if options.gs_path and options.local_pkg_path:
722 parser.error('Cannot specify both --gs-path and --local-pkg-path')
Ben Pasteneb44d8e72021-02-19 13:56:43 -0800723 if not (options.staging_only or options.device):
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700724 parser.error('Need to specify --device')
Steven Bennetts46a84c32016-08-12 15:20:46 -0700725 if options.staging_flags and not options.build_dir:
726 parser.error('--staging-flags require --build-dir to be set.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700727
Steven Bennetts46a84c32016-08-12 15:20:46 -0700728 if options.strict:
729 logging.warning('--strict is deprecated.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700730 if options.gyp_defines:
731 logging.warning('--gyp-defines is deprecated.')
Thiago Goncales12793312013-05-23 11:26:17 -0700732
733 if options.mount or options.mount_dir:
734 if not options.target_dir:
735 options.target_dir = _CHROME_DIR_MOUNT
736 else:
737 if not options.target_dir:
Erik Chen75a2f492020-08-06 19:15:11 -0700738 options.target_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Thiago Goncales12793312013-05-23 11:26:17 -0700739
740 if options.mount and not options.mount_dir:
741 options.mount_dir = _CHROME_DIR
742
Mike Frysingerc3061a62015-06-04 04:16:18 -0400743 return options
Ryan Cui3045c5d2012-07-13 18:00:33 -0700744
745
Mike Frysingerc3061a62015-06-04 04:16:18 -0400746def _PostParseCheck(options):
Steve Funge984a532013-11-25 17:09:25 -0800747 """Perform some usage validation (after we've parsed the arguments).
Ryan Cui3045c5d2012-07-13 18:00:33 -0700748
749 Args:
Mike Frysinger5fe3f222016-09-01 00:14:16 -0400750 options: The options object returned by the cli parser.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700751 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700752 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
753 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
754
Steven Bennetts368c3e52016-09-23 13:05:21 -0700755 if not options.gn_args:
756 gn_env = os.getenv('GN_ARGS')
757 if gn_env is not None:
758 options.gn_args = gn_helpers.FromGNArgs(gn_env)
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800759 logging.debug('GN_ARGS taken from environment: %s', options.gn_args)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700760
Steven Bennetts60600462016-05-12 10:40:20 -0700761 if not options.staging_flags:
762 use_env = os.getenv('USE')
763 if use_env is not None:
764 options.staging_flags = ' '.join(set(use_env.split()).intersection(
765 chrome_util.STAGING_FLAGS))
766 logging.info('Staging flags taken from USE in environment: %s',
767 options.staging_flags)
768
Ryan Cuia56a71e2012-10-18 18:40:35 -0700769
Ryan Cui504db722013-01-22 11:48:01 -0800770def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700771 """Get the chrome prebuilt tarball from GS.
772
Mike Frysinger02e1e072013-11-10 22:11:34 -0500773 Returns:
774 Path to the fetched chrome tarball.
Ryan Cuia56a71e2012-10-18 18:40:35 -0700775 """
David James9374aac2013-10-08 16:00:17 -0700776 gs_ctx = gs.GSContext(cache_dir=cache_dir, init_boto=True)
Mike Frysinger7ad4fd62014-02-11 16:53:56 -0500777 files = gs_ctx.LS(gs_path)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800778 files = [found for found in files if
779 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
780 if not files:
781 raise Exception('No chrome package found at %s' % gs_path)
782 elif len(files) > 1:
783 # - Users should provide us with a direct link to either a stripped or
784 # unstripped chrome package.
785 # - In the case of being provided with an archive directory, where both
786 # stripped and unstripped chrome available, use the stripped chrome
787 # package.
788 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
789 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
790 files = [f for f in files if not 'unstripped' in f]
791 assert len(files) == 1
792 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800793
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800794 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800795 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800796 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
797 chrome_path = os.path.join(tempdir, filename)
798 assert os.path.exists(chrome_path)
799 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700800
801
Ryan Cuif890a3e2013-03-07 18:57:06 -0800802@contextlib.contextmanager
803def _StripBinContext(options):
804 if not options.dostrip:
805 yield None
806 elif options.strip_bin:
807 yield options.strip_bin
808 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800809 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800810 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
Aviv Keshet1c986f32014-04-24 13:20:49 -0700811 with sdk.Prepare(components=components, target_tc=options.target_tc,
812 toolchain_url=options.toolchain_url) as ctx:
Ryan Cui686ec052013-02-12 16:39:41 -0800813 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
814 constants.CHROME_ENV_FILE)
815 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
816 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
817 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800818 yield strip_bin
819
820
Steve Funge984a532013-11-25 17:09:25 -0800821def _PrepareStagingDir(options, tempdir, staging_dir, copy_paths=None,
Erik Chen75a2f492020-08-06 19:15:11 -0700822 chrome_dir=None):
Ryan Cuif890a3e2013-03-07 18:57:06 -0800823 """Place the necessary files in the staging directory.
824
825 The staging directory is the directory used to rsync the build artifacts over
826 to the device. Only the necessary Chrome build artifacts are put into the
827 staging directory.
828 """
Erik Chen75a2f492020-08-06 19:15:11 -0700829 if chrome_dir is None:
830 chrome_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Ryan Cui5866be02013-03-18 14:12:00 -0700831 osutils.SafeMakedirs(staging_dir)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400832 os.chmod(staging_dir, 0o755)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800833 if options.build_dir:
834 with _StripBinContext(options) as strip_bin:
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700835 strip_flags = (None if options.strip_flags is None else
836 shlex.split(options.strip_flags))
Ryan Cui686ec052013-02-12 16:39:41 -0800837 chrome_util.StageChromeFromBuildDir(
Steven Bennetts46a84c32016-08-12 15:20:46 -0700838 staging_dir, options.build_dir, strip_bin,
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700839 sloppy=options.sloppy, gn_args=options.gn_args,
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700840 staging_flags=options.staging_flags,
Steve Funge984a532013-11-25 17:09:25 -0800841 strip_flags=strip_flags, copy_paths=copy_paths)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700842 else:
843 pkg_path = options.local_pkg_path
844 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800845 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
846 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700847
848 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800849 logging.info('Extracting %s...', pkg_path)
Ryan Cui5866be02013-03-18 14:12:00 -0700850 # Extract only the ./opt/google/chrome contents, directly into the staging
851 # dir, collapsing the directory hierarchy.
Steve Funge984a532013-11-25 17:09:25 -0800852 if pkg_path[-4:] == '.zip':
Mike Frysinger45602c72019-09-22 02:15:11 -0400853 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800854 ['unzip', '-X', pkg_path, _ANDROID_DIR_EXTRACT_PATH, '-d',
855 staging_dir])
856 for filename in glob.glob(os.path.join(staging_dir, 'system/chrome/*')):
857 shutil.move(filename, staging_dir)
858 osutils.RmDir(os.path.join(staging_dir, 'system'), ignore_missing=True)
859 else:
Mike Frysinger45602c72019-09-22 02:15:11 -0400860 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800861 ['tar', '--strip-components', '4', '--extract',
862 '--preserve-permissions', '--file', pkg_path, '.%s' % chrome_dir],
863 cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700864
Ryan Cui71aa8de2013-04-19 16:12:55 -0700865
Ryan Cui3045c5d2012-07-13 18:00:33 -0700866def main(argv):
Mike Frysingerc3061a62015-06-04 04:16:18 -0400867 options = _ParseCommandLine(argv)
868 _PostParseCheck(options)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700869
Aviv Keshet01a82e92017-03-02 17:39:59 -0800870 with osutils.TempDir(set_global=True) as tempdir:
871 staging_dir = options.staging_dir
872 if not staging_dir:
873 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700874
Aviv Keshet01a82e92017-03-02 17:39:59 -0800875 deploy = DeployChrome(options, tempdir, staging_dir)
876 try:
877 deploy.Perform()
878 except failures_lib.StepFailure as ex:
879 raise SystemExit(str(ex).strip())
880 deploy.Cleanup()