blob: 4a286e01fcb17dc878c7f02036b6dcb93f6ceb13 [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'
David James2cb34002013-03-01 18:42:40 -080067
David Haddock3151d912017-10-24 03:50:32 +000068_UMOUNT_DIR_IF_MOUNTPOINT_CMD = (
69 'if mountpoint -q %(dir)s; then umount %(dir)s; fi')
70_BIND_TO_FINAL_DIR_CMD = 'mount --rbind %s %s'
71_SET_MOUNT_FLAGS_CMD = 'mount -o remount,exec,suid %s'
Anushruth8d797672019-10-17 12:22:31 -070072_MKDIR_P_CMD = 'mkdir -p --mode 0775 %s'
David Haddock3151d912017-10-24 03:50:32 +000073
74DF_COMMAND = 'df -k %s'
75
Mike Frysingere65f3752014-12-08 00:46:39 -050076
Ryan Cui3045c5d2012-07-13 18:00:33 -070077def _UrlBaseName(url):
78 """Return the last component of the URL."""
79 return url.rstrip('/').rpartition('/')[-1]
80
81
Yu-Ju Hongc54d3342014-05-14 12:42:06 -070082class DeployFailure(failures_lib.StepFailure):
David James88e6f032013-03-02 08:13:20 -080083 """Raised whenever the deploy fails."""
84
85
Ryan Cui7193a7e2013-04-26 14:15:19 -070086DeviceInfo = collections.namedtuple(
87 'DeviceInfo', ['target_dir_size', 'target_fs_free'])
88
89
Ryan Cui3045c5d2012-07-13 18:00:33 -070090class DeployChrome(object):
91 """Wraps the core deployment functionality."""
Mike Frysingere65f3752014-12-08 00:46:39 -050092
Ryan Cuia56a71e2012-10-18 18:40:35 -070093 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -070094 """Initialize the class.
95
Mike Frysinger02e1e072013-11-10 22:11:34 -050096 Args:
Mike Frysingerc3061a62015-06-04 04:16:18 -040097 options: options object.
Ryan Cuie535b172012-10-19 18:25:03 -070098 tempdir: Scratch space for the class. Caller has responsibility to clean
99 it up.
Steve Funge984a532013-11-25 17:09:25 -0800100 staging_dir: Directory to stage the files to.
Ryan Cuie535b172012-10-19 18:25:03 -0700101 """
Ryan Cui3045c5d2012-07-13 18:00:33 -0700102 self.tempdir = tempdir
103 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -0700104 self.staging_dir = staging_dir
Robert Flack1dc7ea82014-11-26 13:50:24 -0500105 if not self.options.staging_only:
Avery Musbach3edff0e2020-03-27 13:35:53 -0700106 self.device = remote.ChromiumOSDevice(options.to, port=options.port,
107 ping=options.ping,
108 private_key=options.private_key,
109 include_dev_paths=False)
Steven Bennettsca73efa2018-07-10 13:36:56 -0700110 self._root_dir_is_still_readonly = multiprocessing.Event()
Steven Bennetts5a7c72d2016-10-17 20:04:46 +0000111
Sadrul Habib Chowdhury977aef72016-11-21 16:30:37 -0500112 self.copy_paths = chrome_util.GetCopyPaths('chrome')
Steve Funge984a532013-11-25 17:09:25 -0800113 self.chrome_dir = _CHROME_DIR
114
Ryan Cui7193a7e2013-04-26 14:15:19 -0700115 def _GetRemoteMountFree(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400116 result = self.device.run(DF_COMMAND % remote_dir)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700117 line = result.output.splitlines()[1]
Steve Funge984a532013-11-25 17:09:25 -0800118 value = line.split()[3]
119 multipliers = {
120 'G': 1024 * 1024 * 1024,
121 'M': 1024 * 1024,
122 'K': 1024,
123 }
124 return int(value.rstrip('GMK')) * multipliers.get(value[-1], 1)
Ryan Cui7193a7e2013-04-26 14:15:19 -0700125
126 def _GetRemoteDirSize(self, remote_dir):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400127 result = self.device.run('du -ks %s' % remote_dir,
128 capture_output=True, encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700129 return int(result.output.split()[0])
130
131 def _GetStagingDirSize(self):
Mike Frysinger45602c72019-09-22 02:15:11 -0400132 result = cros_build_lib.dbg_run(['du', '-ks', self.staging_dir],
Mike Frysinger0282d222019-12-17 17:15:48 -0500133 stdout=True, capture_output=True,
Mike Frysingerb88d5fb2019-10-23 00:38:45 -0400134 encoding='utf-8')
Ryan Cui7193a7e2013-04-26 14:15:19 -0700135 return int(result.output.split()[0])
136
Ryan Cui3045c5d2012-07-13 18:00:33 -0700137 def _ChromeFileInUse(self):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400138 result = self.device.run(LSOF_COMMAND_CHROME % (self.options.target_dir,),
139 check=False, capture_output=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700140 return result.returncode == 0
141
Justin TerAvestfac210e2017-04-13 11:39:00 -0600142 def _Reboot(self):
143 # A reboot in developer mode takes a while (and has delays), so the user
144 # will have time to read and act on the USB boot instructions below.
145 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
146 self.device.Reboot()
147
Ryan Cui3045c5d2012-07-13 18:00:33 -0700148 def _DisableRootfsVerification(self):
149 if not self.options.force:
150 logging.error('Detected that the device has rootfs verification enabled.')
151 logging.info('This script can automatically remove the rootfs '
152 'verification, which requires that it reboot the device.')
153 logging.info('Make sure the device is in developer mode!')
154 logging.info('Skip this prompt by specifying --force.')
Ben Chan37d64e52018-10-05 15:35:02 -0700155 if not cros_build_lib.BooleanPrompt('Remove rootfs verification?', False):
Steven Bennettsca73efa2018-07-10 13:36:56 -0700156 return False
Ryan Cui3045c5d2012-07-13 18:00:33 -0700157
158 logging.info('Removing rootfs verification from %s', self.options.to)
159 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
160 # Use --force to bypass the checks.
David Haddock3151d912017-10-24 03:50:32 +0000161 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
162 '--remove_rootfs_verification --force')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700163 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400164 self.device.run(cmd % partition, check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700165
Justin TerAvestfac210e2017-04-13 11:39:00 -0600166 self._Reboot()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700167
David James88e6f032013-03-02 08:13:20 -0800168 # Now that the machine has been rebooted, we need to kill Chrome again.
169 self._KillProcsIfNeeded()
170
171 # Make sure the rootfs is writable now.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500172 self._MountRootfsAsWritable(check=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700173
Steven Bennettsca73efa2018-07-10 13:36:56 -0700174 return True
175
Ryan Cui3045c5d2012-07-13 18:00:33 -0700176 def _CheckUiJobStarted(self):
177 # status output is in the format:
178 # <job_name> <status> ['process' <pid>].
179 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800180 try:
Mike Frysinger3459bf52020-03-31 00:52:11 -0400181 result = self.device.run('status ui', capture_output=True,
182 encoding='utf-8')
Ryan Cuif2d1a582013-02-19 14:08:13 -0800183 except cros_build_lib.RunCommandError as e:
184 if 'Unknown job' in e.result.error:
185 return False
186 else:
187 raise e
188
Ryan Cui3045c5d2012-07-13 18:00:33 -0700189 return result.output.split()[1].split('/')[0] == 'start'
190
191 def _KillProcsIfNeeded(self):
192 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800193 logging.info('Shutting down Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400194 self.device.run('stop ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700195
196 # Developers sometimes run session_manager manually, in which case we'll
197 # need to help shut the chrome processes down.
198 try:
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700199 with timeout_util.Timeout(self.options.process_timeout):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700200 while self._ChromeFileInUse():
201 logging.warning('The chrome binary on the device is in use.')
202 logging.warning('Killing chrome and session_manager processes...\n')
203
Mike Frysinger3459bf52020-03-31 00:52:11 -0400204 self.device.run("pkill 'chrome|session_manager'", check=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700205 # Wait for processes to actually terminate
206 time.sleep(POST_KILL_WAIT)
207 logging.info('Rechecking the chrome binary...')
David James3432acd2013-11-27 10:02:18 -0800208 except timeout_util.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800209 msg = ('Could not kill processes after %s seconds. Please exit any '
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700210 'running chrome processes and try again.'
211 % self.options.process_timeout)
David James88e6f032013-03-02 08:13:20 -0800212 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700213
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500214 def _MountRootfsAsWritable(self, check=False):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800215 """Mounts the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700216
Steven Bennettsca73efa2018-07-10 13:36:56 -0700217 If the command fails and the root dir is not writable then this function
218 sets self._root_dir_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700219
Mike Frysinger02e1e072013-11-10 22:11:34 -0500220 Args:
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500221 check: See remote.RemoteAccess.RemoteSh for details.
David James88e6f032013-03-02 08:13:20 -0800222 """
Yu-Ju Hong7e116ac2014-01-03 17:08:26 -0800223 # TODO: Should migrate to use the remount functions in remote_access.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400224 result = self.device.run(MOUNT_RW_COMMAND, check=check,
225 capture_output=True, encoding='utf-8')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700226 if result.returncode and not self.device.IsDirWritable('/'):
227 self._root_dir_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700228
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800229 def _EnsureTargetDir(self):
230 """Ensures that the target directory exists on the remote device."""
231 target_dir = self.options.target_dir
232 # Any valid /opt directory should already exist so avoid the remote call.
233 if os.path.commonprefix([target_dir, '/opt']) == '/opt':
234 return
Mike Frysinger3459bf52020-03-31 00:52:11 -0400235 self.device.run(['mkdir', '-p', '--mode', '0775', target_dir])
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800236
Ryan Cui7193a7e2013-04-26 14:15:19 -0700237 def _GetDeviceInfo(self):
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800238 """Returns the disk space used and available for the target diectory."""
Ryan Cui7193a7e2013-04-26 14:15:19 -0700239 steps = [
240 functools.partial(self._GetRemoteDirSize, self.options.target_dir),
241 functools.partial(self._GetRemoteMountFree, self.options.target_dir)
242 ]
243 return_values = parallel.RunParallelSteps(steps, return_values=True)
244 return DeviceInfo(*return_values)
245
246 def _CheckDeviceFreeSpace(self, device_info):
247 """See if target device has enough space for Chrome.
248
Mike Frysinger02e1e072013-11-10 22:11:34 -0500249 Args:
Ryan Cui7193a7e2013-04-26 14:15:19 -0700250 device_info: A DeviceInfo named tuple.
251 """
252 effective_free = device_info.target_dir_size + device_info.target_fs_free
253 staging_size = self._GetStagingDirSize()
254 if effective_free < staging_size:
Daniel Erat1ae46382014-08-14 10:23:39 -0700255 raise DeployFailure(
256 'Not enough free space on the device. Required: %s MiB, '
Mike Frysinger93e8ffa2019-07-03 20:24:18 -0400257 'actual: %s MiB.' % (staging_size // 1024, effective_free // 1024))
Ryan Cui7193a7e2013-04-26 14:15:19 -0700258 if device_info.target_fs_free < (100 * 1024):
259 logging.warning('The device has less than 100MB free. deploy_chrome may '
260 'hang during the transfer.')
261
Satoru Takabayashif2893002017-06-20 14:52:48 +0900262 def _ShouldUseCompression(self):
263 """Checks if compression should be used for rsync."""
264 if self.options.compress == 'always':
265 return True
266 elif self.options.compress == 'never':
267 return False
268 elif self.options.compress == 'auto':
269 return not self.device.HasGigabitEthernet()
270
Ryan Cui3045c5d2012-07-13 18:00:33 -0700271 def _Deploy(self):
Pawel Osciak577773a2013-03-05 10:52:12 -0800272 logging.info('Copying Chrome to %s on device...', self.options.target_dir)
Ilja H. Friedel0ab63e12017-03-28 13:29:48 -0700273 # CopyToDevice will fall back to scp if rsync is corrupted on stateful.
274 # This does not work for deploy.
Satoru Takabayashiab380bc2015-01-15 16:00:41 +0900275 if not self.device.HasRsync():
276 raise DeployFailure(
David Pursellcfd58872015-03-19 09:15:48 -0700277 'rsync is not found on the device.\n'
Jorge Lucangeli Obesea3742f2019-02-07 15:29:09 -0500278 'Run dev_install on the device to get rsync installed.')
Robert Flack1dc7ea82014-11-26 13:50:24 -0500279 self.device.CopyToDevice('%s/' % os.path.abspath(self.staging_dir),
280 self.options.target_dir,
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700281 mode='rsync', inplace=True,
Satoru Takabayashif2893002017-06-20 14:52:48 +0900282 compress=self._ShouldUseCompression(),
Steven Bennettsd57a72a2017-03-20 12:54:00 -0700283 debug_level=logging.INFO,
Robert Flack1dc7ea82014-11-26 13:50:24 -0500284 verbose=self.options.verbose)
Ben Pastene5f03b052019-08-12 18:03:24 -0700285
286 # Set the security context on the default Chrome dir if that's where it's
287 # getting deployed, and only on SELinux supported devices.
288 if (self.device.IsSELinuxAvailable() and
289 _CHROME_DIR in (self.options.target_dir, self.options.mount_dir)):
Mike Frysinger3459bf52020-03-31 00:52:11 -0400290 self.device.run(['restorecon', '-R', _CHROME_DIR])
Steve Funge984a532013-11-25 17:09:25 -0800291
292 for p in self.copy_paths:
Steve Funge984a532013-11-25 17:09:25 -0800293 if p.mode:
294 # Set mode if necessary.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400295 self.device.run('chmod %o %s/%s' % (
David Haddock3151d912017-10-24 03:50:32 +0000296 p.mode, self.options.target_dir, p.src if not p.dest else p.dest))
Steve Funge984a532013-11-25 17:09:25 -0800297
Daniel Erat0fce5bf2017-09-28 17:29:23 -0700298 # Send SIGHUP to dbus-daemon to tell it to reload its configs. This won't
299 # pick up major changes (bus type, logging, etc.), but all we care about is
300 # getting the latest policy from /opt/google/chrome/dbus so that Chrome will
301 # be authorized to take ownership of its service names.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400302 self.device.run(DBUS_RELOAD_COMMAND, check=False)
Justin TerAvestfac210e2017-04-13 11:39:00 -0600303
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800304 if self.options.startui:
Steve Funge984a532013-11-25 17:09:25 -0800305 logging.info('Starting UI...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400306 self.device.run('start ui')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700307
David James88e6f032013-03-02 08:13:20 -0800308 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700309 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800310 logging.info('Testing connection to the device...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400311 self.device.run('true')
David James88e6f032013-03-02 08:13:20 -0800312 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700313 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800314 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700315
Avery Musbach3edff0e2020-03-27 13:35:53 -0700316 def _CheckBoard(self):
317 """Check that the Chrome build is targeted for the device board."""
318 if self.options.board == self.device.board:
319 return
320 logging.warning('Device board is %s whereas target board is %s.',
321 self.device.board, self.options.board)
322 if self.options.force:
323 return
324 if not cros_build_lib.BooleanPrompt('Continue despite board mismatch?',
325 False):
326 raise DeployFailure('Aborted.')
327
Steve Funge984a532013-11-25 17:09:25 -0800328 def _CheckDeployType(self):
Steve Fung63d705d2014-03-16 03:14:03 -0700329 if self.options.build_dir:
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700330 def BinaryExists(filename):
331 """Checks if the passed-in file is present in the build directory."""
332 return os.path.exists(os.path.join(self.options.build_dir, filename))
333
Daniel Erat9813f0e2014-11-12 11:00:28 -0700334 # Handle non-Chrome deployments.
335 if not BinaryExists('chrome'):
Daniel Eratf53bd3a2016-12-02 11:28:36 -0700336 if BinaryExists('app_shell'):
Daniel Erat9813f0e2014-11-12 11:00:28 -0700337 self.copy_paths = chrome_util.GetCopyPaths('app_shell')
338
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700339 # TODO(derat): Update _Deploy() and remove this after figuring out how
Daniel Eratf53bd3a2016-12-02 11:28:36 -0700340 # app_shell should be executed.
Daniel Erat3fd6c7a2014-05-02 14:28:31 -0700341 self.options.startui = False
342
David James88e6f032013-03-02 08:13:20 -0800343 def _PrepareStagingDir(self):
Steve Funge984a532013-11-25 17:09:25 -0800344 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir,
345 self.copy_paths, self.chrome_dir)
David James88e6f032013-03-02 08:13:20 -0800346
Thiago Goncales12793312013-05-23 11:26:17 -0700347 def _MountTarget(self):
348 logging.info('Mounting Chrome...')
349
Anushruth8d797672019-10-17 12:22:31 -0700350 # Create directory if does not exist.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400351 self.device.run(_MKDIR_P_CMD % self.options.mount_dir)
Anushruth8d797672019-10-17 12:22:31 -0700352 try:
353 # Umount the existing mount on mount_dir if present first.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400354 self.device.run(_UMOUNT_DIR_IF_MOUNTPOINT_CMD %
355 {'dir': self.options.mount_dir})
Anushruth8d797672019-10-17 12:22:31 -0700356 except cros_build_lib.RunCommandError as e:
357 logging.error('Failed to umount %s', self.options.mount_dir)
358 # If there is a failure, check if some processs is using the mount_dir.
Mike Frysinger3459bf52020-03-31 00:52:11 -0400359 result = self.device.run(LSOF_COMMAND % (self.options.mount_dir,),
360 check=False, capture_output=True,
361 encoding='utf-8')
Anushruth8d797672019-10-17 12:22:31 -0700362 logging.error('lsof %s -->', self.options.mount_dir)
363 logging.error(result.stdout)
364 raise e
365
Mike Frysinger3459bf52020-03-31 00:52:11 -0400366 self.device.run(_BIND_TO_FINAL_DIR_CMD % (self.options.target_dir,
367 self.options.mount_dir))
Anushruth8d797672019-10-17 12:22:31 -0700368
Thiago Goncales12793312013-05-23 11:26:17 -0700369 # Chrome needs partition to have exec and suid flags set
Mike Frysinger3459bf52020-03-31 00:52:11 -0400370 self.device.run(_SET_MOUNT_FLAGS_CMD % (self.options.mount_dir,))
Robert Flack1dc7ea82014-11-26 13:50:24 -0500371
372 def Cleanup(self):
373 """Clean up RemoteDevice."""
374 if not self.options.staging_only:
375 self.device.Cleanup()
Thiago Goncales12793312013-05-23 11:26:17 -0700376
David James88e6f032013-03-02 08:13:20 -0800377 def Perform(self):
Steve Funge984a532013-11-25 17:09:25 -0800378 self._CheckDeployType()
379
David James88e6f032013-03-02 08:13:20 -0800380 # If requested, just do the staging step.
381 if self.options.staging_only:
382 self._PrepareStagingDir()
383 return 0
384
Avery Musbach3edff0e2020-03-27 13:35:53 -0700385 # Check that the build matches the device.
386 self._CheckBoard()
387
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800388 # Ensure that the target directory exists before running parallel steps.
389 self._EnsureTargetDir()
390
David James88e6f032013-03-02 08:13:20 -0800391 # Run setup steps in parallel. If any step fails, RunParallelSteps will
David James48f6b332013-03-03 20:11:18 -0800392 # stop printing output at that point, and halt any running steps.
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800393 logging.info('Preparing device')
Steve Funge984a532013-11-25 17:09:25 -0800394 steps = [self._GetDeviceInfo, self._CheckConnection,
395 self._KillProcsIfNeeded, self._MountRootfsAsWritable,
396 self._PrepareStagingDir]
Ryan Cui7193a7e2013-04-26 14:15:19 -0700397 ret = parallel.RunParallelSteps(steps, halt_on_error=True,
398 return_values=True)
399 self._CheckDeviceFreeSpace(ret[0])
David James88e6f032013-03-02 08:13:20 -0800400
Steven Bennettsca73efa2018-07-10 13:36:56 -0700401 # If the root dir is not writable, try disabling rootfs verification.
402 # (We always do this by default so that developers can write to
403 # /etc/chriome_dev.conf and other directories in the rootfs).
404 if self._root_dir_is_still_readonly.is_set():
405 if self.options.noremove_rootfs_verification:
406 logging.warning('Skipping disable rootfs verification.')
407 elif not self._DisableRootfsVerification():
408 logging.warning('Failed to disable rootfs verification.')
409
410 # If the target dir is still not writable (i.e. the user opted out or the
411 # command failed), abort.
412 if not self.device.IsDirWritable(self.options.target_dir):
413 if self.options.startui:
414 logging.info('Restarting Chrome...')
Mike Frysinger3459bf52020-03-31 00:52:11 -0400415 self.device.run('start ui')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700416 raise DeployFailure('Target location is not writable. Aborting.')
David James88e6f032013-03-02 08:13:20 -0800417
Thiago Goncales12793312013-05-23 11:26:17 -0700418 if self.options.mount_dir is not None:
419 self._MountTarget()
420
David James88e6f032013-03-02 08:13:20 -0800421 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700422 self._Deploy()
423
424
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700425def ValidateStagingFlags(value):
426 """Convert formatted string to dictionary."""
427 return chrome_util.ProcessShellFlags(value)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700428
429
Steven Bennetts368c3e52016-09-23 13:05:21 -0700430def ValidateGnArgs(value):
431 """Convert GN_ARGS-formatted string to dictionary."""
432 return gn_helpers.FromGNArgs(value)
433
434
Ryan Cuie535b172012-10-19 18:25:03 -0700435def _CreateParser():
436 """Create our custom parser."""
Mike Frysingerc3061a62015-06-04 04:16:18 -0400437 parser = commandline.ArgumentParser(description=__doc__, caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700438
Ryan Cuia56a71e2012-10-18 18:40:35 -0700439 # TODO(rcui): Have this use the UI-V2 format of having source and target
440 # device be specified as positional arguments.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400441 parser.add_argument('--force', action='store_true', default=False,
Avery Musbach3edff0e2020-03-27 13:35:53 -0700442 help='Skip all prompts (such as the prompt for disabling '
443 'of rootfs verification). This may result in the '
444 'target machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800445 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
Mike Frysingerc3061a62015-06-04 04:16:18 -0400446 parser.add_argument('--board', default=sdk_board_env,
Mike Frysinger80de5012019-08-01 14:10:53 -0400447 help='The board the Chrome build is targeted for. When '
Mike Frysingerc3061a62015-06-04 04:16:18 -0400448 "in a 'cros chrome-sdk' shell, defaults to the SDK "
Mike Frysinger80de5012019-08-01 14:10:53 -0400449 'board.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400450 parser.add_argument('--build-dir', type='path',
451 help='The directory with Chrome build artifacts to '
452 'deploy from. Typically of format '
453 '<chrome_root>/out/Debug. When this option is used, '
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700454 'the GN_ARGS environment variable must be set.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400455 parser.add_argument('--target-dir', type='path',
456 default=None,
457 help='Target directory on device to deploy Chrome into.')
458 parser.add_argument('-g', '--gs-path', type='gs_path',
459 help='GS path that contains the chrome to deploy.')
Adrian Eldera2c548a2017-11-07 19:01:29 -0500460 parser.add_argument('--private-key', type='path', default=None,
461 help='An ssh private key to use when deploying to '
462 'a CrOS device.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400463 parser.add_argument('--nostartui', action='store_false', dest='startui',
464 default=True,
465 help="Don't restart the ui daemon after deployment.")
466 parser.add_argument('--nostrip', action='store_false', dest='dostrip',
467 default=True,
468 help="Don't strip binaries during deployment. Warning: "
469 'the resulting binaries will be very large!')
470 parser.add_argument('-p', '--port', type=int, default=remote.DEFAULT_SSH_PORT,
471 help='Port of the target device to connect to.')
472 parser.add_argument('-t', '--to',
473 help='The IP address of the CrOS device to deploy to.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400474 parser.add_argument('--mount-dir', type='path', default=None,
475 help='Deploy Chrome in target directory and bind it '
476 'to the directory specified by this flag.'
477 'Any existing mount on this directory will be '
478 'umounted first.')
479 parser.add_argument('--mount', action='store_true', default=False,
480 help='Deploy Chrome to default target directory and bind '
481 'it to the default mount directory.'
482 'Any existing mount on this directory will be '
483 'umounted first.')
Steven Bennettsca73efa2018-07-10 13:36:56 -0700484 parser.add_argument('--noremove-rootfs-verification', action='store_true',
485 default=False, help='Never remove rootfs verification.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700486
Mike Frysingerc3061a62015-06-04 04:16:18 -0400487 group = parser.add_argument_group('Advanced Options')
488 group.add_argument('-l', '--local-pkg-path', type='path',
489 help='Path to local chrome prebuilt package to deploy.')
490 group.add_argument('--sloppy', action='store_true', default=False,
491 help='Ignore when mandatory artifacts are missing.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700492 group.add_argument('--staging-flags', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400493 help=('Extra flags to control staging. Valid flags are - '
494 '%s' % ', '.join(chrome_util.STAGING_FLAGS)))
Steven Bennetts46a84c32016-08-12 15:20:46 -0700495 # TODO(stevenjb): Remove --strict entirely once removed from the ebuild.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400496 group.add_argument('--strict', action='store_true', default=False,
Steven Bennetts46a84c32016-08-12 15:20:46 -0700497 help='Deprecated. Default behavior is "strict". Use '
498 '--sloppy to omit warnings for missing optional '
499 'files.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400500 group.add_argument('--strip-flags', default=None,
501 help="Flags to call the 'strip' binutil tool with. "
Mike Frysinger80de5012019-08-01 14:10:53 -0400502 'Overrides the default arguments.')
Mike Frysingerc3061a62015-06-04 04:16:18 -0400503 group.add_argument('--ping', action='store_true', default=False,
504 help='Ping the device before connection attempt.')
Achuith Bhandarkar2f8352f2017-06-02 12:47:18 -0700505 group.add_argument('--process-timeout', type=int,
506 default=KILL_PROC_MAX_WAIT,
507 help='Timeout for process shutdown.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700508
Mike Frysingerc3061a62015-06-04 04:16:18 -0400509 group = parser.add_argument_group(
510 'Metadata Overrides (Advanced)',
511 description='Provide all of these overrides in order to remove '
512 'dependencies on metadata.json existence.')
513 group.add_argument('--target-tc', action='store', default=None,
514 help='Override target toolchain name, e.g. '
515 'x86_64-cros-linux-gnu')
516 group.add_argument('--toolchain-url', action='store', default=None,
517 help='Override toolchain url format pattern, e.g. '
518 '2014/04/%%(target)s-2014.04.23.220740.tar.xz')
Aviv Keshet1c986f32014-04-24 13:20:49 -0700519
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700520 # DEPRECATED: --gyp-defines is ignored, but retained for backwards
521 # compatibility. TODO(stevenjb): Remove once eliminated from the ebuild.
522 parser.add_argument('--gyp-defines', default=None, type=ValidateStagingFlags,
Mike Frysingerc3061a62015-06-04 04:16:18 -0400523 help=argparse.SUPPRESS)
Steven Bennetts368c3e52016-09-23 13:05:21 -0700524
525 # GN_ARGS (args.gn) used to build Chrome. Influences which files are staged
526 # when --build-dir is set. Defaults to reading from the GN_ARGS env variable.
527 # CURRENLY IGNORED, ADDED FOR FORWARD COMPATABILITY.
528 parser.add_argument('--gn-args', default=None, type=ValidateGnArgs,
529 help=argparse.SUPPRESS)
530
Ryan Cuia56a71e2012-10-18 18:40:35 -0700531 # Path of an empty directory to stage chrome artifacts to. Defaults to a
532 # temporary directory that is removed when the script finishes. If the path
533 # is specified, then it will not be removed.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400534 parser.add_argument('--staging-dir', type='path', default=None,
535 help=argparse.SUPPRESS)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700536 # Only prepare the staging directory, and skip deploying to the device.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400537 parser.add_argument('--staging-only', action='store_true', default=False,
538 help=argparse.SUPPRESS)
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700539 # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
540 # is used as-is, and not normalized. Used by the Chrome ebuild to skip
541 # fetching the SDK toolchain.
Mike Frysingerc3061a62015-06-04 04:16:18 -0400542 parser.add_argument('--strip-bin', default=None, help=argparse.SUPPRESS)
Satoru Takabayashif2893002017-06-20 14:52:48 +0900543 parser.add_argument('--compress', action='store', default='auto',
544 choices=('always', 'never', 'auto'),
545 help='Choose the data compression behavior. Default '
546 'is set to "auto", that disables compression if '
547 'the target device has a gigabit ethernet port.')
Ryan Cuie535b172012-10-19 18:25:03 -0700548 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700549
Ryan Cuie535b172012-10-19 18:25:03 -0700550
551def _ParseCommandLine(argv):
552 """Parse args, and run environment-independent checks."""
553 parser = _CreateParser()
Mike Frysingerc3061a62015-06-04 04:16:18 -0400554 options = parser.parse_args(argv)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700555
Ryan Cuia56a71e2012-10-18 18:40:35 -0700556 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
557 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800558 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700559 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
560 parser.error('Cannot specify both --build_dir and '
561 '--gs-path/--local-pkg-patch')
Avery Musbach3edff0e2020-03-27 13:35:53 -0700562 if not options.board:
563 parser.error('--board is required outside of cros chrome_sdk')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700564 if options.gs_path and options.local_pkg_path:
565 parser.error('Cannot specify both --gs-path and --local-pkg-path')
566 if not (options.staging_only or options.to):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700567 parser.error('Need to specify --to')
Steven Bennetts46a84c32016-08-12 15:20:46 -0700568 if options.staging_flags and not options.build_dir:
569 parser.error('--staging-flags require --build-dir to be set.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700570
Steven Bennetts46a84c32016-08-12 15:20:46 -0700571 if options.strict:
572 logging.warning('--strict is deprecated.')
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700573 if options.gyp_defines:
574 logging.warning('--gyp-defines is deprecated.')
Thiago Goncales12793312013-05-23 11:26:17 -0700575
576 if options.mount or options.mount_dir:
577 if not options.target_dir:
578 options.target_dir = _CHROME_DIR_MOUNT
579 else:
580 if not options.target_dir:
581 options.target_dir = _CHROME_DIR
582
583 if options.mount and not options.mount_dir:
584 options.mount_dir = _CHROME_DIR
585
Mike Frysingerc3061a62015-06-04 04:16:18 -0400586 return options
Ryan Cui3045c5d2012-07-13 18:00:33 -0700587
588
Mike Frysingerc3061a62015-06-04 04:16:18 -0400589def _PostParseCheck(options):
Steve Funge984a532013-11-25 17:09:25 -0800590 """Perform some usage validation (after we've parsed the arguments).
Ryan Cui3045c5d2012-07-13 18:00:33 -0700591
592 Args:
Mike Frysinger5fe3f222016-09-01 00:14:16 -0400593 options: The options object returned by the cli parser.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700594 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700595 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
596 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
597
Steven Bennetts368c3e52016-09-23 13:05:21 -0700598 if not options.gn_args:
599 gn_env = os.getenv('GN_ARGS')
600 if gn_env is not None:
601 options.gn_args = gn_helpers.FromGNArgs(gn_env)
Steven Bennetts2ae83c72017-12-04 16:34:24 -0800602 logging.debug('GN_ARGS taken from environment: %s', options.gn_args)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700603
Steven Bennetts60600462016-05-12 10:40:20 -0700604 if not options.staging_flags:
605 use_env = os.getenv('USE')
606 if use_env is not None:
607 options.staging_flags = ' '.join(set(use_env.split()).intersection(
608 chrome_util.STAGING_FLAGS))
609 logging.info('Staging flags taken from USE in environment: %s',
610 options.staging_flags)
611
Ryan Cuia56a71e2012-10-18 18:40:35 -0700612
Ryan Cui504db722013-01-22 11:48:01 -0800613def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700614 """Get the chrome prebuilt tarball from GS.
615
Mike Frysinger02e1e072013-11-10 22:11:34 -0500616 Returns:
617 Path to the fetched chrome tarball.
Ryan Cuia56a71e2012-10-18 18:40:35 -0700618 """
David James9374aac2013-10-08 16:00:17 -0700619 gs_ctx = gs.GSContext(cache_dir=cache_dir, init_boto=True)
Mike Frysinger7ad4fd62014-02-11 16:53:56 -0500620 files = gs_ctx.LS(gs_path)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800621 files = [found for found in files if
622 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
623 if not files:
624 raise Exception('No chrome package found at %s' % gs_path)
625 elif len(files) > 1:
626 # - Users should provide us with a direct link to either a stripped or
627 # unstripped chrome package.
628 # - In the case of being provided with an archive directory, where both
629 # stripped and unstripped chrome available, use the stripped chrome
630 # package.
631 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
632 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
633 files = [f for f in files if not 'unstripped' in f]
634 assert len(files) == 1
635 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800636
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800637 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800638 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800639 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
640 chrome_path = os.path.join(tempdir, filename)
641 assert os.path.exists(chrome_path)
642 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700643
644
Ryan Cuif890a3e2013-03-07 18:57:06 -0800645@contextlib.contextmanager
646def _StripBinContext(options):
647 if not options.dostrip:
648 yield None
649 elif options.strip_bin:
650 yield options.strip_bin
651 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800652 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800653 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
Aviv Keshet1c986f32014-04-24 13:20:49 -0700654 with sdk.Prepare(components=components, target_tc=options.target_tc,
655 toolchain_url=options.toolchain_url) as ctx:
Ryan Cui686ec052013-02-12 16:39:41 -0800656 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
657 constants.CHROME_ENV_FILE)
658 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
659 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
660 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800661 yield strip_bin
662
663
Steve Funge984a532013-11-25 17:09:25 -0800664def _PrepareStagingDir(options, tempdir, staging_dir, copy_paths=None,
665 chrome_dir=_CHROME_DIR):
Ryan Cuif890a3e2013-03-07 18:57:06 -0800666 """Place the necessary files in the staging directory.
667
668 The staging directory is the directory used to rsync the build artifacts over
669 to the device. Only the necessary Chrome build artifacts are put into the
670 staging directory.
671 """
Ryan Cui5866be02013-03-18 14:12:00 -0700672 osutils.SafeMakedirs(staging_dir)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400673 os.chmod(staging_dir, 0o755)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800674 if options.build_dir:
675 with _StripBinContext(options) as strip_bin:
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700676 strip_flags = (None if options.strip_flags is None else
677 shlex.split(options.strip_flags))
Ryan Cui686ec052013-02-12 16:39:41 -0800678 chrome_util.StageChromeFromBuildDir(
Steven Bennetts46a84c32016-08-12 15:20:46 -0700679 staging_dir, options.build_dir, strip_bin,
Steven Bennettsda8d9f02016-09-23 13:25:45 -0700680 sloppy=options.sloppy, gn_args=options.gn_args,
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700681 staging_flags=options.staging_flags,
Steve Funge984a532013-11-25 17:09:25 -0800682 strip_flags=strip_flags, copy_paths=copy_paths)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700683 else:
684 pkg_path = options.local_pkg_path
685 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800686 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
687 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700688
689 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800690 logging.info('Extracting %s...', pkg_path)
Ryan Cui5866be02013-03-18 14:12:00 -0700691 # Extract only the ./opt/google/chrome contents, directly into the staging
692 # dir, collapsing the directory hierarchy.
Steve Funge984a532013-11-25 17:09:25 -0800693 if pkg_path[-4:] == '.zip':
Mike Frysinger45602c72019-09-22 02:15:11 -0400694 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800695 ['unzip', '-X', pkg_path, _ANDROID_DIR_EXTRACT_PATH, '-d',
696 staging_dir])
697 for filename in glob.glob(os.path.join(staging_dir, 'system/chrome/*')):
698 shutil.move(filename, staging_dir)
699 osutils.RmDir(os.path.join(staging_dir, 'system'), ignore_missing=True)
700 else:
Mike Frysinger45602c72019-09-22 02:15:11 -0400701 cros_build_lib.dbg_run(
Steve Funge984a532013-11-25 17:09:25 -0800702 ['tar', '--strip-components', '4', '--extract',
703 '--preserve-permissions', '--file', pkg_path, '.%s' % chrome_dir],
704 cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700705
Ryan Cui71aa8de2013-04-19 16:12:55 -0700706
Ryan Cui3045c5d2012-07-13 18:00:33 -0700707def main(argv):
Mike Frysingerc3061a62015-06-04 04:16:18 -0400708 options = _ParseCommandLine(argv)
709 _PostParseCheck(options)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700710
Mike Frysinger3459bf52020-03-31 00:52:11 -0400711 # Set cros_build_lib debug level to hide run spew.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700712 if options.verbose:
Ryan Cuia56a71e2012-10-18 18:40:35 -0700713 logging.getLogger().setLevel(logging.DEBUG)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700714 else:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800715 logging.getLogger().setLevel(logging.INFO)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700716
Aviv Keshet01a82e92017-03-02 17:39:59 -0800717 with osutils.TempDir(set_global=True) as tempdir:
718 staging_dir = options.staging_dir
719 if not staging_dir:
720 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700721
Aviv Keshet01a82e92017-03-02 17:39:59 -0800722 deploy = DeployChrome(options, tempdir, staging_dir)
723 try:
724 deploy.Perform()
725 except failures_lib.StepFailure as ex:
726 raise SystemExit(str(ex).strip())
727 deploy.Cleanup()