blob: c5fe29988d0a66afdba6c813ed9fc764bf071738 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Ryan Cui3045c5d2012-07-13 18:00:33 -07002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Steve Funge984a532013-11-25 17:09:25 -08006"""Script that deploys a Chrome build to a device.
Ryan Cuia56a71e2012-10-18 18:40:35 -07007
8The script supports deploying Chrome from these sources:
9
101. A local build output directory, such as chromium/src/out/[Debug|Release].
112. A Chrome tarball uploaded by a trybot/official-builder to GoogleStorage.
123. A Chrome tarball existing locally.
13
14The script copies the necessary contents of the source location (tarball or
15build directory) and rsyncs the contents of the staging directory onto your
16device's rootfs.
17"""
Ryan Cui3045c5d2012-07-13 18:00:33 -070018
Mike Frysinger383367e2014-09-16 15:06:17 -040019from __future__ import print_function
20
Mike Frysingerc3061a62015-06-04 04:16:18 -040021import argparse
Ryan Cui7193a7e2013-04-26 14:15:19 -070022import collections
Ryan Cuif890a3e2013-03-07 18:57:06 -080023import contextlib
Ryan Cui7193a7e2013-04-26 14:15:19 -070024import functools
Steve Funge984a532013-11-25 17:09:25 -080025import glob
David James88e6f032013-03-02 08:13:20 -080026import multiprocessing
Ryan Cui3045c5d2012-07-13 18:00:33 -070027import os
Ryan Cui5b7c2ed2013-03-18 18:45:46 -070028import shlex
Steve Funge984a532013-11-25 17:09:25 -080029import shutil
Mike Frysinger6165cdc2020-02-21 02:38:07 -050030import sys
Ryan Cui3045c5d2012-07-13 18:00:33 -070031import time
Ryan Cui3045c5d2012-07-13 18:00:33 -070032
Aviv Keshetb7519e12016-10-04 00:50:00 -070033from chromite.lib import constants
34from chromite.lib import failures_lib
David Pursellcfd58872015-03-19 09:15:48 -070035from chromite.cli.cros import cros_chrome_sdk
Ryan Cuia56a71e2012-10-18 18:40:35 -070036from chromite.lib import chrome_util
Ryan Cuie535b172012-10-19 18:25:03 -070037from chromite.lib import commandline
Ralph Nathan91874ca2015-03-19 13:29:41 -070038from chromite.lib import cros_build_lib
39from chromite.lib import cros_logging as logging
Ryan Cui777ff422012-12-07 13:12:54 -080040from chromite.lib import gs
Ryan Cui3045c5d2012-07-13 18:00:33 -070041from chromite.lib import osutils
David James88e6f032013-03-02 08:13:20 -080042from chromite.lib import parallel
Ryan Cui3045c5d2012-07-13 18:00:33 -070043from chromite.lib import remote_access as remote
David James3432acd2013-11-27 10:02:18 -080044from chromite.lib import timeout_util
Steven Bennetts368c3e52016-09-23 13:05:21 -070045from gn_helpers import gn_helpers
Ryan Cui3045c5d2012-07-13 18:00:33 -070046
47
Mike Frysinger6165cdc2020-02-21 02:38:07 -050048assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
49
50
Ryan Cui3045c5d2012-07-13 18:00:33 -070051KERNEL_A_PARTITION = 2
52KERNEL_B_PARTITION = 4
53
54KILL_PROC_MAX_WAIT = 10
55POST_KILL_WAIT = 2
56
David Haddock3151d912017-10-24 03:50:32 +000057MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Anushruth8d797672019-10-17 12:22:31 -070058LSOF_COMMAND_CHROME = 'lsof %s/chrome'
59LSOF_COMMAND = 'lsof %s'
David Haddock3151d912017-10-24 03:50:32 +000060DBUS_RELOAD_COMMAND = 'killall -HUP dbus-daemon'
Ryan Cui3045c5d2012-07-13 18:00:33 -070061
Steve Funge984a532013-11-25 17:09:25 -080062_ANDROID_DIR = '/system/chrome'
63_ANDROID_DIR_EXTRACT_PATH = 'system/chrome/*'
64
David James2cb34002013-03-01 18:42:40 -080065_CHROME_DIR = '/opt/google/chrome'
Thiago Goncales12793312013-05-23 11:26:17 -070066_CHROME_DIR_MOUNT = '/mnt/stateful_partition/deploy_rootfs/opt/google/chrome'
Ben Pastenee484b342020-06-30 18:29:27 -070067_CHROME_TEST_BIN_DIR = '/usr/local/libexec/chrome-binary-tests'
David James2cb34002013-03-01 18:42:40 -080068
David Haddock3151d912017-10-24 03:50:32 +000069_UMOUNT_DIR_IF_MOUNTPOINT_CMD = (
70 'if mountpoint -q %(dir)s; then umount %(dir)s; fi')
71_BIND_TO_FINAL_DIR_CMD = 'mount --rbind %s %s'
72_SET_MOUNT_FLAGS_CMD = 'mount -o remount,exec,suid %s'
Anushruth8d797672019-10-17 12:22:31 -070073_MKDIR_P_CMD = 'mkdir -p --mode 0775 %s'
Ben Pastenee484b342020-06-30 18:29:27 -070074_FIND_TEST_BIN_CMD = 'find %s -maxdepth 1 -executable -type f' % (
75 _CHROME_TEST_BIN_DIR)
David Haddock3151d912017-10-24 03:50:32 +000076
77DF_COMMAND = 'df -k %s'
78
Mike Frysingere65f3752014-12-08 00:46:39 -050079
Ryan Cui3045c5d2012-07-13 18:00:33 -070080def _UrlBaseName(url):
81 """Return the last component of the URL."""
82 return url.rstrip('/').rpartition('/')[-1]
83
84
Yu-Ju Hongc54d3342014-05-14 12:42:06 -070085class DeployFailure(failures_lib.StepFailure):
David James88e6f032013-03-02 08:13:20 -080086 """Raised whenever the deploy fails."""
87
88
Ryan Cui7193a7e2013-04-26 14:15:19 -070089DeviceInfo = collections.namedtuple(
90 'DeviceInfo', ['target_dir_size', 'target_fs_free'])
91
92
Ryan Cui3045c5d2012-07-13 18:00:33 -070093class DeployChrome(object):
94 """Wraps the core deployment functionality."""
Mike Frysingere65f3752014-12-08 00:46:39 -050095
Ryan Cuia56a71e2012-10-18 18:40:35 -070096 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -070097 """Initialize the class.
98
Mike Frysinger02e1e072013-11-10 22:11:34 -050099 Args:
Mike Frysingerc3061a62015-06-04 04:16:18 -0400100 options: options object.
Ryan Cuie535b172012-10-19 18:25:03 -0700101 tempdir: Scratch space for the class. Caller has responsibility to clean
102 it up.
Steve Funge984a532013-11-25 17:09:25 -0800103 staging_dir: Directory to stage the files to.
Ryan Cuie535b172012-10-19 18:25:03 -0700104 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700105 self.tempdir = tempdir
106 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -0700107 self.staging_dir = staging_dir
Robert Flack1dc7ea82014-11-26 13:50:24 -0500108 if not self.options.staging_only:
Avery Musbach3edff0e2020-03-27 13:35:53 -0700109 self.device = remote.ChromiumOSDevice(options.to, port=options.port,
110 ping=options.ping,
111 private_key=options.private_key,
112 include_dev_paths=False)
Steven Bennettsca73efa2018-07-10 13:36:56 -0700113 self._root_dir_is_still_readonly = multiprocessing.Event()
Steven Bennetts5a7c72d2016-10-17 20:04:46 +0000114
Sadrul Habib Chowdhury977aef72016-11-21 16:30:37 -0500115 self.copy_paths = chrome_util.GetCopyPaths('chrome')
Steve Funge984a532013-11-25 17:09:25 -0800116 self.chrome_dir = _CHROME_DIR
117
Ryan Cui7193a7e2013-04-26 14:15:19 -0700118 def _GetRemoteMountFree(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400119 result = self.device.run(DF_COMMAND % remote_dir)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700120 line = result.output.splitlines()[1]
Steve Funge984a532013-11-25 17:09:25 -0800121 value = line.split()[3]
122 multipliers = {
123 'G': 1024 * 1024 * 1024,
124 'M': 1024 * 1024,
125 'K': 1024,
126 }
127 return int(value.rstrip('GMK')) * multipliers.get(value[-1], 1)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700128
129 def _GetRemoteDirSize(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400130 result = self.device.run('du -ks %s' % remote_dir,
131 capture_output=True, encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700132 return int(result.output.split()[0])
133
134 def _GetStagingDirSize(self):
Mike Frysinger45602c72019-09-22 02:15:11 -0400135 result = cros_build_lib.dbg_run(['du', '-ks', self.staging_dir],
Mike Frysinger0282d222019-12-17 17:15:48 -0500136 stdout=True, capture_output=True,
Mike Frysingerb88d5fb2019-10-23 00:38:45 -0400137 encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700138 return int(result.output.split()[0])
139
Ryan Cui3045c5d2012-07-13 18:00:33 -0700140 def _ChromeFileInUse(self):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400141 result = self.device.run(LSOF_COMMAND_CHROME % (self.options.target_dir,),
142 check=False, capture_output=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700143 return result.returncode == 0
144
Justin TerAvestfac210e2017-04-13 11:39:00 -0600145 def _Reboot(self):
146 # A reboot in developer mode takes a while (and has delays), so the user
147 # will have time to read and act on the USB boot instructions below.
148 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
149 self.device.Reboot()
150
Ryan Cui3045c5d2012-07-13 18:00:33 -0700151 def _DisableRootfsVerification(self):
152 if not self.options.force:
153 logging.error('Detected that the device has rootfs verification enabled.')
154 logging.info('This script can automatically remove the rootfs '
155 'verification, which requires that it reboot the device.')
156 logging.info('Make sure the device is in developer mode!')
157 logging.info('Skip this prompt by specifying --force.')
Ben Chan37d64e52018-10-05 15:35:02 -0700158 if not cros_build_lib.BooleanPrompt('Remove rootfs verification?', False):
Steven Bennettsca73efa2018-07-10 13:36:56 -0700159 return False
Ryan Cui3045c5d2012-07-13 18:00:33 -0700160
161 logging.info('Removing rootfs verification from %s', self.options.to)
162 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
163 # Use --force to bypass the checks.
David Haddock3151d912017-10-24 03:50:32 +0000164 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
165 '--remove_rootfs_verification --force')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700166 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400167 self.device.run(cmd % partition, check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700168
Justin TerAvestfac210e2017-04-13 11:39:00 -0600169 self._Reboot()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700170
David James88e6f032013-03-02 08:13:20 -0800171 # Now that the machine has been rebooted, we need to kill Chrome again.
172 self._KillProcsIfNeeded()
173
174 # Make sure the rootfs is writable now.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500175 self._MountRootfsAsWritable(check=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700176
Steven Bennettsca73efa2018-07-10 13:36:56 -0700177 return True
178
Ryan Cui3045c5d2012-07-13 18:00:33 -0700179 def _CheckUiJobStarted(self):
180 # status output is in the format:
181 # <job_name> <status> ['process' <pid>].
182 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800183 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400184 result = self.device.run('status ui', capture_output=True,
185 encoding='utf-8')
Ryan Cuif2d1a582013-02-19 14:08:13 -0800186 except cros_build_lib.RunCommandError as e:
187 if 'Unknown job' in e.result.error:
188 return False
189 else:
190 raise e
191
Ryan Cui3045c5d2012-07-13 18:00:33 -0700192 return result.output.split()[1].split('/')[0] == 'start'
193
194 def _KillProcsIfNeeded(self):
195 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800196 logging.info('Shutting down Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400197 self.device.run('stop ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700198
199 # Developers sometimes run session_manager manually, in which case we'll
200 # need to help shut the chrome processes down.
201 try:
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700202 with timeout_util.Timeout(self.options.process_timeout):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700203 while self._ChromeFileInUse():
204 logging.warning('The chrome binary on the device is in use.')
205 logging.warning('Killing chrome and session_manager processes...\n')
206
Mike Frysinger3459bf52020-03-31 00:52:11 -0400207 self.device.run("pkill 'chrome|session_manager'", check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700208 # Wait for processes to actually terminate
209 time.sleep(POST_KILL_WAIT)
210 logging.info('Rechecking the chrome binary...')
David James3432acd2013-11-27 10:02:18 -0800211 except timeout_util.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800212 msg = ('Could not kill processes after %s seconds. Please exit any '
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700213 'running chrome processes and try again.'
214 % self.options.process_timeout)
David James88e6f032013-03-02 08:13:20 -0800215 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700216
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500217 def _MountRootfsAsWritable(self, check=False):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800218 """Mounts the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700219
Steven Bennettsca73efa2018-07-10 13:36:56 -0700220 If the command fails and the root dir is not writable then this function
221 sets self._root_dir_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700222
Mike Frysinger02e1e072013-11-10 22:11:34 -0500223 Args:
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500224 check: See remote.RemoteAccess.RemoteSh for details.
David James88e6f032013-03-02 08:13:20 -0800225 """
Yu-Ju Hong7e116ac2014-01-03 17:08:26 -0800226 # TODO: Should migrate to use the remount functions in remote_access.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400227 result = self.device.run(MOUNT_RW_COMMAND, check=check,
228 capture_output=True, encoding='utf-8')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700229 if result.returncode and not self.device.IsDirWritable('/'):
230 self._root_dir_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700231
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800232 def _EnsureTargetDir(self):
233 """Ensures that the target directory exists on the remote device."""
234 target_dir = self.options.target_dir
235 # Any valid /opt directory should already exist so avoid the remote call.
236 if os.path.commonprefix([target_dir, '/opt']) == '/opt':
237 return
Mike Frysinger3459bf52020-03-31 00:52:11 -0400238 self.device.run(['mkdir', '-p', '--mode', '0775', target_dir])
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800239
Ryan Cui7193a7e2013-04-26 14:15:19 -0700240 def _GetDeviceInfo(self):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800241 """Returns the disk space used and available for the target diectory."""
Ryan Cui7193a7e2013-04-26 14:15:19 -0700242 steps = [
243 functools.partial(self._GetRemoteDirSize, self.options.target_dir),
244 functools.partial(self._GetRemoteMountFree, self.options.target_dir)
245 ]
246 return_values = parallel.RunParallelSteps(steps, return_values=True)
247 return DeviceInfo(*return_values)
248
249 def _CheckDeviceFreeSpace(self, device_info):
250 """See if target device has enough space for Chrome.
251
Mike Frysinger02e1e072013-11-10 22:11:34 -0500252 Args:
Ryan Cui7193a7e2013-04-26 14:15:19 -0700253 device_info: A DeviceInfo named tuple.
254 """
255 effective_free = device_info.target_dir_size + device_info.target_fs_free
256 staging_size = self._GetStagingDirSize()
257 if effective_free < staging_size:
Daniel Erat1ae46382014-08-14 10:23:39 -0700258 raise DeployFailure(
259 'Not enough free space on the device. Required: %s MiB, '
Mike Frysinger93e8ffa2019-07-03 20:24:18 -0400260 'actual: %s MiB.' % (staging_size // 1024, effective_free // 1024))
Ryan Cui7193a7e2013-04-26 14:15:19 -0700261 if device_info.target_fs_free < (100 * 1024):
262 logging.warning('The device has less than 100MB free. deploy_chrome may '
263 'hang during the transfer.')
264
Satoru Takabayashif2893002017-06-20 14:52:48 +0900265 def _ShouldUseCompression(self):
266 """Checks if compression should be used for rsync."""
267 if self.options.compress == 'always':
268 return True
269 elif self.options.compress == 'never':
270 return False
271 elif self.options.compress == 'auto':
272 return not self.device.HasGigabitEthernet()
273
Ryan Cui3045c5d2012-07-13 18:00:33 -0700274 def _Deploy(self):
Pawel Osciak577773a2013-03-05 10:52:12 -0800275 logging.info('Copying Chrome to %s on device...', self.options.target_dir)
Ilja H. Friedel0ab63e12017-03-28 13:29:48 -0700276 # CopyToDevice will fall back to scp if rsync is corrupted on stateful.
277 # This does not work for deploy.
Satoru Takabayashiab380bc2015-01-15 16:00:41 +0900278 if not self.device.HasRsync():
279 raise DeployFailure(
David Pursellcfd58872015-03-19 09:15:48 -0700280 'rsync is not found on the device.\n'
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500281 'Run dev_install on the device to get rsync installed.')
Robert Flack1dc7ea82014-11-26 13:50:24 -0500282 self.device.CopyToDevice('%s/' % os.path.abspath(self.staging_dir),
283 self.options.target_dir,
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700284 mode='rsync', inplace=True,
Satoru Takabayashif2893002017-06-20 14:52:48 +0900285 compress=self._ShouldUseCompression(),
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700286 debug_level=logging.INFO,
Robert Flack1dc7ea82014-11-26 13:50:24 -0500287 verbose=self.options.verbose)
Ben Pastene5f03b052019-08-12 18:03:24 -0700288
289 # Set the security context on the default Chrome dir if that's where it's
290 # getting deployed, and only on SELinux supported devices.
291 if (self.device.IsSELinuxAvailable() and
292 _CHROME_DIR in (self.options.target_dir, self.options.mount_dir)):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400293 self.device.run(['restorecon', '-R', _CHROME_DIR])
Steve Funge984a532013-11-25 17:09:25 -0800294
295 for p in self.copy_paths:
Steve Funge984a532013-11-25 17:09:25 -0800296 if p.mode:
297 # Set mode if necessary.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400298 self.device.run('chmod %o %s/%s' % (
David Haddock3151d912017-10-24 03:50:32 +0000299 p.mode, self.options.target_dir, p.src if not p.dest else p.dest))
Steve Funge984a532013-11-25 17:09:25 -0800300
Daniel Erat0fce5bf2017-09-28 17:29:23 -0700301 # Send SIGHUP to dbus-daemon to tell it to reload its configs. This won't
302 # pick up major changes (bus type, logging, etc.), but all we care about is
303 # getting the latest policy from /opt/google/chrome/dbus so that Chrome will
304 # be authorized to take ownership of its service names.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400305 self.device.run(DBUS_RELOAD_COMMAND, check=False)
Justin TerAvestfac210e2017-04-13 11:39:00 -0600306
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800307 if self.options.startui:
Steve Funge984a532013-11-25 17:09:25 -0800308 logging.info('Starting UI...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400309 self.device.run('start ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700310
Ben Pastenee484b342020-06-30 18:29:27 -0700311 def _DeployTestBinaries(self):
312 """Deploys any local test binary to _CHROME_TEST_BIN_DIR on the device.
313
314 There could be several binaries located in the local build dir, so compare
315 what's already present on the device in _CHROME_TEST_BIN_DIR , and copy
316 over any that we also built ourselves.
317 """
318 r = self.device.run(_FIND_TEST_BIN_CMD, check=False)
319 if r.returncode != 0:
320 raise DeployFailure('Unable to ls contents of %s' % _CHROME_TEST_BIN_DIR)
321 binaries_to_copy = []
322 for f in r.output.splitlines():
323 binaries_to_copy.append(
324 chrome_util.Path(os.path.basename(f), exe=True, optional=True))
325
326 staging_dir = os.path.join(
327 self.tempdir, os.path.basename(_CHROME_TEST_BIN_DIR))
328 _PrepareStagingDir(self.options, self.tempdir, staging_dir,
329 copy_paths=binaries_to_copy)
330 self.device.CopyToDevice(
331 staging_dir, os.path.dirname(_CHROME_TEST_BIN_DIR), mode='rsync')
332
David James88e6f032013-03-02 08:13:20 -0800333 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700334 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800335 logging.info('Testing connection to the device...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400336 self.device.run('true')
David James88e6f032013-03-02 08:13:20 -0800337 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700338 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800339 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700340
Avery Musbach3edff0e2020-03-27 13:35:53 -0700341 def _CheckBoard(self):
342 """Check that the Chrome build is targeted for the device board."""
343 if self.options.board == self.device.board:
344 return
345 logging.warning('Device board is %s whereas target board is %s.',
346 self.device.board, self.options.board)
347 if self.options.force:
348 return
349 if not cros_build_lib.BooleanPrompt('Continue despite board mismatch?',
350 False):
351 raise DeployFailure('Aborted.')
352
Steve Funge984a532013-11-25 17:09:25 -0800353 def _CheckDeployType(self):
Steve Fung63d705d2014-03-16 03:14:03 -0700354 if self.options.build_dir:
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700355 def BinaryExists(filename):
356 """Checks if the passed-in file is present in the build directory."""
357 return os.path.exists(os.path.join(self.options.build_dir, filename))
358
Daniel Erat9813f0e2014-11-12 11:00:28 -0700359 # Handle non-Chrome deployments.
360 if not BinaryExists('chrome'):
Daniel Eratf53bd3a2016-12-02 11:28:36 -0700361 if BinaryExists('app_shell'):
Daniel Erat9813f0e2014-11-12 11:00:28 -0700362 self.copy_paths = chrome_util.GetCopyPaths('app_shell')
363
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700364 # TODO(derat): Update _Deploy() and remove this after figuring out how
Daniel Eratf53bd3a2016-12-02 11:28:36 -0700365 # app_shell should be executed.
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700366 self.options.startui = False
367
David James88e6f032013-03-02 08:13:20 -0800368 def _PrepareStagingDir(self):
Steve Funge984a532013-11-25 17:09:25 -0800369 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir,
370 self.copy_paths, self.chrome_dir)
David James88e6f032013-03-02 08:13:20 -0800371
Thiago Goncales12793312013-05-23 11:26:17 -0700372 def _MountTarget(self):
373 logging.info('Mounting Chrome...')
374
Anushruth8d797672019-10-17 12:22:31 -0700375 # Create directory if does not exist.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400376 self.device.run(_MKDIR_P_CMD % self.options.mount_dir)
Anushruth8d797672019-10-17 12:22:31 -0700377 try:
378 # Umount the existing mount on mount_dir if present first.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400379 self.device.run(_UMOUNT_DIR_IF_MOUNTPOINT_CMD %
380 {'dir': self.options.mount_dir})
Anushruth8d797672019-10-17 12:22:31 -0700381 except cros_build_lib.RunCommandError as e:
382 logging.error('Failed to umount %s', self.options.mount_dir)
383 # If there is a failure, check if some processs is using the mount_dir.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400384 result = self.device.run(LSOF_COMMAND % (self.options.mount_dir,),
385 check=False, capture_output=True,
386 encoding='utf-8')
Anushruth8d797672019-10-17 12:22:31 -0700387 logging.error('lsof %s -->', self.options.mount_dir)
388 logging.error(result.stdout)
389 raise e
390
Mike Frysinger3459bf52020-03-31 00:52:11 -0400391 self.device.run(_BIND_TO_FINAL_DIR_CMD % (self.options.target_dir,
392 self.options.mount_dir))
Anushruth8d797672019-10-17 12:22:31 -0700393
Thiago Goncales12793312013-05-23 11:26:17 -0700394 # Chrome needs partition to have exec and suid flags set
Mike Frysinger3459bf52020-03-31 00:52:11 -0400395 self.device.run(_SET_MOUNT_FLAGS_CMD % (self.options.mount_dir,))
Robert Flack1dc7ea82014-11-26 13:50:24 -0500396
397 def Cleanup(self):
398 """Clean up RemoteDevice."""
399 if not self.options.staging_only:
400 self.device.Cleanup()
Thiago Goncales12793312013-05-23 11:26:17 -0700401
David James88e6f032013-03-02 08:13:20 -0800402 def Perform(self):
Steve Funge984a532013-11-25 17:09:25 -0800403 self._CheckDeployType()
404
David James88e6f032013-03-02 08:13:20 -0800405 # If requested, just do the staging step.
406 if self.options.staging_only:
407 self._PrepareStagingDir()
408 return 0
409
Avery Musbach3edff0e2020-03-27 13:35:53 -0700410 # Check that the build matches the device.
411 self._CheckBoard()
412
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800413 # Ensure that the target directory exists before running parallel steps.
414 self._EnsureTargetDir()
415
David James88e6f032013-03-02 08:13:20 -0800416 # Run setup steps in parallel. If any step fails, RunParallelSteps will
David James48f6b332013-03-03 20:11:18 -0800417 # stop printing output at that point, and halt any running steps.
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800418 logging.info('Preparing device')
Steve Funge984a532013-11-25 17:09:25 -0800419 steps = [self._GetDeviceInfo, self._CheckConnection,
420 self._KillProcsIfNeeded, self._MountRootfsAsWritable,
421 self._PrepareStagingDir]
Ryan Cui7193a7e2013-04-26 14:15:19 -0700422 ret = parallel.RunParallelSteps(steps, halt_on_error=True,
423 return_values=True)
424 self._CheckDeviceFreeSpace(ret[0])
David James88e6f032013-03-02 08:13:20 -0800425
Steven Bennettsca73efa2018-07-10 13:36:56 -0700426 # If the root dir is not writable, try disabling rootfs verification.
427 # (We always do this by default so that developers can write to
428 # /etc/chriome_dev.conf and other directories in the rootfs).
429 if self._root_dir_is_still_readonly.is_set():
430 if self.options.noremove_rootfs_verification:
431 logging.warning('Skipping disable rootfs verification.')
432 elif not self._DisableRootfsVerification():
433 logging.warning('Failed to disable rootfs verification.')
434
435 # If the target dir is still not writable (i.e. the user opted out or the
436 # command failed), abort.
437 if not self.device.IsDirWritable(self.options.target_dir):
438 if self.options.startui:
439 logging.info('Restarting Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400440 self.device.run('start ui')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700441 raise DeployFailure('Target location is not writable. Aborting.')
David James88e6f032013-03-02 08:13:20 -0800442
Thiago Goncales12793312013-05-23 11:26:17 -0700443 if self.options.mount_dir is not None:
444 self._MountTarget()
445
David James88e6f032013-03-02 08:13:20 -0800446 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700447 self._Deploy()
Ben Pastenee484b342020-06-30 18:29:27 -0700448 if self.options.deploy_test_binaries:
449 self._DeployTestBinaries()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700450
451
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700452def ValidateStagingFlags(value):
453 """Convert formatted string to dictionary."""
454 return chrome_util.ProcessShellFlags(value)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700455
456
Steven Bennetts368c3e52016-09-23 13:05:21 -0700457def ValidateGnArgs(value):
458 """Convert GN_ARGS-formatted string to dictionary."""
459 return gn_helpers.FromGNArgs(value)
460
461
Ryan Cuie535b172012-10-19 18:25:03 -0700462def _CreateParser():
463 """Create our custom parser."""
Mike Frysingerc3061a62015-06-04 04:16:18 -0400464 parser = commandline.ArgumentParser(description=__doc__, caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700465
Ryan Cuia56a71e2012-10-18 18:40:35 -0700466 # TODO(rcui): Have this use the UI-V2 format of having source and target
467 # device be specified as positional arguments.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400468 parser.add_argument('--force', action='store_true', default=False,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700469 help='Skip all prompts (such as the prompt for disabling '
470 'of rootfs verification). This may result in the '
471 'target machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800472 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
Mike Frysingerc3061a62015-06-04 04:16:18 -0400473 parser.add_argument('--board', default=sdk_board_env,
Mike Frysinger80de5012019-08-01 14:10:53 -0400474 help='The board the Chrome build is targeted for. When '
Mike Frysingerc3061a62015-06-04 04:16:18 -0400475 "in a 'cros chrome-sdk' shell, defaults to the SDK "
Mike Frysinger80de5012019-08-01 14:10:53 -0400476 'board.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400477 parser.add_argument('--build-dir', type='path',
478 help='The directory with Chrome build artifacts to '
479 'deploy from. Typically of format '
480 '<chrome_root>/out/Debug. When this option is used, '
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700481 'the GN_ARGS environment variable must be set.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400482 parser.add_argument('--target-dir', type='path',
483 default=None,
484 help='Target directory on device to deploy Chrome into.')
485 parser.add_argument('-g', '--gs-path', type='gs_path',
486 help='GS path that contains the chrome to deploy.')
Adrian Eldera2c548a2017-11-07 19:01:29 -0500487 parser.add_argument('--private-key', type='path', default=None,
488 help='An ssh private key to use when deploying to '
489 'a CrOS device.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400490 parser.add_argument('--nostartui', action='store_false', dest='startui',
491 default=True,
492 help="Don't restart the ui daemon after deployment.")
493 parser.add_argument('--nostrip', action='store_false', dest='dostrip',
494 default=True,
495 help="Don't strip binaries during deployment. Warning: "
496 'the resulting binaries will be very large!')
497 parser.add_argument('-p', '--port', type=int, default=remote.DEFAULT_SSH_PORT,
498 help='Port of the target device to connect to.')
499 parser.add_argument('-t', '--to',
500 help='The IP address of the CrOS device to deploy to.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400501 parser.add_argument('--mount-dir', type='path', default=None,
502 help='Deploy Chrome in target directory and bind it '
503 'to the directory specified by this flag.'
504 'Any existing mount on this directory will be '
505 'umounted first.')
506 parser.add_argument('--mount', action='store_true', default=False,
507 help='Deploy Chrome to default target directory and bind '
508 'it to the default mount directory.'
509 'Any existing mount on this directory will be '
510 'umounted first.')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700511 parser.add_argument('--noremove-rootfs-verification', action='store_true',
512 default=False, help='Never remove rootfs verification.')
Ben Pastenee484b342020-06-30 18:29:27 -0700513 parser.add_argument('--deploy-test-binaries', action='store_true',
514 default=False,
515 help='Also deploy any test binaries to %s. Useful for '
516 'running any Tast tests that execute these '
517 'binaries.' % _CHROME_TEST_BIN_DIR)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700518
Mike Frysingerc3061a62015-06-04 04:16:18 -0400519 group = parser.add_argument_group('Advanced Options')
520 group.add_argument('-l', '--local-pkg-path', type='path',
521 help='Path to local chrome prebuilt package to deploy.')
522 group.add_argument('--sloppy', action='store_true', default=False,
523 help='Ignore when mandatory artifacts are missing.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700524 group.add_argument('--staging-flags', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400525 help=('Extra flags to control staging. Valid flags are - '
526 '%s' % ', '.join(chrome_util.STAGING_FLAGS)))
Steven Bennetts46a84c32016-08-12 15:20:46 -0700527 # TODO(stevenjb): Remove --strict entirely once removed from the ebuild.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400528 group.add_argument('--strict', action='store_true', default=False,
Steven Bennetts46a84c32016-08-12 15:20:46 -0700529 help='Deprecated. Default behavior is "strict". Use '
530 '--sloppy to omit warnings for missing optional '
531 'files.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400532 group.add_argument('--strip-flags', default=None,
533 help="Flags to call the 'strip' binutil tool with. "
Mike Frysinger80de5012019-08-01 14:10:53 -0400534 'Overrides the default arguments.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400535 group.add_argument('--ping', action='store_true', default=False,
536 help='Ping the device before connection attempt.')
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700537 group.add_argument('--process-timeout', type=int,
538 default=KILL_PROC_MAX_WAIT,
539 help='Timeout for process shutdown.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700540
Mike Frysingerc3061a62015-06-04 04:16:18 -0400541 group = parser.add_argument_group(
542 'Metadata Overrides (Advanced)',
543 description='Provide all of these overrides in order to remove '
544 'dependencies on metadata.json existence.')
545 group.add_argument('--target-tc', action='store', default=None,
546 help='Override target toolchain name, e.g. '
547 'x86_64-cros-linux-gnu')
548 group.add_argument('--toolchain-url', action='store', default=None,
549 help='Override toolchain url format pattern, e.g. '
550 '2014/04/%%(target)s-2014.04.23.220740.tar.xz')
Aviv Keshet1c986f32014-04-24 13:20:49 -0700551
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700552 # DEPRECATED: --gyp-defines is ignored, but retained for backwards
553 # compatibility. TODO(stevenjb): Remove once eliminated from the ebuild.
554 parser.add_argument('--gyp-defines', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400555 help=argparse.SUPPRESS)
Steven Bennetts368c3e52016-09-23 13:05:21 -0700556
557 # GN_ARGS (args.gn) used to build Chrome. Influences which files are staged
558 # when --build-dir is set. Defaults to reading from the GN_ARGS env variable.
559 # CURRENLY IGNORED, ADDED FOR FORWARD COMPATABILITY.
560 parser.add_argument('--gn-args', default=None, type=ValidateGnArgs,
561 help=argparse.SUPPRESS)
562
Ryan Cuia56a71e2012-10-18 18:40:35 -0700563 # Path of an empty directory to stage chrome artifacts to. Defaults to a
564 # temporary directory that is removed when the script finishes. If the path
565 # is specified, then it will not be removed.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400566 parser.add_argument('--staging-dir', type='path', default=None,
567 help=argparse.SUPPRESS)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700568 # Only prepare the staging directory, and skip deploying to the device.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400569 parser.add_argument('--staging-only', action='store_true', default=False,
570 help=argparse.SUPPRESS)
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700571 # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
572 # is used as-is, and not normalized. Used by the Chrome ebuild to skip
573 # fetching the SDK toolchain.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400574 parser.add_argument('--strip-bin', default=None, help=argparse.SUPPRESS)
Satoru Takabayashif2893002017-06-20 14:52:48 +0900575 parser.add_argument('--compress', action='store', default='auto',
576 choices=('always', 'never', 'auto'),
577 help='Choose the data compression behavior. Default '
578 'is set to "auto", that disables compression if '
579 'the target device has a gigabit ethernet port.')
Ryan Cuie535b172012-10-19 18:25:03 -0700580 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700581
Ryan Cuie535b172012-10-19 18:25:03 -0700582
583def _ParseCommandLine(argv):
584 """Parse args, and run environment-independent checks."""
585 parser = _CreateParser()
Mike Frysingerc3061a62015-06-04 04:16:18 -0400586 options = parser.parse_args(argv)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700587
Ryan Cuia56a71e2012-10-18 18:40:35 -0700588 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
589 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800590 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700591 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
592 parser.error('Cannot specify both --build_dir and '
593 '--gs-path/--local-pkg-patch')
Avery Musbach3edff0e2020-03-27 13:35:53 -0700594 if not options.board:
595 parser.error('--board is required outside of cros chrome_sdk')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700596 if options.gs_path and options.local_pkg_path:
597 parser.error('Cannot specify both --gs-path and --local-pkg-path')
598 if not (options.staging_only or options.to):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700599 parser.error('Need to specify --to')
Steven Bennetts46a84c32016-08-12 15:20:46 -0700600 if options.staging_flags and not options.build_dir:
601 parser.error('--staging-flags require --build-dir to be set.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700602
Steven Bennetts46a84c32016-08-12 15:20:46 -0700603 if options.strict:
604 logging.warning('--strict is deprecated.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700605 if options.gyp_defines:
606 logging.warning('--gyp-defines is deprecated.')
Thiago Goncales12793312013-05-23 11:26:17 -0700607
608 if options.mount or options.mount_dir:
609 if not options.target_dir:
610 options.target_dir = _CHROME_DIR_MOUNT
611 else:
612 if not options.target_dir:
613 options.target_dir = _CHROME_DIR
614
615 if options.mount and not options.mount_dir:
616 options.mount_dir = _CHROME_DIR
617
Mike Frysingerc3061a62015-06-04 04:16:18 -0400618 return options
Ryan Cui3045c5d2012-07-13 18:00:33 -0700619
620
Mike Frysingerc3061a62015-06-04 04:16:18 -0400621def _PostParseCheck(options):
Steve Funge984a532013-11-25 17:09:25 -0800622 """Perform some usage validation (after we've parsed the arguments).
Ryan Cui3045c5d2012-07-13 18:00:33 -0700623
624 Args:
Mike Frysinger5fe3f222016-09-01 00:14:16 -0400625 options: The options object returned by the cli parser.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700626 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700627 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
628 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
629
Steven Bennetts368c3e52016-09-23 13:05:21 -0700630 if not options.gn_args:
631 gn_env = os.getenv('GN_ARGS')
632 if gn_env is not None:
633 options.gn_args = gn_helpers.FromGNArgs(gn_env)
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800634 logging.debug('GN_ARGS taken from environment: %s', options.gn_args)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700635
Steven Bennetts60600462016-05-12 10:40:20 -0700636 if not options.staging_flags:
637 use_env = os.getenv('USE')
638 if use_env is not None:
639 options.staging_flags = ' '.join(set(use_env.split()).intersection(
640 chrome_util.STAGING_FLAGS))
641 logging.info('Staging flags taken from USE in environment: %s',
642 options.staging_flags)
643
Ryan Cuia56a71e2012-10-18 18:40:35 -0700644
Ryan Cui504db722013-01-22 11:48:01 -0800645def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700646 """Get the chrome prebuilt tarball from GS.
647
Mike Frysinger02e1e072013-11-10 22:11:34 -0500648 Returns:
649 Path to the fetched chrome tarball.
Ryan Cuia56a71e2012-10-18 18:40:35 -0700650 """
David James9374aac2013-10-08 16:00:17 -0700651 gs_ctx = gs.GSContext(cache_dir=cache_dir, init_boto=True)
Mike Frysinger7ad4fd62014-02-11 16:53:56 -0500652 files = gs_ctx.LS(gs_path)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800653 files = [found for found in files if
654 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
655 if not files:
656 raise Exception('No chrome package found at %s' % gs_path)
657 elif len(files) > 1:
658 # - Users should provide us with a direct link to either a stripped or
659 # unstripped chrome package.
660 # - In the case of being provided with an archive directory, where both
661 # stripped and unstripped chrome available, use the stripped chrome
662 # package.
663 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
664 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
665 files = [f for f in files if not 'unstripped' in f]
666 assert len(files) == 1
667 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800668
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800669 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800670 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800671 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
672 chrome_path = os.path.join(tempdir, filename)
673 assert os.path.exists(chrome_path)
674 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700675
676
Ryan Cuif890a3e2013-03-07 18:57:06 -0800677@contextlib.contextmanager
678def _StripBinContext(options):
679 if not options.dostrip:
680 yield None
681 elif options.strip_bin:
682 yield options.strip_bin
683 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800684 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800685 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
Aviv Keshet1c986f32014-04-24 13:20:49 -0700686 with sdk.Prepare(components=components, target_tc=options.target_tc,
687 toolchain_url=options.toolchain_url) as ctx:
Ryan Cui686ec052013-02-12 16:39:41 -0800688 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
689 constants.CHROME_ENV_FILE)
690 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
691 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
692 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800693 yield strip_bin
694
695
Steve Funge984a532013-11-25 17:09:25 -0800696def _PrepareStagingDir(options, tempdir, staging_dir, copy_paths=None,
697 chrome_dir=_CHROME_DIR):
Ryan Cuif890a3e2013-03-07 18:57:06 -0800698 """Place the necessary files in the staging directory.
699
700 The staging directory is the directory used to rsync the build artifacts over
701 to the device. Only the necessary Chrome build artifacts are put into the
702 staging directory.
703 """
Ryan Cui5866be02013-03-18 14:12:00 -0700704 osutils.SafeMakedirs(staging_dir)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400705 os.chmod(staging_dir, 0o755)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800706 if options.build_dir:
707 with _StripBinContext(options) as strip_bin:
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700708 strip_flags = (None if options.strip_flags is None else
709 shlex.split(options.strip_flags))
Ryan Cui686ec052013-02-12 16:39:41 -0800710 chrome_util.StageChromeFromBuildDir(
Steven Bennetts46a84c32016-08-12 15:20:46 -0700711 staging_dir, options.build_dir, strip_bin,
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700712 sloppy=options.sloppy, gn_args=options.gn_args,
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700713 staging_flags=options.staging_flags,
Steve Funge984a532013-11-25 17:09:25 -0800714 strip_flags=strip_flags, copy_paths=copy_paths)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700715 else:
716 pkg_path = options.local_pkg_path
717 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800718 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
719 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700720
721 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800722 logging.info('Extracting %s...', pkg_path)
Ryan Cui5866be02013-03-18 14:12:00 -0700723 # Extract only the ./opt/google/chrome contents, directly into the staging
724 # dir, collapsing the directory hierarchy.
Steve Funge984a532013-11-25 17:09:25 -0800725 if pkg_path[-4:] == '.zip':
Mike Frysinger45602c72019-09-22 02:15:11 -0400726 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800727 ['unzip', '-X', pkg_path, _ANDROID_DIR_EXTRACT_PATH, '-d',
728 staging_dir])
729 for filename in glob.glob(os.path.join(staging_dir, 'system/chrome/*')):
730 shutil.move(filename, staging_dir)
731 osutils.RmDir(os.path.join(staging_dir, 'system'), ignore_missing=True)
732 else:
Mike Frysinger45602c72019-09-22 02:15:11 -0400733 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800734 ['tar', '--strip-components', '4', '--extract',
735 '--preserve-permissions', '--file', pkg_path, '.%s' % chrome_dir],
736 cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700737
Ryan Cui71aa8de2013-04-19 16:12:55 -0700738
Ryan Cui3045c5d2012-07-13 18:00:33 -0700739def main(argv):
Mike Frysingerc3061a62015-06-04 04:16:18 -0400740 options = _ParseCommandLine(argv)
741 _PostParseCheck(options)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700742
Aviv Keshet01a82e92017-03-02 17:39:59 -0800743 with osutils.TempDir(set_global=True) as tempdir:
744 staging_dir = options.staging_dir
745 if not staging_dir:
746 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700747
Aviv Keshet01a82e92017-03-02 17:39:59 -0800748 deploy = DeployChrome(options, tempdir, staging_dir)
749 try:
750 deploy.Perform()
751 except failures_lib.StepFailure as ex:
752 raise SystemExit(str(ex).strip())
753 deploy.Cleanup()