blob: b2434bdefe51debe8a04e7609f3dc7916b7a7aa2 [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
Chris McDonald59650c32021-07-20 15:29:28 -060023import logging
David James88e6f032013-03-02 08:13:20 -080024import multiprocessing
Ryan Cui3045c5d2012-07-13 18:00:33 -070025import os
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090026import re
Ryan Cui5b7c2ed2013-03-18 18:45:46 -070027import shlex
Steve Funge984a532013-11-25 17:09:25 -080028import shutil
Ryan Cui3045c5d2012-07-13 18:00:33 -070029import time
Ryan Cui3045c5d2012-07-13 18:00:33 -070030
Chris McDonald59650c32021-07-20 15:29:28 -060031from chromite.third_party.gn_helpers import gn_helpers
32
David Pursellcfd58872015-03-19 09:15:48 -070033from chromite.cli.cros import cros_chrome_sdk
Ryan Cuia56a71e2012-10-18 18:40:35 -070034from chromite.lib import chrome_util
Ryan Cuie535b172012-10-19 18:25:03 -070035from chromite.lib import commandline
Mike Frysinger47b10992021-01-23 00:23:25 -050036from chromite.lib import constants
Ralph Nathan91874ca2015-03-19 13:29:41 -070037from chromite.lib import cros_build_lib
Mike Frysinger47b10992021-01-23 00:23:25 -050038from chromite.lib import failures_lib
Ryan Cui777ff422012-12-07 13:12:54 -080039from chromite.lib import gs
Ryan Cui3045c5d2012-07-13 18:00:33 -070040from chromite.lib import osutils
David James88e6f032013-03-02 08:13:20 -080041from chromite.lib import parallel
Ryan Cui3045c5d2012-07-13 18:00:33 -070042from chromite.lib import remote_access as remote
Brian Sheedy86f12342020-10-29 15:30:02 -070043from chromite.lib import retry_util
David James3432acd2013-11-27 10:02:18 -080044from chromite.lib import timeout_util
Ryan Cui3045c5d2012-07-13 18:00:33 -070045
46
Ryan Cui3045c5d2012-07-13 18:00:33 -070047KERNEL_A_PARTITION = 2
48KERNEL_B_PARTITION = 4
49
50KILL_PROC_MAX_WAIT = 10
51POST_KILL_WAIT = 2
52
David Haddock3151d912017-10-24 03:50:32 +000053MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Anushruth8d797672019-10-17 12:22:31 -070054LSOF_COMMAND_CHROME = 'lsof %s/chrome'
55LSOF_COMMAND = 'lsof %s'
David Haddock3151d912017-10-24 03:50:32 +000056DBUS_RELOAD_COMMAND = 'killall -HUP dbus-daemon'
Ryan Cui3045c5d2012-07-13 18:00:33 -070057
Steve Funge984a532013-11-25 17:09:25 -080058_ANDROID_DIR = '/system/chrome'
59_ANDROID_DIR_EXTRACT_PATH = 'system/chrome/*'
60
David James2cb34002013-03-01 18:42:40 -080061_CHROME_DIR = '/opt/google/chrome'
Thiago Goncales12793312013-05-23 11:26:17 -070062_CHROME_DIR_MOUNT = '/mnt/stateful_partition/deploy_rootfs/opt/google/chrome'
Ben Pastenee484b342020-06-30 18:29:27 -070063_CHROME_TEST_BIN_DIR = '/usr/local/libexec/chrome-binary-tests'
David James2cb34002013-03-01 18:42:40 -080064
David Haddock3151d912017-10-24 03:50:32 +000065_UMOUNT_DIR_IF_MOUNTPOINT_CMD = (
66 'if mountpoint -q %(dir)s; then umount %(dir)s; fi')
67_BIND_TO_FINAL_DIR_CMD = 'mount --rbind %s %s'
68_SET_MOUNT_FLAGS_CMD = 'mount -o remount,exec,suid %s'
Anushruth8d797672019-10-17 12:22:31 -070069_MKDIR_P_CMD = 'mkdir -p --mode 0775 %s'
Ben Pastenee484b342020-06-30 18:29:27 -070070_FIND_TEST_BIN_CMD = 'find %s -maxdepth 1 -executable -type f' % (
71 _CHROME_TEST_BIN_DIR)
David Haddock3151d912017-10-24 03:50:32 +000072
73DF_COMMAND = 'df -k %s'
74
ChromeOS Developer536f59a2020-08-10 14:39:27 -070075LACROS_DIR = '/usr/local/lacros-chrome'
Erik Chen75a2f492020-08-06 19:15:11 -070076_CONF_FILE = '/etc/chrome_dev.conf'
77_KILL_LACROS_CHROME_CMD = 'pkill -f %(lacros_dir)s/chrome'
78MODIFIED_CONF_FILE = f'modified {_CONF_FILE}'
79
80# This command checks if "--enable-features=LacrosSupport" is present in
81# /etc/chrome_dev.conf. If it is not, then it is added.
82# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
83# to modify chrome_dev.conf. Either revisit this policy or find another
84# mechanism to pass configuration to ash-chrome.
85ENABLE_LACROS_VIA_CONF_COMMAND = f"""
86 if ! grep -q "^--enable-features=LacrosSupport$" {_CONF_FILE}; then
87 echo "--enable-features=LacrosSupport" >> {_CONF_FILE};
88 echo {MODIFIED_CONF_FILE};
89 fi
90"""
91
92# This command checks if "--lacros-chrome-path=" is present with the right value
93# in /etc/chrome_dev.conf. If it is not, then all previous instances are removed
94# and the new one is added.
95# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
96# to modify chrome_dev.conf. Either revisit this policy or find another
97# mechanism to pass configuration to ash-chrome.
98_SET_LACROS_PATH_VIA_CONF_COMMAND = """
99 if ! grep -q "^--lacros-chrome-path=%(lacros_path)s$" %(conf_file)s; then
100 sed 's/--lacros-chrome-path/#--lacros-chrome-path/' %(conf_file)s;
101 echo "--lacros-chrome-path=%(lacros_path)s" >> %(conf_file)s;
102 echo %(modified_conf_file)s;
103 fi
104"""
Mike Frysingere65f3752014-12-08 00:46:39 -0500105
Ryan Cui3045c5d2012-07-13 18:00:33 -0700106def _UrlBaseName(url):
107 """Return the last component of the URL."""
108 return url.rstrip('/').rpartition('/')[-1]
109
110
Yu-Ju Hongc54d3342014-05-14 12:42:06 -0700111class DeployFailure(failures_lib.StepFailure):
David James88e6f032013-03-02 08:13:20 -0800112 """Raised whenever the deploy fails."""
113
114
Ryan Cui7193a7e2013-04-26 14:15:19 -0700115DeviceInfo = collections.namedtuple(
116 'DeviceInfo', ['target_dir_size', 'target_fs_free'])
117
118
Ryan Cui3045c5d2012-07-13 18:00:33 -0700119class DeployChrome(object):
120 """Wraps the core deployment functionality."""
Mike Frysingere65f3752014-12-08 00:46:39 -0500121
Ryan Cuia56a71e2012-10-18 18:40:35 -0700122 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -0700123 """Initialize the class.
124
Mike Frysinger02e1e072013-11-10 22:11:34 -0500125 Args:
Mike Frysingerc3061a62015-06-04 04:16:18 -0400126 options: options object.
Ryan Cuie535b172012-10-19 18:25:03 -0700127 tempdir: Scratch space for the class. Caller has responsibility to clean
128 it up.
Steve Funge984a532013-11-25 17:09:25 -0800129 staging_dir: Directory to stage the files to.
Ryan Cuie535b172012-10-19 18:25:03 -0700130 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700131 self.tempdir = tempdir
132 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -0700133 self.staging_dir = staging_dir
Robert Flack1dc7ea82014-11-26 13:50:24 -0500134 if not self.options.staging_only:
Ben Pasteneb44d8e72021-02-19 13:56:43 -0800135 hostname = options.device.hostname
136 port = options.device.port
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700137 self.device = remote.ChromiumOSDevice(hostname, port=port,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700138 ping=options.ping,
139 private_key=options.private_key,
140 include_dev_paths=False)
Steven Bennettsca73efa2018-07-10 13:36:56 -0700141 self._root_dir_is_still_readonly = multiprocessing.Event()
Steven Bennetts5a7c72d2016-10-17 20:04:46 +0000142
Erik Chen75a2f492020-08-06 19:15:11 -0700143 self._deployment_name = 'lacros' if options.lacros else 'chrome'
144 self.copy_paths = chrome_util.GetCopyPaths(self._deployment_name)
145
146 self.chrome_dir = LACROS_DIR if self.options.lacros else _CHROME_DIR
147
148 # Whether UI was stopped during setup.
149 self._stopped_ui = False
Steve Funge984a532013-11-25 17:09:25 -0800150
Ryan Cui7193a7e2013-04-26 14:15:19 -0700151 def _GetRemoteMountFree(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400152 result = self.device.run(DF_COMMAND % remote_dir)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700153 line = result.output.splitlines()[1]
Steve Funge984a532013-11-25 17:09:25 -0800154 value = line.split()[3]
155 multipliers = {
156 'G': 1024 * 1024 * 1024,
157 'M': 1024 * 1024,
158 'K': 1024,
159 }
160 return int(value.rstrip('GMK')) * multipliers.get(value[-1], 1)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700161
162 def _GetRemoteDirSize(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400163 result = self.device.run('du -ks %s' % remote_dir,
164 capture_output=True, encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700165 return int(result.output.split()[0])
166
167 def _GetStagingDirSize(self):
Mike Frysinger45602c72019-09-22 02:15:11 -0400168 result = cros_build_lib.dbg_run(['du', '-ks', self.staging_dir],
Mike Frysinger0282d222019-12-17 17:15:48 -0500169 stdout=True, capture_output=True,
Mike Frysingerb88d5fb2019-10-23 00:38:45 -0400170 encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700171 return int(result.output.split()[0])
172
Ryan Cui3045c5d2012-07-13 18:00:33 -0700173 def _ChromeFileInUse(self):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400174 result = self.device.run(LSOF_COMMAND_CHROME % (self.options.target_dir,),
175 check=False, capture_output=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700176 return result.returncode == 0
177
Justin TerAvestfac210e2017-04-13 11:39:00 -0600178 def _Reboot(self):
179 # A reboot in developer mode takes a while (and has delays), so the user
180 # will have time to read and act on the USB boot instructions below.
181 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
182 self.device.Reboot()
183
Ryan Cui3045c5d2012-07-13 18:00:33 -0700184 def _DisableRootfsVerification(self):
185 if not self.options.force:
186 logging.error('Detected that the device has rootfs verification enabled.')
187 logging.info('This script can automatically remove the rootfs '
188 'verification, which requires that it reboot the device.')
189 logging.info('Make sure the device is in developer mode!')
190 logging.info('Skip this prompt by specifying --force.')
Ben Chan37d64e52018-10-05 15:35:02 -0700191 if not cros_build_lib.BooleanPrompt('Remove rootfs verification?', False):
Steven Bennettsca73efa2018-07-10 13:36:56 -0700192 return False
Ryan Cui3045c5d2012-07-13 18:00:33 -0700193
Ben Pasteneb44d8e72021-02-19 13:56:43 -0800194 logging.info('Removing rootfs verification from %s', self.options.device)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700195 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
196 # Use --force to bypass the checks.
David Haddock3151d912017-10-24 03:50:32 +0000197 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
198 '--remove_rootfs_verification --force')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700199 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400200 self.device.run(cmd % partition, check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700201
Justin TerAvestfac210e2017-04-13 11:39:00 -0600202 self._Reboot()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700203
David James88e6f032013-03-02 08:13:20 -0800204 # Now that the machine has been rebooted, we need to kill Chrome again.
Erik Chen75a2f492020-08-06 19:15:11 -0700205 self._KillAshChromeIfNeeded()
David James88e6f032013-03-02 08:13:20 -0800206
207 # Make sure the rootfs is writable now.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500208 self._MountRootfsAsWritable(check=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700209
Steven Bennettsca73efa2018-07-10 13:36:56 -0700210 return True
211
Ryan Cui3045c5d2012-07-13 18:00:33 -0700212 def _CheckUiJobStarted(self):
213 # status output is in the format:
214 # <job_name> <status> ['process' <pid>].
215 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800216 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400217 result = self.device.run('status ui', capture_output=True,
218 encoding='utf-8')
Ryan Cuif2d1a582013-02-19 14:08:13 -0800219 except cros_build_lib.RunCommandError as e:
220 if 'Unknown job' in e.result.error:
221 return False
222 else:
223 raise e
224
Ryan Cui3045c5d2012-07-13 18:00:33 -0700225 return result.output.split()[1].split('/')[0] == 'start'
226
Erik Chen75a2f492020-08-06 19:15:11 -0700227 def _KillLacrosChrome(self):
228 """This method kills lacros-chrome on the device, if it's running."""
229 self.device.run(_KILL_LACROS_CHROME_CMD %
230 {'lacros_dir': self.options.target_dir}, check=False)
231
232 def _KillAshChromeIfNeeded(self):
233 """This method kills ash-chrome on the device, if it's running.
234
235 This method calls 'stop ui', and then also manually pkills both ash-chrome
236 and the session manager.
237 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700238 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800239 logging.info('Shutting down Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400240 self.device.run('stop ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700241
242 # Developers sometimes run session_manager manually, in which case we'll
243 # need to help shut the chrome processes down.
244 try:
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700245 with timeout_util.Timeout(self.options.process_timeout):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700246 while self._ChromeFileInUse():
247 logging.warning('The chrome binary on the device is in use.')
248 logging.warning('Killing chrome and session_manager processes...\n')
249
Mike Frysinger3459bf52020-03-31 00:52:11 -0400250 self.device.run("pkill 'chrome|session_manager'", check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700251 # Wait for processes to actually terminate
252 time.sleep(POST_KILL_WAIT)
253 logging.info('Rechecking the chrome binary...')
David James3432acd2013-11-27 10:02:18 -0800254 except timeout_util.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800255 msg = ('Could not kill processes after %s seconds. Please exit any '
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700256 'running chrome processes and try again.'
257 % self.options.process_timeout)
David James88e6f032013-03-02 08:13:20 -0800258 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700259
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500260 def _MountRootfsAsWritable(self, check=False):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800261 """Mounts the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700262
Steven Bennettsca73efa2018-07-10 13:36:56 -0700263 If the command fails and the root dir is not writable then this function
264 sets self._root_dir_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700265
Mike Frysinger02e1e072013-11-10 22:11:34 -0500266 Args:
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500267 check: See remote.RemoteAccess.RemoteSh for details.
David James88e6f032013-03-02 08:13:20 -0800268 """
Yu-Ju Hong7e116ac2014-01-03 17:08:26 -0800269 # TODO: Should migrate to use the remount functions in remote_access.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400270 result = self.device.run(MOUNT_RW_COMMAND, check=check,
271 capture_output=True, encoding='utf-8')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700272 if result.returncode and not self.device.IsDirWritable('/'):
273 self._root_dir_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700274
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800275 def _EnsureTargetDir(self):
276 """Ensures that the target directory exists on the remote device."""
277 target_dir = self.options.target_dir
278 # Any valid /opt directory should already exist so avoid the remote call.
279 if os.path.commonprefix([target_dir, '/opt']) == '/opt':
280 return
Mike Frysinger3459bf52020-03-31 00:52:11 -0400281 self.device.run(['mkdir', '-p', '--mode', '0775', target_dir])
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800282
Ryan Cui7193a7e2013-04-26 14:15:19 -0700283 def _GetDeviceInfo(self):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800284 """Returns the disk space used and available for the target diectory."""
Ryan Cui7193a7e2013-04-26 14:15:19 -0700285 steps = [
286 functools.partial(self._GetRemoteDirSize, self.options.target_dir),
287 functools.partial(self._GetRemoteMountFree, self.options.target_dir)
288 ]
289 return_values = parallel.RunParallelSteps(steps, return_values=True)
290 return DeviceInfo(*return_values)
291
292 def _CheckDeviceFreeSpace(self, device_info):
293 """See if target device has enough space for Chrome.
294
Mike Frysinger02e1e072013-11-10 22:11:34 -0500295 Args:
Ryan Cui7193a7e2013-04-26 14:15:19 -0700296 device_info: A DeviceInfo named tuple.
297 """
298 effective_free = device_info.target_dir_size + device_info.target_fs_free
299 staging_size = self._GetStagingDirSize()
300 if effective_free < staging_size:
Daniel Erat1ae46382014-08-14 10:23:39 -0700301 raise DeployFailure(
302 'Not enough free space on the device. Required: %s MiB, '
Mike Frysinger93e8ffa2019-07-03 20:24:18 -0400303 'actual: %s MiB.' % (staging_size // 1024, effective_free // 1024))
Ryan Cui7193a7e2013-04-26 14:15:19 -0700304 if device_info.target_fs_free < (100 * 1024):
305 logging.warning('The device has less than 100MB free. deploy_chrome may '
306 'hang during the transfer.')
307
Satoru Takabayashif2893002017-06-20 14:52:48 +0900308 def _ShouldUseCompression(self):
309 """Checks if compression should be used for rsync."""
310 if self.options.compress == 'always':
311 return True
312 elif self.options.compress == 'never':
313 return False
314 elif self.options.compress == 'auto':
315 return not self.device.HasGigabitEthernet()
316
Ryan Cui3045c5d2012-07-13 18:00:33 -0700317 def _Deploy(self):
Erik Chen75a2f492020-08-06 19:15:11 -0700318 logging.info('Copying %s to %s on device...', self._deployment_name,
319 self.options.target_dir)
Ilja H. Friedel0ab63e12017-03-28 13:29:48 -0700320 # CopyToDevice will fall back to scp if rsync is corrupted on stateful.
321 # This does not work for deploy.
Satoru Takabayashiab380bc2015-01-15 16:00:41 +0900322 if not self.device.HasRsync():
323 raise DeployFailure(
David Pursellcfd58872015-03-19 09:15:48 -0700324 'rsync is not found on the device.\n'
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500325 'Run dev_install on the device to get rsync installed.')
Robert Flack1dc7ea82014-11-26 13:50:24 -0500326 self.device.CopyToDevice('%s/' % os.path.abspath(self.staging_dir),
327 self.options.target_dir,
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700328 mode='rsync', inplace=True,
Satoru Takabayashif2893002017-06-20 14:52:48 +0900329 compress=self._ShouldUseCompression(),
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700330 debug_level=logging.INFO,
Robert Flack1dc7ea82014-11-26 13:50:24 -0500331 verbose=self.options.verbose)
Ben Pastene5f03b052019-08-12 18:03:24 -0700332
333 # Set the security context on the default Chrome dir if that's where it's
334 # getting deployed, and only on SELinux supported devices.
Erik Chen75a2f492020-08-06 19:15:11 -0700335 if not self.options.lacros and self.device.IsSELinuxAvailable() and (
Ben Pastene5f03b052019-08-12 18:03:24 -0700336 _CHROME_DIR in (self.options.target_dir, self.options.mount_dir)):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400337 self.device.run(['restorecon', '-R', _CHROME_DIR])
Steve Funge984a532013-11-25 17:09:25 -0800338
339 for p in self.copy_paths:
Steve Funge984a532013-11-25 17:09:25 -0800340 if p.mode:
341 # Set mode if necessary.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400342 self.device.run('chmod %o %s/%s' % (
David Haddock3151d912017-10-24 03:50:32 +0000343 p.mode, self.options.target_dir, p.src if not p.dest else p.dest))
Steve Funge984a532013-11-25 17:09:25 -0800344
Erik Chen75a2f492020-08-06 19:15:11 -0700345 if self.options.lacros:
346 self.device.run(['chown', '-R', 'chronos:chronos',
347 self.options.target_dir])
348
Daniel Erat0fce5bf2017-09-28 17:29:23 -0700349 # Send SIGHUP to dbus-daemon to tell it to reload its configs. This won't
350 # pick up major changes (bus type, logging, etc.), but all we care about is
351 # getting the latest policy from /opt/google/chrome/dbus so that Chrome will
352 # be authorized to take ownership of its service names.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400353 self.device.run(DBUS_RELOAD_COMMAND, check=False)
Justin TerAvestfac210e2017-04-13 11:39:00 -0600354
Erik Chen75a2f492020-08-06 19:15:11 -0700355 if self.options.startui and self._stopped_ui:
Steve Funge984a532013-11-25 17:09:25 -0800356 logging.info('Starting UI...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400357 self.device.run('start ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700358
Ben Pastenee484b342020-06-30 18:29:27 -0700359 def _DeployTestBinaries(self):
360 """Deploys any local test binary to _CHROME_TEST_BIN_DIR on the device.
361
362 There could be several binaries located in the local build dir, so compare
363 what's already present on the device in _CHROME_TEST_BIN_DIR , and copy
364 over any that we also built ourselves.
365 """
366 r = self.device.run(_FIND_TEST_BIN_CMD, check=False)
367 if r.returncode != 0:
368 raise DeployFailure('Unable to ls contents of %s' % _CHROME_TEST_BIN_DIR)
369 binaries_to_copy = []
370 for f in r.output.splitlines():
371 binaries_to_copy.append(
372 chrome_util.Path(os.path.basename(f), exe=True, optional=True))
373
374 staging_dir = os.path.join(
375 self.tempdir, os.path.basename(_CHROME_TEST_BIN_DIR))
376 _PrepareStagingDir(self.options, self.tempdir, staging_dir,
377 copy_paths=binaries_to_copy)
Brian Sheedy86f12342020-10-29 15:30:02 -0700378 # Deploying can occasionally run into issues with rsync getting a broken
379 # pipe, so retry several times. See crbug.com/1141618 for more
380 # information.
381 retry_util.RetryException(
382 None, 3, self.device.CopyToDevice, staging_dir,
383 os.path.dirname(_CHROME_TEST_BIN_DIR), mode='rsync')
Ben Pastenee484b342020-06-30 18:29:27 -0700384
David James88e6f032013-03-02 08:13:20 -0800385 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700386 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800387 logging.info('Testing connection to the device...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400388 self.device.run('true')
David James88e6f032013-03-02 08:13:20 -0800389 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700390 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800391 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700392
Avery Musbach3edff0e2020-03-27 13:35:53 -0700393 def _CheckBoard(self):
394 """Check that the Chrome build is targeted for the device board."""
395 if self.options.board == self.device.board:
396 return
397 logging.warning('Device board is %s whereas target board is %s.',
398 self.device.board, self.options.board)
399 if self.options.force:
400 return
401 if not cros_build_lib.BooleanPrompt('Continue despite board mismatch?',
402 False):
403 raise DeployFailure('Aborted.')
404
Steve Funge984a532013-11-25 17:09:25 -0800405 def _CheckDeployType(self):
Steve Fung63d705d2014-03-16 03:14:03 -0700406 if self.options.build_dir:
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700407 def BinaryExists(filename):
408 """Checks if the passed-in file is present in the build directory."""
409 return os.path.exists(os.path.join(self.options.build_dir, filename))
410
Erik Chen75a2f492020-08-06 19:15:11 -0700411 # In the future, lacros-chrome and ash-chrome will likely be named
412 # something other than 'chrome' to avoid confusion.
Daniel Erat9813f0e2014-11-12 11:00:28 -0700413 # Handle non-Chrome deployments.
414 if not BinaryExists('chrome'):
Daniel Eratf53bd3a2016-12-02 11:28:36 -0700415 if BinaryExists('app_shell'):
Daniel Erat9813f0e2014-11-12 11:00:28 -0700416 self.copy_paths = chrome_util.GetCopyPaths('app_shell')
417
David James88e6f032013-03-02 08:13:20 -0800418 def _PrepareStagingDir(self):
Steve Funge984a532013-11-25 17:09:25 -0800419 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir,
420 self.copy_paths, self.chrome_dir)
David James88e6f032013-03-02 08:13:20 -0800421
Thiago Goncales12793312013-05-23 11:26:17 -0700422 def _MountTarget(self):
423 logging.info('Mounting Chrome...')
424
Anushruth8d797672019-10-17 12:22:31 -0700425 # Create directory if does not exist.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400426 self.device.run(_MKDIR_P_CMD % self.options.mount_dir)
Anushruth8d797672019-10-17 12:22:31 -0700427 try:
428 # Umount the existing mount on mount_dir if present first.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400429 self.device.run(_UMOUNT_DIR_IF_MOUNTPOINT_CMD %
430 {'dir': self.options.mount_dir})
Anushruth8d797672019-10-17 12:22:31 -0700431 except cros_build_lib.RunCommandError as e:
432 logging.error('Failed to umount %s', self.options.mount_dir)
433 # If there is a failure, check if some processs is using the mount_dir.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400434 result = self.device.run(LSOF_COMMAND % (self.options.mount_dir,),
435 check=False, capture_output=True,
436 encoding='utf-8')
Anushruth8d797672019-10-17 12:22:31 -0700437 logging.error('lsof %s -->', self.options.mount_dir)
438 logging.error(result.stdout)
439 raise e
440
Mike Frysinger3459bf52020-03-31 00:52:11 -0400441 self.device.run(_BIND_TO_FINAL_DIR_CMD % (self.options.target_dir,
442 self.options.mount_dir))
Anushruth8d797672019-10-17 12:22:31 -0700443
Thiago Goncales12793312013-05-23 11:26:17 -0700444 # Chrome needs partition to have exec and suid flags set
Mike Frysinger3459bf52020-03-31 00:52:11 -0400445 self.device.run(_SET_MOUNT_FLAGS_CMD % (self.options.mount_dir,))
Robert Flack1dc7ea82014-11-26 13:50:24 -0500446
447 def Cleanup(self):
448 """Clean up RemoteDevice."""
449 if not self.options.staging_only:
450 self.device.Cleanup()
Thiago Goncales12793312013-05-23 11:26:17 -0700451
David James88e6f032013-03-02 08:13:20 -0800452 def Perform(self):
Steve Funge984a532013-11-25 17:09:25 -0800453 self._CheckDeployType()
454
David James88e6f032013-03-02 08:13:20 -0800455 # If requested, just do the staging step.
456 if self.options.staging_only:
457 self._PrepareStagingDir()
458 return 0
459
Erik Chen75a2f492020-08-06 19:15:11 -0700460 # Check that the build matches the device. Lacros-chrome skips this check as
461 # it's currently board independent. This means that it's possible to deploy
462 # a build of lacros-chrome with a mismatched architecture. We don't try to
463 # prevent this developer foot-gun.
464 if not self.options.lacros:
465 self._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700466
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800467 # Ensure that the target directory exists before running parallel steps.
468 self._EnsureTargetDir()
469
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800470 logging.info('Preparing device')
Steve Funge984a532013-11-25 17:09:25 -0800471 steps = [self._GetDeviceInfo, self._CheckConnection,
Erik Chen75a2f492020-08-06 19:15:11 -0700472 self._MountRootfsAsWritable,
Steve Funge984a532013-11-25 17:09:25 -0800473 self._PrepareStagingDir]
Erik Chen75a2f492020-08-06 19:15:11 -0700474
Yuke Liaobe6bac32020-12-26 22:16:49 -0800475 restart_ui = True
476 if self.options.lacros:
Yuke Liao24fc60c2020-12-26 22:16:49 -0800477 # If this is a lacros build, we only want to restart ash-chrome if needed.
478 restart_ui = False
Yuke Liaobe6bac32020-12-26 22:16:49 -0800479 steps.append(self._KillLacrosChrome)
Yuke Liao24fc60c2020-12-26 22:16:49 -0800480 if self.options.modify_config_file:
481 restart_ui = self._ModifyConfigFileIfNeededForLacros()
482
Yuke Liaobe6bac32020-12-26 22:16:49 -0800483 if restart_ui:
484 steps.append(self._KillAshChromeIfNeeded)
ChromeOS Developer78345852020-08-11 23:09:48 -0700485 self._stopped_ui = True
Yuke Liaobe6bac32020-12-26 22:16:49 -0800486
Ryan Cui7193a7e2013-04-26 14:15:19 -0700487 ret = parallel.RunParallelSteps(steps, halt_on_error=True,
488 return_values=True)
489 self._CheckDeviceFreeSpace(ret[0])
David James88e6f032013-03-02 08:13:20 -0800490
Steven Bennettsca73efa2018-07-10 13:36:56 -0700491 # If the root dir is not writable, try disabling rootfs verification.
492 # (We always do this by default so that developers can write to
493 # /etc/chriome_dev.conf and other directories in the rootfs).
494 if self._root_dir_is_still_readonly.is_set():
495 if self.options.noremove_rootfs_verification:
496 logging.warning('Skipping disable rootfs verification.')
497 elif not self._DisableRootfsVerification():
498 logging.warning('Failed to disable rootfs verification.')
499
500 # If the target dir is still not writable (i.e. the user opted out or the
501 # command failed), abort.
502 if not self.device.IsDirWritable(self.options.target_dir):
Erik Chen75a2f492020-08-06 19:15:11 -0700503 if self.options.startui and self._stopped_ui:
Steven Bennettsca73efa2018-07-10 13:36:56 -0700504 logging.info('Restarting Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400505 self.device.run('start ui')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700506 raise DeployFailure('Target location is not writable. Aborting.')
David James88e6f032013-03-02 08:13:20 -0800507
Thiago Goncales12793312013-05-23 11:26:17 -0700508 if self.options.mount_dir is not None:
509 self._MountTarget()
510
David James88e6f032013-03-02 08:13:20 -0800511 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700512 self._Deploy()
Ben Pastenee484b342020-06-30 18:29:27 -0700513 if self.options.deploy_test_binaries:
514 self._DeployTestBinaries()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700515
516
Yuke Liaobe6bac32020-12-26 22:16:49 -0800517 def _ModifyConfigFileIfNeededForLacros(self):
518 """Modifies the /etc/chrome_dev.conf file for lacros-chrome.
519
520 Returns:
521 True if the file is modified, and the return value is usually used to
522 determine whether restarting ash-chrome is needed.
523 """
524 assert self.options.lacros, (
525 'Only deploying lacros-chrome needs to modify the config file.')
526 # Update /etc/chrome_dev.conf to include appropriate flags.
527 modified = False
528 result = self.device.run(ENABLE_LACROS_VIA_CONF_COMMAND, shell=True)
529 if result.stdout.strip() == MODIFIED_CONF_FILE:
530 modified = True
531 result = self.device.run(
532 _SET_LACROS_PATH_VIA_CONF_COMMAND % {
533 'conf_file': _CONF_FILE,
534 'lacros_path': self.options.target_dir,
535 'modified_conf_file': MODIFIED_CONF_FILE
536 },
537 shell=True)
538 if result.stdout.strip() == MODIFIED_CONF_FILE:
539 modified = True
540
541 return modified
542
543
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700544def ValidateStagingFlags(value):
545 """Convert formatted string to dictionary."""
546 return chrome_util.ProcessShellFlags(value)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700547
548
Steven Bennetts368c3e52016-09-23 13:05:21 -0700549def ValidateGnArgs(value):
550 """Convert GN_ARGS-formatted string to dictionary."""
551 return gn_helpers.FromGNArgs(value)
552
553
Ryan Cuie535b172012-10-19 18:25:03 -0700554def _CreateParser():
555 """Create our custom parser."""
Mike Frysingerc3061a62015-06-04 04:16:18 -0400556 parser = commandline.ArgumentParser(description=__doc__, caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700557
Ryan Cuia56a71e2012-10-18 18:40:35 -0700558 # TODO(rcui): Have this use the UI-V2 format of having source and target
559 # device be specified as positional arguments.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400560 parser.add_argument('--force', action='store_true', default=False,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700561 help='Skip all prompts (such as the prompt for disabling '
562 'of rootfs verification). This may result in the '
563 'target machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800564 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
Mike Frysingerc3061a62015-06-04 04:16:18 -0400565 parser.add_argument('--board', default=sdk_board_env,
Mike Frysinger80de5012019-08-01 14:10:53 -0400566 help='The board the Chrome build is targeted for. When '
Mike Frysingerc3061a62015-06-04 04:16:18 -0400567 "in a 'cros chrome-sdk' shell, defaults to the SDK "
Mike Frysinger80de5012019-08-01 14:10:53 -0400568 'board.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400569 parser.add_argument('--build-dir', type='path',
570 help='The directory with Chrome build artifacts to '
571 'deploy from. Typically of format '
572 '<chrome_root>/out/Debug. When this option is used, '
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700573 'the GN_ARGS environment variable must be set.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400574 parser.add_argument('--target-dir', type='path',
575 default=None,
576 help='Target directory on device to deploy Chrome into.')
577 parser.add_argument('-g', '--gs-path', type='gs_path',
578 help='GS path that contains the chrome to deploy.')
Adrian Eldera2c548a2017-11-07 19:01:29 -0500579 parser.add_argument('--private-key', type='path', default=None,
580 help='An ssh private key to use when deploying to '
581 'a CrOS device.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400582 parser.add_argument('--nostartui', action='store_false', dest='startui',
583 default=True,
584 help="Don't restart the ui daemon after deployment.")
585 parser.add_argument('--nostrip', action='store_false', dest='dostrip',
586 default=True,
587 help="Don't strip binaries during deployment. Warning: "
588 'the resulting binaries will be very large!')
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700589 parser.add_argument(
590 '-d', '--device',
591 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
592 help='Device hostname or IP in the format hostname[:port].')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400593 parser.add_argument('--mount-dir', type='path', default=None,
594 help='Deploy Chrome in target directory and bind it '
595 'to the directory specified by this flag.'
596 'Any existing mount on this directory will be '
597 'umounted first.')
598 parser.add_argument('--mount', action='store_true', default=False,
599 help='Deploy Chrome to default target directory and bind '
600 'it to the default mount directory.'
601 'Any existing mount on this directory will be '
602 'umounted first.')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700603 parser.add_argument('--noremove-rootfs-verification', action='store_true',
604 default=False, help='Never remove rootfs verification.')
Ben Pastenee484b342020-06-30 18:29:27 -0700605 parser.add_argument('--deploy-test-binaries', action='store_true',
606 default=False,
607 help='Also deploy any test binaries to %s. Useful for '
608 'running any Tast tests that execute these '
609 'binaries.' % _CHROME_TEST_BIN_DIR)
Erik Chen75a2f492020-08-06 19:15:11 -0700610 parser.add_argument('--lacros', action='store_true', default=False,
611 help='Deploys lacros-chrome rather than ash-chrome.')
Yuke Liao24fc60c2020-12-26 22:16:49 -0800612 parser.add_argument('--skip-modifying-config-file', action='store_false',
613 dest='modify_config_file',
614 help='By default, deploying lacros-chrome modifies the '
615 '/etc/chrome_dev.conf file, which interferes with '
616 'automated testing, and this argument disables it.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700617
Mike Frysingerc3061a62015-06-04 04:16:18 -0400618 group = parser.add_argument_group('Advanced Options')
619 group.add_argument('-l', '--local-pkg-path', type='path',
620 help='Path to local chrome prebuilt package to deploy.')
621 group.add_argument('--sloppy', action='store_true', default=False,
622 help='Ignore when mandatory artifacts are missing.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700623 group.add_argument('--staging-flags', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400624 help=('Extra flags to control staging. Valid flags are - '
625 '%s' % ', '.join(chrome_util.STAGING_FLAGS)))
Steven Bennetts46a84c32016-08-12 15:20:46 -0700626 # TODO(stevenjb): Remove --strict entirely once removed from the ebuild.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400627 group.add_argument('--strict', action='store_true', default=False,
Steven Bennetts46a84c32016-08-12 15:20:46 -0700628 help='Deprecated. Default behavior is "strict". Use '
629 '--sloppy to omit warnings for missing optional '
630 'files.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400631 group.add_argument('--strip-flags', default=None,
632 help="Flags to call the 'strip' binutil tool with. "
Mike Frysinger80de5012019-08-01 14:10:53 -0400633 'Overrides the default arguments.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400634 group.add_argument('--ping', action='store_true', default=False,
635 help='Ping the device before connection attempt.')
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700636 group.add_argument('--process-timeout', type=int,
637 default=KILL_PROC_MAX_WAIT,
638 help='Timeout for process shutdown.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700639
Mike Frysingerc3061a62015-06-04 04:16:18 -0400640 group = parser.add_argument_group(
641 'Metadata Overrides (Advanced)',
642 description='Provide all of these overrides in order to remove '
643 'dependencies on metadata.json existence.')
644 group.add_argument('--target-tc', action='store', default=None,
645 help='Override target toolchain name, e.g. '
646 'x86_64-cros-linux-gnu')
647 group.add_argument('--toolchain-url', action='store', default=None,
648 help='Override toolchain url format pattern, e.g. '
649 '2014/04/%%(target)s-2014.04.23.220740.tar.xz')
Aviv Keshet1c986f32014-04-24 13:20:49 -0700650
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700651 # DEPRECATED: --gyp-defines is ignored, but retained for backwards
652 # compatibility. TODO(stevenjb): Remove once eliminated from the ebuild.
653 parser.add_argument('--gyp-defines', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400654 help=argparse.SUPPRESS)
Steven Bennetts368c3e52016-09-23 13:05:21 -0700655
656 # GN_ARGS (args.gn) used to build Chrome. Influences which files are staged
657 # when --build-dir is set. Defaults to reading from the GN_ARGS env variable.
658 # CURRENLY IGNORED, ADDED FOR FORWARD COMPATABILITY.
659 parser.add_argument('--gn-args', default=None, type=ValidateGnArgs,
660 help=argparse.SUPPRESS)
661
Ryan Cuia56a71e2012-10-18 18:40:35 -0700662 # Path of an empty directory to stage chrome artifacts to. Defaults to a
663 # temporary directory that is removed when the script finishes. If the path
664 # is specified, then it will not be removed.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400665 parser.add_argument('--staging-dir', type='path', default=None,
666 help=argparse.SUPPRESS)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700667 # Only prepare the staging directory, and skip deploying to the device.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400668 parser.add_argument('--staging-only', action='store_true', default=False,
669 help=argparse.SUPPRESS)
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700670 # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
671 # is used as-is, and not normalized. Used by the Chrome ebuild to skip
672 # fetching the SDK toolchain.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400673 parser.add_argument('--strip-bin', default=None, help=argparse.SUPPRESS)
Satoru Takabayashif2893002017-06-20 14:52:48 +0900674 parser.add_argument('--compress', action='store', default='auto',
675 choices=('always', 'never', 'auto'),
676 help='Choose the data compression behavior. Default '
677 'is set to "auto", that disables compression if '
678 'the target device has a gigabit ethernet port.')
Ryan Cuie535b172012-10-19 18:25:03 -0700679 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700680
Ryan Cuie535b172012-10-19 18:25:03 -0700681
682def _ParseCommandLine(argv):
683 """Parse args, and run environment-independent checks."""
684 parser = _CreateParser()
Mike Frysingerc3061a62015-06-04 04:16:18 -0400685 options = parser.parse_args(argv)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700686
Ryan Cuia56a71e2012-10-18 18:40:35 -0700687 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
688 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800689 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700690 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
691 parser.error('Cannot specify both --build_dir and '
692 '--gs-path/--local-pkg-patch')
Erik Chen75a2f492020-08-06 19:15:11 -0700693 if options.lacros:
694 if options.board:
695 parser.error('--board is not supported with --lacros')
696 # The stripping implemented in this file rely on the cros-chrome-sdk, which
697 # is inappropriate for Lacros. Lacros stripping is currently not
698 # implemented.
699 if options.dostrip:
700 parser.error('--lacros requires --nostrip')
701 if options.mount_dir or options.mount:
702 parser.error('--lacros does not support --mount or --mount-dir')
703 if options.deploy_test_binaries:
704 parser.error('--lacros does not support --deploy-test-binaries')
705 if options.local_pkg_path:
706 parser.error('--lacros does not support --local-pkg-path')
707 else:
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +0900708 if not options.board and options.build_dir:
709 match = re.search(r'out_([^/]+)/Release$', options.build_dir)
710 if match:
711 options.board = match.group(1)
712 logging.info('--board is set to %s', options.board)
Erik Chen75a2f492020-08-06 19:15:11 -0700713 if not options.board:
714 parser.error('--board is required')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700715 if options.gs_path and options.local_pkg_path:
716 parser.error('Cannot specify both --gs-path and --local-pkg-path')
Ben Pasteneb44d8e72021-02-19 13:56:43 -0800717 if not (options.staging_only or options.device):
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700718 parser.error('Need to specify --device')
Steven Bennetts46a84c32016-08-12 15:20:46 -0700719 if options.staging_flags and not options.build_dir:
720 parser.error('--staging-flags require --build-dir to be set.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700721
Steven Bennetts46a84c32016-08-12 15:20:46 -0700722 if options.strict:
723 logging.warning('--strict is deprecated.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700724 if options.gyp_defines:
725 logging.warning('--gyp-defines is deprecated.')
Thiago Goncales12793312013-05-23 11:26:17 -0700726
727 if options.mount or options.mount_dir:
728 if not options.target_dir:
729 options.target_dir = _CHROME_DIR_MOUNT
730 else:
731 if not options.target_dir:
Erik Chen75a2f492020-08-06 19:15:11 -0700732 options.target_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Thiago Goncales12793312013-05-23 11:26:17 -0700733
734 if options.mount and not options.mount_dir:
735 options.mount_dir = _CHROME_DIR
736
Mike Frysingerc3061a62015-06-04 04:16:18 -0400737 return options
Ryan Cui3045c5d2012-07-13 18:00:33 -0700738
739
Mike Frysingerc3061a62015-06-04 04:16:18 -0400740def _PostParseCheck(options):
Steve Funge984a532013-11-25 17:09:25 -0800741 """Perform some usage validation (after we've parsed the arguments).
Ryan Cui3045c5d2012-07-13 18:00:33 -0700742
743 Args:
Mike Frysinger5fe3f222016-09-01 00:14:16 -0400744 options: The options object returned by the cli parser.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700745 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700746 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
747 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
748
Steven Bennetts368c3e52016-09-23 13:05:21 -0700749 if not options.gn_args:
750 gn_env = os.getenv('GN_ARGS')
751 if gn_env is not None:
752 options.gn_args = gn_helpers.FromGNArgs(gn_env)
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800753 logging.debug('GN_ARGS taken from environment: %s', options.gn_args)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700754
Steven Bennetts60600462016-05-12 10:40:20 -0700755 if not options.staging_flags:
756 use_env = os.getenv('USE')
757 if use_env is not None:
758 options.staging_flags = ' '.join(set(use_env.split()).intersection(
759 chrome_util.STAGING_FLAGS))
760 logging.info('Staging flags taken from USE in environment: %s',
761 options.staging_flags)
762
Ryan Cuia56a71e2012-10-18 18:40:35 -0700763
Ryan Cui504db722013-01-22 11:48:01 -0800764def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700765 """Get the chrome prebuilt tarball from GS.
766
Mike Frysinger02e1e072013-11-10 22:11:34 -0500767 Returns:
768 Path to the fetched chrome tarball.
Ryan Cuia56a71e2012-10-18 18:40:35 -0700769 """
David James9374aac2013-10-08 16:00:17 -0700770 gs_ctx = gs.GSContext(cache_dir=cache_dir, init_boto=True)
Mike Frysinger7ad4fd62014-02-11 16:53:56 -0500771 files = gs_ctx.LS(gs_path)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800772 files = [found for found in files if
773 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
774 if not files:
775 raise Exception('No chrome package found at %s' % gs_path)
776 elif len(files) > 1:
777 # - Users should provide us with a direct link to either a stripped or
778 # unstripped chrome package.
779 # - In the case of being provided with an archive directory, where both
780 # stripped and unstripped chrome available, use the stripped chrome
781 # package.
782 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
783 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
784 files = [f for f in files if not 'unstripped' in f]
785 assert len(files) == 1
786 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800787
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800788 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800789 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800790 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
791 chrome_path = os.path.join(tempdir, filename)
792 assert os.path.exists(chrome_path)
793 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700794
795
Ryan Cuif890a3e2013-03-07 18:57:06 -0800796@contextlib.contextmanager
797def _StripBinContext(options):
798 if not options.dostrip:
799 yield None
800 elif options.strip_bin:
801 yield options.strip_bin
802 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800803 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800804 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
Aviv Keshet1c986f32014-04-24 13:20:49 -0700805 with sdk.Prepare(components=components, target_tc=options.target_tc,
806 toolchain_url=options.toolchain_url) as ctx:
Ryan Cui686ec052013-02-12 16:39:41 -0800807 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
808 constants.CHROME_ENV_FILE)
809 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
810 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
811 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800812 yield strip_bin
813
814
Steve Funge984a532013-11-25 17:09:25 -0800815def _PrepareStagingDir(options, tempdir, staging_dir, copy_paths=None,
Erik Chen75a2f492020-08-06 19:15:11 -0700816 chrome_dir=None):
Ryan Cuif890a3e2013-03-07 18:57:06 -0800817 """Place the necessary files in the staging directory.
818
819 The staging directory is the directory used to rsync the build artifacts over
820 to the device. Only the necessary Chrome build artifacts are put into the
821 staging directory.
822 """
Erik Chen75a2f492020-08-06 19:15:11 -0700823 if chrome_dir is None:
824 chrome_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Ryan Cui5866be02013-03-18 14:12:00 -0700825 osutils.SafeMakedirs(staging_dir)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400826 os.chmod(staging_dir, 0o755)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800827 if options.build_dir:
828 with _StripBinContext(options) as strip_bin:
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700829 strip_flags = (None if options.strip_flags is None else
830 shlex.split(options.strip_flags))
Ryan Cui686ec052013-02-12 16:39:41 -0800831 chrome_util.StageChromeFromBuildDir(
Steven Bennetts46a84c32016-08-12 15:20:46 -0700832 staging_dir, options.build_dir, strip_bin,
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700833 sloppy=options.sloppy, gn_args=options.gn_args,
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700834 staging_flags=options.staging_flags,
Steve Funge984a532013-11-25 17:09:25 -0800835 strip_flags=strip_flags, copy_paths=copy_paths)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700836 else:
837 pkg_path = options.local_pkg_path
838 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800839 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
840 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700841
842 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800843 logging.info('Extracting %s...', pkg_path)
Ryan Cui5866be02013-03-18 14:12:00 -0700844 # Extract only the ./opt/google/chrome contents, directly into the staging
845 # dir, collapsing the directory hierarchy.
Steve Funge984a532013-11-25 17:09:25 -0800846 if pkg_path[-4:] == '.zip':
Mike Frysinger45602c72019-09-22 02:15:11 -0400847 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800848 ['unzip', '-X', pkg_path, _ANDROID_DIR_EXTRACT_PATH, '-d',
849 staging_dir])
850 for filename in glob.glob(os.path.join(staging_dir, 'system/chrome/*')):
851 shutil.move(filename, staging_dir)
852 osutils.RmDir(os.path.join(staging_dir, 'system'), ignore_missing=True)
853 else:
Mike Frysinger45602c72019-09-22 02:15:11 -0400854 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800855 ['tar', '--strip-components', '4', '--extract',
856 '--preserve-permissions', '--file', pkg_path, '.%s' % chrome_dir],
857 cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700858
Ryan Cui71aa8de2013-04-19 16:12:55 -0700859
Ryan Cui3045c5d2012-07-13 18:00:33 -0700860def main(argv):
Mike Frysingerc3061a62015-06-04 04:16:18 -0400861 options = _ParseCommandLine(argv)
862 _PostParseCheck(options)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700863
Aviv Keshet01a82e92017-03-02 17:39:59 -0800864 with osutils.TempDir(set_global=True) as tempdir:
865 staging_dir = options.staging_dir
866 if not staging_dir:
867 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700868
Aviv Keshet01a82e92017-03-02 17:39:59 -0800869 deploy = DeployChrome(options, tempdir, staging_dir)
870 try:
871 deploy.Perform()
872 except failures_lib.StepFailure as ex:
873 raise SystemExit(str(ex).strip())
874 deploy.Cleanup()