blob: 70466ba39b47a3f96f2e60e8f4d9cb547c68f479 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Ryan Cui3045c5d2012-07-13 18:00:33 -07002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Steve Funge984a532013-11-25 17:09:25 -08006"""Script that deploys a Chrome build to a device.
Ryan Cuia56a71e2012-10-18 18:40:35 -07007
8The script supports deploying Chrome from these sources:
9
101. A local build output directory, such as chromium/src/out/[Debug|Release].
112. A Chrome tarball uploaded by a trybot/official-builder to GoogleStorage.
123. A Chrome tarball existing locally.
13
14The script copies the necessary contents of the source location (tarball or
15build directory) and rsyncs the contents of the staging directory onto your
16device's rootfs.
17"""
Ryan Cui3045c5d2012-07-13 18:00:33 -070018
Mike Frysinger383367e2014-09-16 15:06:17 -040019from __future__ import print_function
20
Mike Frysingerc3061a62015-06-04 04:16:18 -040021import argparse
Ryan Cui7193a7e2013-04-26 14:15:19 -070022import collections
Ryan Cuif890a3e2013-03-07 18:57:06 -080023import contextlib
Ryan Cui7193a7e2013-04-26 14:15:19 -070024import functools
Steve Funge984a532013-11-25 17:09:25 -080025import glob
David James88e6f032013-03-02 08:13:20 -080026import multiprocessing
Ryan Cui3045c5d2012-07-13 18:00:33 -070027import os
Ryan Cui5b7c2ed2013-03-18 18:45:46 -070028import shlex
Steve Funge984a532013-11-25 17:09:25 -080029import shutil
Mike Frysinger6165cdc2020-02-21 02:38:07 -050030import sys
Ryan Cui3045c5d2012-07-13 18:00:33 -070031import time
Ryan Cui3045c5d2012-07-13 18:00:33 -070032
Aviv Keshetb7519e12016-10-04 00:50:00 -070033from chromite.lib import constants
34from chromite.lib import failures_lib
David Pursellcfd58872015-03-19 09:15:48 -070035from chromite.cli.cros import cros_chrome_sdk
Ryan Cuia56a71e2012-10-18 18:40:35 -070036from chromite.lib import chrome_util
Ryan Cuie535b172012-10-19 18:25:03 -070037from chromite.lib import commandline
Ralph Nathan91874ca2015-03-19 13:29:41 -070038from chromite.lib import cros_build_lib
39from chromite.lib import cros_logging as logging
Ryan Cui777ff422012-12-07 13:12:54 -080040from chromite.lib import gs
Ryan Cui3045c5d2012-07-13 18:00:33 -070041from chromite.lib import osutils
David James88e6f032013-03-02 08:13:20 -080042from chromite.lib import parallel
Ryan Cui3045c5d2012-07-13 18:00:33 -070043from chromite.lib import remote_access as remote
Brian Sheedy86f12342020-10-29 15:30:02 -070044from chromite.lib import retry_util
David James3432acd2013-11-27 10:02:18 -080045from chromite.lib import timeout_util
Steven Bennetts368c3e52016-09-23 13:05:21 -070046from gn_helpers import gn_helpers
Ryan Cui3045c5d2012-07-13 18:00:33 -070047
48
Mike Frysinger6165cdc2020-02-21 02:38:07 -050049assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
50
51
Ryan Cui3045c5d2012-07-13 18:00:33 -070052KERNEL_A_PARTITION = 2
53KERNEL_B_PARTITION = 4
54
55KILL_PROC_MAX_WAIT = 10
56POST_KILL_WAIT = 2
57
David Haddock3151d912017-10-24 03:50:32 +000058MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Anushruth8d797672019-10-17 12:22:31 -070059LSOF_COMMAND_CHROME = 'lsof %s/chrome'
60LSOF_COMMAND = 'lsof %s'
David Haddock3151d912017-10-24 03:50:32 +000061DBUS_RELOAD_COMMAND = 'killall -HUP dbus-daemon'
Ryan Cui3045c5d2012-07-13 18:00:33 -070062
Steve Funge984a532013-11-25 17:09:25 -080063_ANDROID_DIR = '/system/chrome'
64_ANDROID_DIR_EXTRACT_PATH = 'system/chrome/*'
65
David James2cb34002013-03-01 18:42:40 -080066_CHROME_DIR = '/opt/google/chrome'
Thiago Goncales12793312013-05-23 11:26:17 -070067_CHROME_DIR_MOUNT = '/mnt/stateful_partition/deploy_rootfs/opt/google/chrome'
Ben Pastenee484b342020-06-30 18:29:27 -070068_CHROME_TEST_BIN_DIR = '/usr/local/libexec/chrome-binary-tests'
David James2cb34002013-03-01 18:42:40 -080069
David Haddock3151d912017-10-24 03:50:32 +000070_UMOUNT_DIR_IF_MOUNTPOINT_CMD = (
71 'if mountpoint -q %(dir)s; then umount %(dir)s; fi')
72_BIND_TO_FINAL_DIR_CMD = 'mount --rbind %s %s'
73_SET_MOUNT_FLAGS_CMD = 'mount -o remount,exec,suid %s'
Anushruth8d797672019-10-17 12:22:31 -070074_MKDIR_P_CMD = 'mkdir -p --mode 0775 %s'
Ben Pastenee484b342020-06-30 18:29:27 -070075_FIND_TEST_BIN_CMD = 'find %s -maxdepth 1 -executable -type f' % (
76 _CHROME_TEST_BIN_DIR)
David Haddock3151d912017-10-24 03:50:32 +000077
78DF_COMMAND = 'df -k %s'
79
ChromeOS Developer536f59a2020-08-10 14:39:27 -070080LACROS_DIR = '/usr/local/lacros-chrome'
Erik Chen75a2f492020-08-06 19:15:11 -070081_CONF_FILE = '/etc/chrome_dev.conf'
82_KILL_LACROS_CHROME_CMD = 'pkill -f %(lacros_dir)s/chrome'
83MODIFIED_CONF_FILE = f'modified {_CONF_FILE}'
84
85# This command checks if "--enable-features=LacrosSupport" is present in
86# /etc/chrome_dev.conf. If it is not, then it is added.
87# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
88# to modify chrome_dev.conf. Either revisit this policy or find another
89# mechanism to pass configuration to ash-chrome.
90ENABLE_LACROS_VIA_CONF_COMMAND = f"""
91 if ! grep -q "^--enable-features=LacrosSupport$" {_CONF_FILE}; then
92 echo "--enable-features=LacrosSupport" >> {_CONF_FILE};
93 echo {MODIFIED_CONF_FILE};
94 fi
95"""
96
97# This command checks if "--lacros-chrome-path=" is present with the right value
98# in /etc/chrome_dev.conf. If it is not, then all previous instances are removed
99# and the new one is added.
100# TODO(https://crbug.com/1112493): Automated scripts are currently not allowed
101# to modify chrome_dev.conf. Either revisit this policy or find another
102# mechanism to pass configuration to ash-chrome.
103_SET_LACROS_PATH_VIA_CONF_COMMAND = """
104 if ! grep -q "^--lacros-chrome-path=%(lacros_path)s$" %(conf_file)s; then
105 sed 's/--lacros-chrome-path/#--lacros-chrome-path/' %(conf_file)s;
106 echo "--lacros-chrome-path=%(lacros_path)s" >> %(conf_file)s;
107 echo %(modified_conf_file)s;
108 fi
109"""
Mike Frysingere65f3752014-12-08 00:46:39 -0500110
Ryan Cui3045c5d2012-07-13 18:00:33 -0700111def _UrlBaseName(url):
112 """Return the last component of the URL."""
113 return url.rstrip('/').rpartition('/')[-1]
114
115
Yu-Ju Hongc54d3342014-05-14 12:42:06 -0700116class DeployFailure(failures_lib.StepFailure):
David James88e6f032013-03-02 08:13:20 -0800117 """Raised whenever the deploy fails."""
118
119
Ryan Cui7193a7e2013-04-26 14:15:19 -0700120DeviceInfo = collections.namedtuple(
121 'DeviceInfo', ['target_dir_size', 'target_fs_free'])
122
123
Ryan Cui3045c5d2012-07-13 18:00:33 -0700124class DeployChrome(object):
125 """Wraps the core deployment functionality."""
Mike Frysingere65f3752014-12-08 00:46:39 -0500126
Ryan Cuia56a71e2012-10-18 18:40:35 -0700127 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -0700128 """Initialize the class.
129
Mike Frysinger02e1e072013-11-10 22:11:34 -0500130 Args:
Mike Frysingerc3061a62015-06-04 04:16:18 -0400131 options: options object.
Ryan Cuie535b172012-10-19 18:25:03 -0700132 tempdir: Scratch space for the class. Caller has responsibility to clean
133 it up.
Steve Funge984a532013-11-25 17:09:25 -0800134 staging_dir: Directory to stage the files to.
Ryan Cuie535b172012-10-19 18:25:03 -0700135 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700136 self.tempdir = tempdir
137 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -0700138 self.staging_dir = staging_dir
Robert Flack1dc7ea82014-11-26 13:50:24 -0500139 if not self.options.staging_only:
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700140 if options.device:
141 hostname = options.device.hostname
142 port = options.device.port
143 else:
144 hostname = options.to
145 port = options.port
146 self.device = remote.ChromiumOSDevice(hostname, port=port,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700147 ping=options.ping,
148 private_key=options.private_key,
149 include_dev_paths=False)
Steven Bennettsca73efa2018-07-10 13:36:56 -0700150 self._root_dir_is_still_readonly = multiprocessing.Event()
Steven Bennetts5a7c72d2016-10-17 20:04:46 +0000151
Erik Chen75a2f492020-08-06 19:15:11 -0700152 self._deployment_name = 'lacros' if options.lacros else 'chrome'
153 self.copy_paths = chrome_util.GetCopyPaths(self._deployment_name)
154
155 self.chrome_dir = LACROS_DIR if self.options.lacros else _CHROME_DIR
156
157 # Whether UI was stopped during setup.
158 self._stopped_ui = False
Steve Funge984a532013-11-25 17:09:25 -0800159
Ryan Cui7193a7e2013-04-26 14:15:19 -0700160 def _GetRemoteMountFree(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400161 result = self.device.run(DF_COMMAND % remote_dir)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700162 line = result.output.splitlines()[1]
Steve Funge984a532013-11-25 17:09:25 -0800163 value = line.split()[3]
164 multipliers = {
165 'G': 1024 * 1024 * 1024,
166 'M': 1024 * 1024,
167 'K': 1024,
168 }
169 return int(value.rstrip('GMK')) * multipliers.get(value[-1], 1)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700170
171 def _GetRemoteDirSize(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400172 result = self.device.run('du -ks %s' % remote_dir,
173 capture_output=True, encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700174 return int(result.output.split()[0])
175
176 def _GetStagingDirSize(self):
Mike Frysinger45602c72019-09-22 02:15:11 -0400177 result = cros_build_lib.dbg_run(['du', '-ks', self.staging_dir],
Mike Frysinger0282d222019-12-17 17:15:48 -0500178 stdout=True, capture_output=True,
Mike Frysingerb88d5fb2019-10-23 00:38:45 -0400179 encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700180 return int(result.output.split()[0])
181
Ryan Cui3045c5d2012-07-13 18:00:33 -0700182 def _ChromeFileInUse(self):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400183 result = self.device.run(LSOF_COMMAND_CHROME % (self.options.target_dir,),
184 check=False, capture_output=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700185 return result.returncode == 0
186
Justin TerAvestfac210e2017-04-13 11:39:00 -0600187 def _Reboot(self):
188 # A reboot in developer mode takes a while (and has delays), so the user
189 # will have time to read and act on the USB boot instructions below.
190 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
191 self.device.Reboot()
192
Ryan Cui3045c5d2012-07-13 18:00:33 -0700193 def _DisableRootfsVerification(self):
194 if not self.options.force:
195 logging.error('Detected that the device has rootfs verification enabled.')
196 logging.info('This script can automatically remove the rootfs '
197 'verification, which requires that it reboot the device.')
198 logging.info('Make sure the device is in developer mode!')
199 logging.info('Skip this prompt by specifying --force.')
Ben Chan37d64e52018-10-05 15:35:02 -0700200 if not cros_build_lib.BooleanPrompt('Remove rootfs verification?', False):
Steven Bennettsca73efa2018-07-10 13:36:56 -0700201 return False
Ryan Cui3045c5d2012-07-13 18:00:33 -0700202
203 logging.info('Removing rootfs verification from %s', self.options.to)
204 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
205 # Use --force to bypass the checks.
David Haddock3151d912017-10-24 03:50:32 +0000206 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
207 '--remove_rootfs_verification --force')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700208 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400209 self.device.run(cmd % partition, check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700210
Justin TerAvestfac210e2017-04-13 11:39:00 -0600211 self._Reboot()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700212
David James88e6f032013-03-02 08:13:20 -0800213 # Now that the machine has been rebooted, we need to kill Chrome again.
Erik Chen75a2f492020-08-06 19:15:11 -0700214 self._KillAshChromeIfNeeded()
David James88e6f032013-03-02 08:13:20 -0800215
216 # Make sure the rootfs is writable now.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500217 self._MountRootfsAsWritable(check=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700218
Steven Bennettsca73efa2018-07-10 13:36:56 -0700219 return True
220
Ryan Cui3045c5d2012-07-13 18:00:33 -0700221 def _CheckUiJobStarted(self):
222 # status output is in the format:
223 # <job_name> <status> ['process' <pid>].
224 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800225 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400226 result = self.device.run('status ui', capture_output=True,
227 encoding='utf-8')
Ryan Cuif2d1a582013-02-19 14:08:13 -0800228 except cros_build_lib.RunCommandError as e:
229 if 'Unknown job' in e.result.error:
230 return False
231 else:
232 raise e
233
Ryan Cui3045c5d2012-07-13 18:00:33 -0700234 return result.output.split()[1].split('/')[0] == 'start'
235
Erik Chen75a2f492020-08-06 19:15:11 -0700236 def _KillLacrosChrome(self):
237 """This method kills lacros-chrome on the device, if it's running."""
238 self.device.run(_KILL_LACROS_CHROME_CMD %
239 {'lacros_dir': self.options.target_dir}, check=False)
240
241 def _KillAshChromeIfNeeded(self):
242 """This method kills ash-chrome on the device, if it's running.
243
244 This method calls 'stop ui', and then also manually pkills both ash-chrome
245 and the session manager.
246 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700247 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800248 logging.info('Shutting down Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400249 self.device.run('stop ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700250
251 # Developers sometimes run session_manager manually, in which case we'll
252 # need to help shut the chrome processes down.
253 try:
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700254 with timeout_util.Timeout(self.options.process_timeout):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700255 while self._ChromeFileInUse():
256 logging.warning('The chrome binary on the device is in use.')
257 logging.warning('Killing chrome and session_manager processes...\n')
258
Mike Frysinger3459bf52020-03-31 00:52:11 -0400259 self.device.run("pkill 'chrome|session_manager'", check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700260 # Wait for processes to actually terminate
261 time.sleep(POST_KILL_WAIT)
262 logging.info('Rechecking the chrome binary...')
David James3432acd2013-11-27 10:02:18 -0800263 except timeout_util.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800264 msg = ('Could not kill processes after %s seconds. Please exit any '
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700265 'running chrome processes and try again.'
266 % self.options.process_timeout)
David James88e6f032013-03-02 08:13:20 -0800267 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700268
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500269 def _MountRootfsAsWritable(self, check=False):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800270 """Mounts the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700271
Steven Bennettsca73efa2018-07-10 13:36:56 -0700272 If the command fails and the root dir is not writable then this function
273 sets self._root_dir_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700274
Mike Frysinger02e1e072013-11-10 22:11:34 -0500275 Args:
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500276 check: See remote.RemoteAccess.RemoteSh for details.
David James88e6f032013-03-02 08:13:20 -0800277 """
Yu-Ju Hong7e116ac2014-01-03 17:08:26 -0800278 # TODO: Should migrate to use the remount functions in remote_access.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400279 result = self.device.run(MOUNT_RW_COMMAND, check=check,
280 capture_output=True, encoding='utf-8')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700281 if result.returncode and not self.device.IsDirWritable('/'):
282 self._root_dir_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700283
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800284 def _EnsureTargetDir(self):
285 """Ensures that the target directory exists on the remote device."""
286 target_dir = self.options.target_dir
287 # Any valid /opt directory should already exist so avoid the remote call.
288 if os.path.commonprefix([target_dir, '/opt']) == '/opt':
289 return
Mike Frysinger3459bf52020-03-31 00:52:11 -0400290 self.device.run(['mkdir', '-p', '--mode', '0775', target_dir])
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800291
Ryan Cui7193a7e2013-04-26 14:15:19 -0700292 def _GetDeviceInfo(self):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800293 """Returns the disk space used and available for the target diectory."""
Ryan Cui7193a7e2013-04-26 14:15:19 -0700294 steps = [
295 functools.partial(self._GetRemoteDirSize, self.options.target_dir),
296 functools.partial(self._GetRemoteMountFree, self.options.target_dir)
297 ]
298 return_values = parallel.RunParallelSteps(steps, return_values=True)
299 return DeviceInfo(*return_values)
300
301 def _CheckDeviceFreeSpace(self, device_info):
302 """See if target device has enough space for Chrome.
303
Mike Frysinger02e1e072013-11-10 22:11:34 -0500304 Args:
Ryan Cui7193a7e2013-04-26 14:15:19 -0700305 device_info: A DeviceInfo named tuple.
306 """
307 effective_free = device_info.target_dir_size + device_info.target_fs_free
308 staging_size = self._GetStagingDirSize()
309 if effective_free < staging_size:
Daniel Erat1ae46382014-08-14 10:23:39 -0700310 raise DeployFailure(
311 'Not enough free space on the device. Required: %s MiB, '
Mike Frysinger93e8ffa2019-07-03 20:24:18 -0400312 'actual: %s MiB.' % (staging_size // 1024, effective_free // 1024))
Ryan Cui7193a7e2013-04-26 14:15:19 -0700313 if device_info.target_fs_free < (100 * 1024):
314 logging.warning('The device has less than 100MB free. deploy_chrome may '
315 'hang during the transfer.')
316
Satoru Takabayashif2893002017-06-20 14:52:48 +0900317 def _ShouldUseCompression(self):
318 """Checks if compression should be used for rsync."""
319 if self.options.compress == 'always':
320 return True
321 elif self.options.compress == 'never':
322 return False
323 elif self.options.compress == 'auto':
324 return not self.device.HasGigabitEthernet()
325
Ryan Cui3045c5d2012-07-13 18:00:33 -0700326 def _Deploy(self):
Erik Chen75a2f492020-08-06 19:15:11 -0700327 logging.info('Copying %s to %s on device...', self._deployment_name,
328 self.options.target_dir)
Ilja H. Friedel0ab63e12017-03-28 13:29:48 -0700329 # CopyToDevice will fall back to scp if rsync is corrupted on stateful.
330 # This does not work for deploy.
Satoru Takabayashiab380bc2015-01-15 16:00:41 +0900331 if not self.device.HasRsync():
332 raise DeployFailure(
David Pursellcfd58872015-03-19 09:15:48 -0700333 'rsync is not found on the device.\n'
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500334 'Run dev_install on the device to get rsync installed.')
Robert Flack1dc7ea82014-11-26 13:50:24 -0500335 self.device.CopyToDevice('%s/' % os.path.abspath(self.staging_dir),
336 self.options.target_dir,
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700337 mode='rsync', inplace=True,
Satoru Takabayashif2893002017-06-20 14:52:48 +0900338 compress=self._ShouldUseCompression(),
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700339 debug_level=logging.INFO,
Robert Flack1dc7ea82014-11-26 13:50:24 -0500340 verbose=self.options.verbose)
Ben Pastene5f03b052019-08-12 18:03:24 -0700341
342 # Set the security context on the default Chrome dir if that's where it's
343 # getting deployed, and only on SELinux supported devices.
Erik Chen75a2f492020-08-06 19:15:11 -0700344 if not self.options.lacros and self.device.IsSELinuxAvailable() and (
Ben Pastene5f03b052019-08-12 18:03:24 -0700345 _CHROME_DIR in (self.options.target_dir, self.options.mount_dir)):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400346 self.device.run(['restorecon', '-R', _CHROME_DIR])
Steve Funge984a532013-11-25 17:09:25 -0800347
348 for p in self.copy_paths:
Steve Funge984a532013-11-25 17:09:25 -0800349 if p.mode:
350 # Set mode if necessary.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400351 self.device.run('chmod %o %s/%s' % (
David Haddock3151d912017-10-24 03:50:32 +0000352 p.mode, self.options.target_dir, p.src if not p.dest else p.dest))
Steve Funge984a532013-11-25 17:09:25 -0800353
Erik Chen75a2f492020-08-06 19:15:11 -0700354 if self.options.lacros:
355 self.device.run(['chown', '-R', 'chronos:chronos',
356 self.options.target_dir])
357
Daniel Erat0fce5bf2017-09-28 17:29:23 -0700358 # Send SIGHUP to dbus-daemon to tell it to reload its configs. This won't
359 # pick up major changes (bus type, logging, etc.), but all we care about is
360 # getting the latest policy from /opt/google/chrome/dbus so that Chrome will
361 # be authorized to take ownership of its service names.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400362 self.device.run(DBUS_RELOAD_COMMAND, check=False)
Justin TerAvestfac210e2017-04-13 11:39:00 -0600363
Erik Chen75a2f492020-08-06 19:15:11 -0700364 if self.options.startui and self._stopped_ui:
Steve Funge984a532013-11-25 17:09:25 -0800365 logging.info('Starting UI...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400366 self.device.run('start ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700367
Ben Pastenee484b342020-06-30 18:29:27 -0700368 def _DeployTestBinaries(self):
369 """Deploys any local test binary to _CHROME_TEST_BIN_DIR on the device.
370
371 There could be several binaries located in the local build dir, so compare
372 what's already present on the device in _CHROME_TEST_BIN_DIR , and copy
373 over any that we also built ourselves.
374 """
375 r = self.device.run(_FIND_TEST_BIN_CMD, check=False)
376 if r.returncode != 0:
377 raise DeployFailure('Unable to ls contents of %s' % _CHROME_TEST_BIN_DIR)
378 binaries_to_copy = []
379 for f in r.output.splitlines():
380 binaries_to_copy.append(
381 chrome_util.Path(os.path.basename(f), exe=True, optional=True))
382
383 staging_dir = os.path.join(
384 self.tempdir, os.path.basename(_CHROME_TEST_BIN_DIR))
385 _PrepareStagingDir(self.options, self.tempdir, staging_dir,
386 copy_paths=binaries_to_copy)
Brian Sheedy86f12342020-10-29 15:30:02 -0700387 # Deploying can occasionally run into issues with rsync getting a broken
388 # pipe, so retry several times. See crbug.com/1141618 for more
389 # information.
390 retry_util.RetryException(
391 None, 3, self.device.CopyToDevice, staging_dir,
392 os.path.dirname(_CHROME_TEST_BIN_DIR), mode='rsync')
Ben Pastenee484b342020-06-30 18:29:27 -0700393
David James88e6f032013-03-02 08:13:20 -0800394 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700395 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800396 logging.info('Testing connection to the device...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400397 self.device.run('true')
David James88e6f032013-03-02 08:13:20 -0800398 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700399 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800400 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700401
Avery Musbach3edff0e2020-03-27 13:35:53 -0700402 def _CheckBoard(self):
403 """Check that the Chrome build is targeted for the device board."""
404 if self.options.board == self.device.board:
405 return
406 logging.warning('Device board is %s whereas target board is %s.',
407 self.device.board, self.options.board)
408 if self.options.force:
409 return
410 if not cros_build_lib.BooleanPrompt('Continue despite board mismatch?',
411 False):
412 raise DeployFailure('Aborted.')
413
Steve Funge984a532013-11-25 17:09:25 -0800414 def _CheckDeployType(self):
Steve Fung63d705d2014-03-16 03:14:03 -0700415 if self.options.build_dir:
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700416 def BinaryExists(filename):
417 """Checks if the passed-in file is present in the build directory."""
418 return os.path.exists(os.path.join(self.options.build_dir, filename))
419
Erik Chen75a2f492020-08-06 19:15:11 -0700420 # In the future, lacros-chrome and ash-chrome will likely be named
421 # something other than 'chrome' to avoid confusion.
Daniel Erat9813f0e2014-11-12 11:00:28 -0700422 # Handle non-Chrome deployments.
423 if not BinaryExists('chrome'):
Daniel Eratf53bd3a2016-12-02 11:28:36 -0700424 if BinaryExists('app_shell'):
Daniel Erat9813f0e2014-11-12 11:00:28 -0700425 self.copy_paths = chrome_util.GetCopyPaths('app_shell')
426
David James88e6f032013-03-02 08:13:20 -0800427 def _PrepareStagingDir(self):
Steve Funge984a532013-11-25 17:09:25 -0800428 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir,
429 self.copy_paths, self.chrome_dir)
David James88e6f032013-03-02 08:13:20 -0800430
Thiago Goncales12793312013-05-23 11:26:17 -0700431 def _MountTarget(self):
432 logging.info('Mounting Chrome...')
433
Anushruth8d797672019-10-17 12:22:31 -0700434 # Create directory if does not exist.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400435 self.device.run(_MKDIR_P_CMD % self.options.mount_dir)
Anushruth8d797672019-10-17 12:22:31 -0700436 try:
437 # Umount the existing mount on mount_dir if present first.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400438 self.device.run(_UMOUNT_DIR_IF_MOUNTPOINT_CMD %
439 {'dir': self.options.mount_dir})
Anushruth8d797672019-10-17 12:22:31 -0700440 except cros_build_lib.RunCommandError as e:
441 logging.error('Failed to umount %s', self.options.mount_dir)
442 # If there is a failure, check if some processs is using the mount_dir.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400443 result = self.device.run(LSOF_COMMAND % (self.options.mount_dir,),
444 check=False, capture_output=True,
445 encoding='utf-8')
Anushruth8d797672019-10-17 12:22:31 -0700446 logging.error('lsof %s -->', self.options.mount_dir)
447 logging.error(result.stdout)
448 raise e
449
Mike Frysinger3459bf52020-03-31 00:52:11 -0400450 self.device.run(_BIND_TO_FINAL_DIR_CMD % (self.options.target_dir,
451 self.options.mount_dir))
Anushruth8d797672019-10-17 12:22:31 -0700452
Thiago Goncales12793312013-05-23 11:26:17 -0700453 # Chrome needs partition to have exec and suid flags set
Mike Frysinger3459bf52020-03-31 00:52:11 -0400454 self.device.run(_SET_MOUNT_FLAGS_CMD % (self.options.mount_dir,))
Robert Flack1dc7ea82014-11-26 13:50:24 -0500455
456 def Cleanup(self):
457 """Clean up RemoteDevice."""
458 if not self.options.staging_only:
459 self.device.Cleanup()
Thiago Goncales12793312013-05-23 11:26:17 -0700460
David James88e6f032013-03-02 08:13:20 -0800461 def Perform(self):
Steve Funge984a532013-11-25 17:09:25 -0800462 self._CheckDeployType()
463
David James88e6f032013-03-02 08:13:20 -0800464 # If requested, just do the staging step.
465 if self.options.staging_only:
466 self._PrepareStagingDir()
467 return 0
468
Erik Chen75a2f492020-08-06 19:15:11 -0700469 # Check that the build matches the device. Lacros-chrome skips this check as
470 # it's currently board independent. This means that it's possible to deploy
471 # a build of lacros-chrome with a mismatched architecture. We don't try to
472 # prevent this developer foot-gun.
473 if not self.options.lacros:
474 self._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700475
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800476 # Ensure that the target directory exists before running parallel steps.
477 self._EnsureTargetDir()
478
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800479 logging.info('Preparing device')
Steve Funge984a532013-11-25 17:09:25 -0800480 steps = [self._GetDeviceInfo, self._CheckConnection,
Erik Chen75a2f492020-08-06 19:15:11 -0700481 self._MountRootfsAsWritable,
Steve Funge984a532013-11-25 17:09:25 -0800482 self._PrepareStagingDir]
Erik Chen75a2f492020-08-06 19:15:11 -0700483
484 # If this is a lacros build, we only want to restart ash-chrome if
485 # necessary, which is done below.
ChromeOS Developer78345852020-08-11 23:09:48 -0700486 if not self.options.lacros:
487 self._stopped_ui = True
Erik Chen75a2f492020-08-06 19:15:11 -0700488 steps += ([self._KillLacrosChrome] if self.options.lacros else
489 [self._KillAshChromeIfNeeded])
Ryan Cui7193a7e2013-04-26 14:15:19 -0700490 ret = parallel.RunParallelSteps(steps, halt_on_error=True,
491 return_values=True)
492 self._CheckDeviceFreeSpace(ret[0])
David James88e6f032013-03-02 08:13:20 -0800493
Steven Bennettsca73efa2018-07-10 13:36:56 -0700494 # If the root dir is not writable, try disabling rootfs verification.
495 # (We always do this by default so that developers can write to
496 # /etc/chriome_dev.conf and other directories in the rootfs).
497 if self._root_dir_is_still_readonly.is_set():
498 if self.options.noremove_rootfs_verification:
499 logging.warning('Skipping disable rootfs verification.')
500 elif not self._DisableRootfsVerification():
501 logging.warning('Failed to disable rootfs verification.')
502
503 # If the target dir is still not writable (i.e. the user opted out or the
504 # command failed), abort.
505 if not self.device.IsDirWritable(self.options.target_dir):
Erik Chen75a2f492020-08-06 19:15:11 -0700506 if self.options.startui and self._stopped_ui:
Steven Bennettsca73efa2018-07-10 13:36:56 -0700507 logging.info('Restarting Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400508 self.device.run('start ui')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700509 raise DeployFailure('Target location is not writable. Aborting.')
David James88e6f032013-03-02 08:13:20 -0800510
Thiago Goncales12793312013-05-23 11:26:17 -0700511 if self.options.mount_dir is not None:
512 self._MountTarget()
513
Erik Chen75a2f492020-08-06 19:15:11 -0700514 if self.options.lacros:
515 # Update /etc/chrome_dev.conf to include appropriate flags.
516 restart_ui = False
517 result = self.device.run(ENABLE_LACROS_VIA_CONF_COMMAND, shell=True)
518 if result.stdout.strip() == MODIFIED_CONF_FILE:
519 restart_ui = True
520 result = self.device.run(_SET_LACROS_PATH_VIA_CONF_COMMAND % {
521 'conf_file': _CONF_FILE, 'lacros_path': self.options.target_dir,
522 'modified_conf_file': MODIFIED_CONF_FILE}, shell=True)
523 if result.stdout.strip() == MODIFIED_CONF_FILE:
524 restart_ui = True
525
526 if restart_ui:
527 self._KillAshChromeIfNeeded()
528
David James88e6f032013-03-02 08:13:20 -0800529 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700530 self._Deploy()
Ben Pastenee484b342020-06-30 18:29:27 -0700531 if self.options.deploy_test_binaries:
532 self._DeployTestBinaries()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700533
534
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700535def ValidateStagingFlags(value):
536 """Convert formatted string to dictionary."""
537 return chrome_util.ProcessShellFlags(value)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700538
539
Steven Bennetts368c3e52016-09-23 13:05:21 -0700540def ValidateGnArgs(value):
541 """Convert GN_ARGS-formatted string to dictionary."""
542 return gn_helpers.FromGNArgs(value)
543
544
Ryan Cuie535b172012-10-19 18:25:03 -0700545def _CreateParser():
546 """Create our custom parser."""
Mike Frysingerc3061a62015-06-04 04:16:18 -0400547 parser = commandline.ArgumentParser(description=__doc__, caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700548
Ryan Cuia56a71e2012-10-18 18:40:35 -0700549 # TODO(rcui): Have this use the UI-V2 format of having source and target
550 # device be specified as positional arguments.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400551 parser.add_argument('--force', action='store_true', default=False,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700552 help='Skip all prompts (such as the prompt for disabling '
553 'of rootfs verification). This may result in the '
554 'target machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800555 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
Mike Frysingerc3061a62015-06-04 04:16:18 -0400556 parser.add_argument('--board', default=sdk_board_env,
Mike Frysinger80de5012019-08-01 14:10:53 -0400557 help='The board the Chrome build is targeted for. When '
Mike Frysingerc3061a62015-06-04 04:16:18 -0400558 "in a 'cros chrome-sdk' shell, defaults to the SDK "
Mike Frysinger80de5012019-08-01 14:10:53 -0400559 'board.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400560 parser.add_argument('--build-dir', type='path',
561 help='The directory with Chrome build artifacts to '
562 'deploy from. Typically of format '
563 '<chrome_root>/out/Debug. When this option is used, '
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700564 'the GN_ARGS environment variable must be set.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400565 parser.add_argument('--target-dir', type='path',
566 default=None,
567 help='Target directory on device to deploy Chrome into.')
568 parser.add_argument('-g', '--gs-path', type='gs_path',
569 help='GS path that contains the chrome to deploy.')
Adrian Eldera2c548a2017-11-07 19:01:29 -0500570 parser.add_argument('--private-key', type='path', default=None,
571 help='An ssh private key to use when deploying to '
572 'a CrOS device.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400573 parser.add_argument('--nostartui', action='store_false', dest='startui',
574 default=True,
575 help="Don't restart the ui daemon after deployment.")
576 parser.add_argument('--nostrip', action='store_false', dest='dostrip',
577 default=True,
578 help="Don't strip binaries during deployment. Warning: "
579 'the resulting binaries will be very large!')
580 parser.add_argument('-p', '--port', type=int, default=remote.DEFAULT_SSH_PORT,
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700581 help='This arg is deprecated. Please use --device '
582 'instead.')
583 parser.add_argument('-t', '--to', deprecated='Use --device instead',
584 help='This arg is deprecated. Please use --device '
585 'instead.')
586 parser.add_argument(
587 '-d', '--device',
588 type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
589 help='Device hostname or IP in the format hostname[:port].')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400590 parser.add_argument('--mount-dir', type='path', default=None,
591 help='Deploy Chrome in target directory and bind it '
592 'to the directory specified by this flag.'
593 'Any existing mount on this directory will be '
594 'umounted first.')
595 parser.add_argument('--mount', action='store_true', default=False,
596 help='Deploy Chrome to default target directory and bind '
597 'it to the default mount directory.'
598 'Any existing mount on this directory will be '
599 'umounted first.')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700600 parser.add_argument('--noremove-rootfs-verification', action='store_true',
601 default=False, help='Never remove rootfs verification.')
Ben Pastenee484b342020-06-30 18:29:27 -0700602 parser.add_argument('--deploy-test-binaries', action='store_true',
603 default=False,
604 help='Also deploy any test binaries to %s. Useful for '
605 'running any Tast tests that execute these '
606 'binaries.' % _CHROME_TEST_BIN_DIR)
Erik Chen75a2f492020-08-06 19:15:11 -0700607 parser.add_argument('--lacros', action='store_true', default=False,
608 help='Deploys lacros-chrome rather than ash-chrome.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700609
Mike Frysingerc3061a62015-06-04 04:16:18 -0400610 group = parser.add_argument_group('Advanced Options')
611 group.add_argument('-l', '--local-pkg-path', type='path',
612 help='Path to local chrome prebuilt package to deploy.')
613 group.add_argument('--sloppy', action='store_true', default=False,
614 help='Ignore when mandatory artifacts are missing.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700615 group.add_argument('--staging-flags', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400616 help=('Extra flags to control staging. Valid flags are - '
617 '%s' % ', '.join(chrome_util.STAGING_FLAGS)))
Steven Bennetts46a84c32016-08-12 15:20:46 -0700618 # TODO(stevenjb): Remove --strict entirely once removed from the ebuild.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400619 group.add_argument('--strict', action='store_true', default=False,
Steven Bennetts46a84c32016-08-12 15:20:46 -0700620 help='Deprecated. Default behavior is "strict". Use '
621 '--sloppy to omit warnings for missing optional '
622 'files.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400623 group.add_argument('--strip-flags', default=None,
624 help="Flags to call the 'strip' binutil tool with. "
Mike Frysinger80de5012019-08-01 14:10:53 -0400625 'Overrides the default arguments.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400626 group.add_argument('--ping', action='store_true', default=False,
627 help='Ping the device before connection attempt.')
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700628 group.add_argument('--process-timeout', type=int,
629 default=KILL_PROC_MAX_WAIT,
630 help='Timeout for process shutdown.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700631
Mike Frysingerc3061a62015-06-04 04:16:18 -0400632 group = parser.add_argument_group(
633 'Metadata Overrides (Advanced)',
634 description='Provide all of these overrides in order to remove '
635 'dependencies on metadata.json existence.')
636 group.add_argument('--target-tc', action='store', default=None,
637 help='Override target toolchain name, e.g. '
638 'x86_64-cros-linux-gnu')
639 group.add_argument('--toolchain-url', action='store', default=None,
640 help='Override toolchain url format pattern, e.g. '
641 '2014/04/%%(target)s-2014.04.23.220740.tar.xz')
Aviv Keshet1c986f32014-04-24 13:20:49 -0700642
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700643 # DEPRECATED: --gyp-defines is ignored, but retained for backwards
644 # compatibility. TODO(stevenjb): Remove once eliminated from the ebuild.
645 parser.add_argument('--gyp-defines', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400646 help=argparse.SUPPRESS)
Steven Bennetts368c3e52016-09-23 13:05:21 -0700647
648 # GN_ARGS (args.gn) used to build Chrome. Influences which files are staged
649 # when --build-dir is set. Defaults to reading from the GN_ARGS env variable.
650 # CURRENLY IGNORED, ADDED FOR FORWARD COMPATABILITY.
651 parser.add_argument('--gn-args', default=None, type=ValidateGnArgs,
652 help=argparse.SUPPRESS)
653
Ryan Cuia56a71e2012-10-18 18:40:35 -0700654 # Path of an empty directory to stage chrome artifacts to. Defaults to a
655 # temporary directory that is removed when the script finishes. If the path
656 # is specified, then it will not be removed.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400657 parser.add_argument('--staging-dir', type='path', default=None,
658 help=argparse.SUPPRESS)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700659 # Only prepare the staging directory, and skip deploying to the device.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400660 parser.add_argument('--staging-only', action='store_true', default=False,
661 help=argparse.SUPPRESS)
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700662 # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
663 # is used as-is, and not normalized. Used by the Chrome ebuild to skip
664 # fetching the SDK toolchain.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400665 parser.add_argument('--strip-bin', default=None, help=argparse.SUPPRESS)
Satoru Takabayashif2893002017-06-20 14:52:48 +0900666 parser.add_argument('--compress', action='store', default='auto',
667 choices=('always', 'never', 'auto'),
668 help='Choose the data compression behavior. Default '
669 'is set to "auto", that disables compression if '
670 'the target device has a gigabit ethernet port.')
Ryan Cuie535b172012-10-19 18:25:03 -0700671 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700672
Ryan Cuie535b172012-10-19 18:25:03 -0700673
674def _ParseCommandLine(argv):
675 """Parse args, and run environment-independent checks."""
676 parser = _CreateParser()
Mike Frysingerc3061a62015-06-04 04:16:18 -0400677 options = parser.parse_args(argv)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700678
Ryan Cuia56a71e2012-10-18 18:40:35 -0700679 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
680 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800681 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700682 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
683 parser.error('Cannot specify both --build_dir and '
684 '--gs-path/--local-pkg-patch')
Erik Chen75a2f492020-08-06 19:15:11 -0700685 if options.lacros:
686 if options.board:
687 parser.error('--board is not supported with --lacros')
688 # The stripping implemented in this file rely on the cros-chrome-sdk, which
689 # is inappropriate for Lacros. Lacros stripping is currently not
690 # implemented.
691 if options.dostrip:
692 parser.error('--lacros requires --nostrip')
693 if options.mount_dir or options.mount:
694 parser.error('--lacros does not support --mount or --mount-dir')
695 if options.deploy_test_binaries:
696 parser.error('--lacros does not support --deploy-test-binaries')
697 if options.local_pkg_path:
698 parser.error('--lacros does not support --local-pkg-path')
699 else:
700 if not options.board:
701 parser.error('--board is required')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700702 if options.gs_path and options.local_pkg_path:
703 parser.error('Cannot specify both --gs-path and --local-pkg-path')
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700704 if not (options.staging_only or options.to or options.device):
705 parser.error('Need to specify --device')
Steven Bennetts46a84c32016-08-12 15:20:46 -0700706 if options.staging_flags and not options.build_dir:
707 parser.error('--staging-flags require --build-dir to be set.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700708
Steven Bennetts46a84c32016-08-12 15:20:46 -0700709 if options.strict:
710 logging.warning('--strict is deprecated.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700711 if options.gyp_defines:
712 logging.warning('--gyp-defines is deprecated.')
Thiago Goncales12793312013-05-23 11:26:17 -0700713
714 if options.mount or options.mount_dir:
715 if not options.target_dir:
716 options.target_dir = _CHROME_DIR_MOUNT
717 else:
718 if not options.target_dir:
Erik Chen75a2f492020-08-06 19:15:11 -0700719 options.target_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Thiago Goncales12793312013-05-23 11:26:17 -0700720
721 if options.mount and not options.mount_dir:
722 options.mount_dir = _CHROME_DIR
723
Ben Pastene3bd3e5e2020-08-10 14:40:43 -0700724 if options.to:
725 if options.device:
726 parser.error('--to and --device are mutually exclusive.')
727 else:
728 logging.warning(
729 "The args '--to' & '--port' are deprecated. Please use '--device' "
730 'instead.')
731
Mike Frysingerc3061a62015-06-04 04:16:18 -0400732 return options
Ryan Cui3045c5d2012-07-13 18:00:33 -0700733
734
Mike Frysingerc3061a62015-06-04 04:16:18 -0400735def _PostParseCheck(options):
Steve Funge984a532013-11-25 17:09:25 -0800736 """Perform some usage validation (after we've parsed the arguments).
Ryan Cui3045c5d2012-07-13 18:00:33 -0700737
738 Args:
Mike Frysinger5fe3f222016-09-01 00:14:16 -0400739 options: The options object returned by the cli parser.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700740 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700741 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
742 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
743
Steven Bennetts368c3e52016-09-23 13:05:21 -0700744 if not options.gn_args:
745 gn_env = os.getenv('GN_ARGS')
746 if gn_env is not None:
747 options.gn_args = gn_helpers.FromGNArgs(gn_env)
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800748 logging.debug('GN_ARGS taken from environment: %s', options.gn_args)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700749
Steven Bennetts60600462016-05-12 10:40:20 -0700750 if not options.staging_flags:
751 use_env = os.getenv('USE')
752 if use_env is not None:
753 options.staging_flags = ' '.join(set(use_env.split()).intersection(
754 chrome_util.STAGING_FLAGS))
755 logging.info('Staging flags taken from USE in environment: %s',
756 options.staging_flags)
757
Ryan Cuia56a71e2012-10-18 18:40:35 -0700758
Ryan Cui504db722013-01-22 11:48:01 -0800759def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700760 """Get the chrome prebuilt tarball from GS.
761
Mike Frysinger02e1e072013-11-10 22:11:34 -0500762 Returns:
763 Path to the fetched chrome tarball.
Ryan Cuia56a71e2012-10-18 18:40:35 -0700764 """
David James9374aac2013-10-08 16:00:17 -0700765 gs_ctx = gs.GSContext(cache_dir=cache_dir, init_boto=True)
Mike Frysinger7ad4fd62014-02-11 16:53:56 -0500766 files = gs_ctx.LS(gs_path)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800767 files = [found for found in files if
768 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
769 if not files:
770 raise Exception('No chrome package found at %s' % gs_path)
771 elif len(files) > 1:
772 # - Users should provide us with a direct link to either a stripped or
773 # unstripped chrome package.
774 # - In the case of being provided with an archive directory, where both
775 # stripped and unstripped chrome available, use the stripped chrome
776 # package.
777 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
778 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
779 files = [f for f in files if not 'unstripped' in f]
780 assert len(files) == 1
781 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800782
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800783 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800784 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800785 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
786 chrome_path = os.path.join(tempdir, filename)
787 assert os.path.exists(chrome_path)
788 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700789
790
Ryan Cuif890a3e2013-03-07 18:57:06 -0800791@contextlib.contextmanager
792def _StripBinContext(options):
793 if not options.dostrip:
794 yield None
795 elif options.strip_bin:
796 yield options.strip_bin
797 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800798 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800799 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
Aviv Keshet1c986f32014-04-24 13:20:49 -0700800 with sdk.Prepare(components=components, target_tc=options.target_tc,
801 toolchain_url=options.toolchain_url) as ctx:
Ryan Cui686ec052013-02-12 16:39:41 -0800802 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
803 constants.CHROME_ENV_FILE)
804 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
805 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
806 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800807 yield strip_bin
808
809
Steve Funge984a532013-11-25 17:09:25 -0800810def _PrepareStagingDir(options, tempdir, staging_dir, copy_paths=None,
Erik Chen75a2f492020-08-06 19:15:11 -0700811 chrome_dir=None):
Ryan Cuif890a3e2013-03-07 18:57:06 -0800812 """Place the necessary files in the staging directory.
813
814 The staging directory is the directory used to rsync the build artifacts over
815 to the device. Only the necessary Chrome build artifacts are put into the
816 staging directory.
817 """
Erik Chen75a2f492020-08-06 19:15:11 -0700818 if chrome_dir is None:
819 chrome_dir = LACROS_DIR if options.lacros else _CHROME_DIR
Ryan Cui5866be02013-03-18 14:12:00 -0700820 osutils.SafeMakedirs(staging_dir)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400821 os.chmod(staging_dir, 0o755)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800822 if options.build_dir:
823 with _StripBinContext(options) as strip_bin:
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700824 strip_flags = (None if options.strip_flags is None else
825 shlex.split(options.strip_flags))
Ryan Cui686ec052013-02-12 16:39:41 -0800826 chrome_util.StageChromeFromBuildDir(
Steven Bennetts46a84c32016-08-12 15:20:46 -0700827 staging_dir, options.build_dir, strip_bin,
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700828 sloppy=options.sloppy, gn_args=options.gn_args,
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700829 staging_flags=options.staging_flags,
Steve Funge984a532013-11-25 17:09:25 -0800830 strip_flags=strip_flags, copy_paths=copy_paths)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700831 else:
832 pkg_path = options.local_pkg_path
833 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800834 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
835 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700836
837 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800838 logging.info('Extracting %s...', pkg_path)
Ryan Cui5866be02013-03-18 14:12:00 -0700839 # Extract only the ./opt/google/chrome contents, directly into the staging
840 # dir, collapsing the directory hierarchy.
Steve Funge984a532013-11-25 17:09:25 -0800841 if pkg_path[-4:] == '.zip':
Mike Frysinger45602c72019-09-22 02:15:11 -0400842 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800843 ['unzip', '-X', pkg_path, _ANDROID_DIR_EXTRACT_PATH, '-d',
844 staging_dir])
845 for filename in glob.glob(os.path.join(staging_dir, 'system/chrome/*')):
846 shutil.move(filename, staging_dir)
847 osutils.RmDir(os.path.join(staging_dir, 'system'), ignore_missing=True)
848 else:
Mike Frysinger45602c72019-09-22 02:15:11 -0400849 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800850 ['tar', '--strip-components', '4', '--extract',
851 '--preserve-permissions', '--file', pkg_path, '.%s' % chrome_dir],
852 cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700853
Ryan Cui71aa8de2013-04-19 16:12:55 -0700854
Ryan Cui3045c5d2012-07-13 18:00:33 -0700855def main(argv):
Mike Frysingerc3061a62015-06-04 04:16:18 -0400856 options = _ParseCommandLine(argv)
857 _PostParseCheck(options)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700858
Aviv Keshet01a82e92017-03-02 17:39:59 -0800859 with osutils.TempDir(set_global=True) as tempdir:
860 staging_dir = options.staging_dir
861 if not staging_dir:
862 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700863
Aviv Keshet01a82e92017-03-02 17:39:59 -0800864 deploy = DeployChrome(options, tempdir, staging_dir)
865 try:
866 deploy.Perform()
867 except failures_lib.StepFailure as ex:
868 raise SystemExit(str(ex).strip())
869 deploy.Cleanup()