blob: 2caf7712517af789a5397bf0d4e67b493295db8b [file] [log] [blame]
Ryan Cui3045c5d2012-07-13 18:00:33 -07001#!/usr/bin/python
2# 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
Ryan Cuia56a71e2012-10-18 18:40:35 -07006
7"""
8Script that deploys a Chrome build to a device.
9
10The script supports deploying Chrome from these sources:
11
121. A local build output directory, such as chromium/src/out/[Debug|Release].
132. A Chrome tarball uploaded by a trybot/official-builder to GoogleStorage.
143. A Chrome tarball existing locally.
15
16The script copies the necessary contents of the source location (tarball or
17build directory) and rsyncs the contents of the staging directory onto your
18device's rootfs.
19"""
Ryan Cui3045c5d2012-07-13 18:00:33 -070020
Ryan Cuif890a3e2013-03-07 18:57:06 -080021import contextlib
Ryan Cui3045c5d2012-07-13 18:00:33 -070022import logging
David James88e6f032013-03-02 08:13:20 -080023import multiprocessing
Ryan Cui3045c5d2012-07-13 18:00:33 -070024import os
Ryan Cuia56a71e2012-10-18 18:40:35 -070025import optparse
Ryan Cui5b7c2ed2013-03-18 18:45:46 -070026import shlex
Ryan Cui3045c5d2012-07-13 18:00:33 -070027import time
Ryan Cui3045c5d2012-07-13 18:00:33 -070028
Ryan Cuia56a71e2012-10-18 18:40:35 -070029
David James629febb2012-11-25 13:07:34 -080030from chromite.buildbot import constants
David James88e6f032013-03-02 08:13:20 -080031from chromite.buildbot import cbuildbot_results as results_lib
Ryan Cui686ec052013-02-12 16:39:41 -080032from chromite.cros.commands import cros_chrome_sdk
Ryan Cuia56a71e2012-10-18 18:40:35 -070033from chromite.lib import chrome_util
Ryan Cui3045c5d2012-07-13 18:00:33 -070034from chromite.lib import cros_build_lib
Ryan Cuie535b172012-10-19 18:25:03 -070035from chromite.lib import commandline
Ryan Cui777ff422012-12-07 13:12:54 -080036from chromite.lib import gs
Ryan Cui3045c5d2012-07-13 18:00:33 -070037from chromite.lib import osutils
David James88e6f032013-03-02 08:13:20 -080038from chromite.lib import parallel
Ryan Cui3045c5d2012-07-13 18:00:33 -070039from chromite.lib import remote_access as remote
Ryan Cui71aa8de2013-04-19 16:12:55 -070040from chromite.lib import stats
Ryan Cui3045c5d2012-07-13 18:00:33 -070041
42
Ryan Cuia56a71e2012-10-18 18:40:35 -070043_USAGE = "deploy_chrome [--]\n\n %s" % __doc__
44
Ryan Cui3045c5d2012-07-13 18:00:33 -070045KERNEL_A_PARTITION = 2
46KERNEL_B_PARTITION = 4
47
48KILL_PROC_MAX_WAIT = 10
49POST_KILL_WAIT = 2
50
Ryan Cuie535b172012-10-19 18:25:03 -070051MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Pawel Osciak577773a2013-03-05 10:52:12 -080052LSOF_COMMAND = 'lsof %s/chrome'
Ryan Cui3045c5d2012-07-13 18:00:33 -070053
David James2cb34002013-03-01 18:42:40 -080054_CHROME_DIR = '/opt/google/chrome'
55
Ryan Cui3045c5d2012-07-13 18:00:33 -070056
Ryan Cui3045c5d2012-07-13 18:00:33 -070057def _UrlBaseName(url):
58 """Return the last component of the URL."""
59 return url.rstrip('/').rpartition('/')[-1]
60
61
David James88e6f032013-03-02 08:13:20 -080062class DeployFailure(results_lib.StepFailure):
63 """Raised whenever the deploy fails."""
64
65
Ryan Cui3045c5d2012-07-13 18:00:33 -070066class DeployChrome(object):
67 """Wraps the core deployment functionality."""
Ryan Cuia56a71e2012-10-18 18:40:35 -070068 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -070069 """Initialize the class.
70
71 Arguments:
72 options: Optparse result structure.
73 tempdir: Scratch space for the class. Caller has responsibility to clean
74 it up.
Ryan Cuie535b172012-10-19 18:25:03 -070075 """
Ryan Cui3045c5d2012-07-13 18:00:33 -070076 self.tempdir = tempdir
77 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -070078 self.staging_dir = staging_dir
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070079 self.host = remote.RemoteAccess(options.to, tempdir, port=options.port)
David James88e6f032013-03-02 08:13:20 -080080 self._rootfs_is_still_readonly = multiprocessing.Event()
Ryan Cui3045c5d2012-07-13 18:00:33 -070081
Ryan Cui3045c5d2012-07-13 18:00:33 -070082 def _ChromeFileInUse(self):
Pawel Osciak577773a2013-03-05 10:52:12 -080083 result = self.host.RemoteSh(LSOF_COMMAND % (self.options.target_dir,),
84 error_code_ok=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -070085 return result.returncode == 0
86
87 def _DisableRootfsVerification(self):
88 if not self.options.force:
89 logging.error('Detected that the device has rootfs verification enabled.')
90 logging.info('This script can automatically remove the rootfs '
91 'verification, which requires that it reboot the device.')
92 logging.info('Make sure the device is in developer mode!')
93 logging.info('Skip this prompt by specifying --force.')
Brian Harring521e7242012-11-01 16:57:42 -070094 if not cros_build_lib.BooleanPrompt('Remove roots verification?', False):
David James88e6f032013-03-02 08:13:20 -080095 # Since we stopped Chrome earlier, it's good form to start it up again.
96 if self.options.startui:
97 logging.info('Starting Chrome...')
98 self.host.RemoteSh('start ui')
99 raise DeployFailure('Need rootfs verification to be disabled. '
100 'Aborting.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700101
102 logging.info('Removing rootfs verification from %s', self.options.to)
103 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
104 # Use --force to bypass the checks.
105 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
106 '--remove_rootfs_verification --force')
107 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
108 self.host.RemoteSh(cmd % partition, error_code_ok=True)
109
110 # A reboot in developer mode takes a while (and has delays), so the user
111 # will have time to read and act on the USB boot instructions below.
112 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
113 self.host.RemoteReboot()
114
David James88e6f032013-03-02 08:13:20 -0800115 # Now that the machine has been rebooted, we need to kill Chrome again.
116 self._KillProcsIfNeeded()
117
118 # Make sure the rootfs is writable now.
119 self._MountRootfsAsWritable(error_code_ok=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700120
121 def _CheckUiJobStarted(self):
122 # status output is in the format:
123 # <job_name> <status> ['process' <pid>].
124 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800125 try:
126 result = self.host.RemoteSh('status ui')
127 except cros_build_lib.RunCommandError as e:
128 if 'Unknown job' in e.result.error:
129 return False
130 else:
131 raise e
132
Ryan Cui3045c5d2012-07-13 18:00:33 -0700133 return result.output.split()[1].split('/')[0] == 'start'
134
135 def _KillProcsIfNeeded(self):
136 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800137 logging.info('Shutting down Chrome...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700138 self.host.RemoteSh('stop ui')
139
140 # Developers sometimes run session_manager manually, in which case we'll
141 # need to help shut the chrome processes down.
142 try:
143 with cros_build_lib.SubCommandTimeout(KILL_PROC_MAX_WAIT):
144 while self._ChromeFileInUse():
145 logging.warning('The chrome binary on the device is in use.')
146 logging.warning('Killing chrome and session_manager processes...\n')
147
148 self.host.RemoteSh("pkill 'chrome|session_manager'",
149 error_code_ok=True)
150 # Wait for processes to actually terminate
151 time.sleep(POST_KILL_WAIT)
152 logging.info('Rechecking the chrome binary...')
153 except cros_build_lib.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800154 msg = ('Could not kill processes after %s seconds. Please exit any '
155 'running chrome processes and try again.' % KILL_PROC_MAX_WAIT)
156 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700157
David James88e6f032013-03-02 08:13:20 -0800158 def _MountRootfsAsWritable(self, error_code_ok=True):
159 """Mount the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700160
David James88e6f032013-03-02 08:13:20 -0800161 If the command fails, and error_code_ok is True, then this function sets
162 self._rootfs_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700163
David James88e6f032013-03-02 08:13:20 -0800164 Arguments:
165 error_code_ok: See remote.RemoteAccess.RemoteSh for details.
166 """
167 result = self.host.RemoteSh(MOUNT_RW_COMMAND, error_code_ok=error_code_ok)
168 if result.returncode:
169 self._rootfs_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700170
171 def _Deploy(self):
Pawel Osciak577773a2013-03-05 10:52:12 -0800172 logging.info('Copying Chrome to %s on device...', self.options.target_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700173 # Show the output (status) for this command.
Pawel Osciak577773a2013-03-05 10:52:12 -0800174 self.host.Rsync('%s/' % os.path.abspath(self.staging_dir),
175 self.options.target_dir,
David Jamesa6e08892013-03-01 13:34:11 -0800176 inplace=True, debug_level=logging.INFO,
177 verbose=self.options.verbose)
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800178 if self.options.startui:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800179 logging.info('Starting Chrome...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700180 self.host.RemoteSh('start ui')
181
David James88e6f032013-03-02 08:13:20 -0800182 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700183 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800184 logging.info('Testing connection to the device...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700185 self.host.RemoteSh('true')
David James88e6f032013-03-02 08:13:20 -0800186 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700187 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800188 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700189
David James88e6f032013-03-02 08:13:20 -0800190 def _PrepareStagingDir(self):
191 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir)
192
193 def Perform(self):
194 # If requested, just do the staging step.
195 if self.options.staging_only:
196 self._PrepareStagingDir()
197 return 0
198
199 # Run setup steps in parallel. If any step fails, RunParallelSteps will
David James48f6b332013-03-03 20:11:18 -0800200 # stop printing output at that point, and halt any running steps.
David James88e6f032013-03-02 08:13:20 -0800201 steps = [self._PrepareStagingDir, self._CheckConnection,
202 self._KillProcsIfNeeded, self._MountRootfsAsWritable]
David James48f6b332013-03-03 20:11:18 -0800203 parallel.RunParallelSteps(steps, halt_on_error=True)
David James88e6f032013-03-02 08:13:20 -0800204
205 # If we failed to mark the rootfs as writable, try disabling rootfs
206 # verification.
207 if self._rootfs_is_still_readonly.is_set():
208 self._DisableRootfsVerification()
209
210 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700211 self._Deploy()
212
213
Ryan Cuia56a71e2012-10-18 18:40:35 -0700214def ValidateGypDefines(_option, _opt, value):
215 """Convert GYP_DEFINES-formatted string to dictionary."""
216 return chrome_util.ProcessGypDefines(value)
217
218
219class CustomOption(commandline.Option):
220 """Subclass Option class to implement path evaluation."""
221 TYPES = commandline.Option.TYPES + ('gyp_defines',)
222 TYPE_CHECKER = commandline.Option.TYPE_CHECKER.copy()
223 TYPE_CHECKER['gyp_defines'] = ValidateGypDefines
224
225
Ryan Cuie535b172012-10-19 18:25:03 -0700226def _CreateParser():
227 """Create our custom parser."""
Ryan Cui504db722013-01-22 11:48:01 -0800228 parser = commandline.OptionParser(usage=_USAGE, option_class=CustomOption,
229 caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700230
Ryan Cuia56a71e2012-10-18 18:40:35 -0700231 # TODO(rcui): Have this use the UI-V2 format of having source and target
232 # device be specified as positional arguments.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700233 parser.add_option('--force', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800234 help='Skip all prompts (i.e., for disabling of rootfs '
235 'verification). This may result in the target '
236 'machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800237 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
238 parser.add_option('--board', default=sdk_board_env,
Ryan Cui686ec052013-02-12 16:39:41 -0800239 help="The board the Chrome build is targeted for. When in "
240 "a 'cros chrome-sdk' shell, defaults to the SDK "
241 "board.")
Ryan Cuia56a71e2012-10-18 18:40:35 -0700242 parser.add_option('--build-dir', type='path',
Ryan Cui686ec052013-02-12 16:39:41 -0800243 help='The directory with Chrome build artifacts to deploy '
244 'from. Typically of format <chrome_root>/out/Debug. '
245 'When this option is used, the GYP_DEFINES '
246 'environment variable must be set.')
Pawel Osciak577773a2013-03-05 10:52:12 -0800247 parser.add_option('--target-dir', type='path',
248 help='Target directory on device to deploy Chrome into.',
249 default=_CHROME_DIR)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700250 parser.add_option('-g', '--gs-path', type='gs_path',
Ryan Cui686ec052013-02-12 16:39:41 -0800251 help='GS path that contains the chrome to deploy.')
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800252 parser.add_option('--nostartui', action='store_false', dest='startui',
253 default=True,
254 help="Don't restart the ui daemon after deployment.")
Ryan Cuif890a3e2013-03-07 18:57:06 -0800255 parser.add_option('--nostrip', action='store_false', dest='dostrip',
256 default=True,
257 help="Don't strip binaries during deployment. Warning: "
258 "the resulting binaries will be very large!")
Ryan Cui3045c5d2012-07-13 18:00:33 -0700259 parser.add_option('-p', '--port', type=int, default=remote.DEFAULT_SSH_PORT,
Ryan Cui686ec052013-02-12 16:39:41 -0800260 help='Port of the target device to connect to.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700261 parser.add_option('-t', '--to',
Ryan Cui686ec052013-02-12 16:39:41 -0800262 help='The IP address of the CrOS device to deploy to.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700263 parser.add_option('-v', '--verbose', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800264 help='Show more debug output.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700265
266 group = optparse.OptionGroup(parser, 'Advanced Options')
267 group.add_option('-l', '--local-pkg-path', type='path',
Ryan Cui686ec052013-02-12 16:39:41 -0800268 help='Path to local chrome prebuilt package to deploy.')
David Jamesa6e08892013-03-01 13:34:11 -0800269 group.add_option('--sloppy', action='store_true', default=False,
270 help='Ignore when mandatory artifacts are missing.')
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700271 group.add_option('--staging-flags', default=None, type='gyp_defines',
272 help='Extra flags to control staging. Valid flags are - %s'
273 % ', '.join(chrome_util.STAGING_FLAGS))
Ryan Cuief91e702013-02-04 12:06:36 -0800274 group.add_option('--strict', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800275 help='Stage artifacts based on the GYP_DEFINES environment '
David Jamesa6e08892013-03-01 13:34:11 -0800276 'variable and --staging-flags, if set. Enforce that '
277 'all optional artifacts are deployed.')
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700278 group.add_option('--strip-flags', default=None,
279 help="Flags to call the 'strip' binutil tool with. "
280 "Overrides the default arguments.")
Ryan Cuief91e702013-02-04 12:06:36 -0800281
Ryan Cuia56a71e2012-10-18 18:40:35 -0700282 parser.add_option_group(group)
283
Ryan Cuif890a3e2013-03-07 18:57:06 -0800284 # GYP_DEFINES that Chrome was built with. Influences which files are staged
285 # when --build-dir is set. Defaults to reading from the GYP_DEFINES
286 # enviroment variable.
287 parser.add_option('--gyp-defines', default=None, type='gyp_defines',
288 help=optparse.SUPPRESS_HELP)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700289 # Path of an empty directory to stage chrome artifacts to. Defaults to a
290 # temporary directory that is removed when the script finishes. If the path
291 # is specified, then it will not be removed.
292 parser.add_option('--staging-dir', type='path', default=None,
293 help=optparse.SUPPRESS_HELP)
294 # Only prepare the staging directory, and skip deploying to the device.
295 parser.add_option('--staging-only', action='store_true', default=False,
296 help=optparse.SUPPRESS_HELP)
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700297 # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
298 # is used as-is, and not normalized. Used by the Chrome ebuild to skip
299 # fetching the SDK toolchain.
300 parser.add_option('--strip-bin', default=None, help=optparse.SUPPRESS_HELP)
Ryan Cuie535b172012-10-19 18:25:03 -0700301 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700302
Ryan Cuie535b172012-10-19 18:25:03 -0700303
304def _ParseCommandLine(argv):
305 """Parse args, and run environment-independent checks."""
306 parser = _CreateParser()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700307 (options, args) = parser.parse_args(argv)
308
Ryan Cuia56a71e2012-10-18 18:40:35 -0700309 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
310 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800311 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700312 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
313 parser.error('Cannot specify both --build_dir and '
314 '--gs-path/--local-pkg-patch')
Ryan Cui686ec052013-02-12 16:39:41 -0800315 if options.build_dir and not options.board:
316 parser.error('--board is required when --build-dir is specified.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700317 if options.gs_path and options.local_pkg_path:
318 parser.error('Cannot specify both --gs-path and --local-pkg-path')
319 if not (options.staging_only or options.to):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700320 parser.error('Need to specify --to')
Ryan Cuief91e702013-02-04 12:06:36 -0800321 if (options.strict or options.staging_flags) and not options.build_dir:
322 parser.error('--strict and --staging-flags require --build-dir to be '
323 'set.')
324 if options.staging_flags and not options.strict:
David Jamesa6e08892013-03-01 13:34:11 -0800325 parser.error('--staging-flags requires --strict to be set.')
326 if options.sloppy and options.strict:
327 parser.error('Cannot specify both --strict and --sloppy.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700328 return options, args
329
330
Ryan Cuie535b172012-10-19 18:25:03 -0700331def _PostParseCheck(options, _args):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700332 """Perform some usage validation (after we've parsed the arguments
333
334 Args:
335 options/args: The options/args object returned by optparse
336 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700337 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
338 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
339
Ryan Cuib623e7b2013-03-14 12:54:11 -0700340 if not options.gyp_defines:
Ryan Cuia56a71e2012-10-18 18:40:35 -0700341 gyp_env = os.getenv('GYP_DEFINES', None)
342 if gyp_env is not None:
343 options.gyp_defines = chrome_util.ProcessGypDefines(gyp_env)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700344 logging.debug('GYP_DEFINES taken from environment: %s',
Ryan Cuia56a71e2012-10-18 18:40:35 -0700345 options.gyp_defines)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700346
347 if options.strict and not options.gyp_defines:
348 cros_build_lib.Die('When --strict is set, the GYP_DEFINES environment '
Ryan Cui686ec052013-02-12 16:39:41 -0800349 'variable must be set.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700350
Ryan Cuib623e7b2013-03-14 12:54:11 -0700351 if (options.gyp_defines and
352 options.gyp_defines.get('component') == 'shared_library'):
353 cros_build_lib.Warning(
354 "Detected 'component=shared_library' in GYP_DEFINES. "
355 "deploy_chrome currently doesn't work well with component build. "
356 "See crosbug.com/196317. Use at your own risk!")
357
Ryan Cuia56a71e2012-10-18 18:40:35 -0700358
Ryan Cui504db722013-01-22 11:48:01 -0800359def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700360 """Get the chrome prebuilt tarball from GS.
361
362 Returns: Path to the fetched chrome tarball.
363 """
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800364 gs_ctx = gs.GSContext.Cached(cache_dir, init_boto=True)
365 files = gs_ctx.LS(gs_path).output.splitlines()
366 files = [found for found in files if
367 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
368 if not files:
369 raise Exception('No chrome package found at %s' % gs_path)
370 elif len(files) > 1:
371 # - Users should provide us with a direct link to either a stripped or
372 # unstripped chrome package.
373 # - In the case of being provided with an archive directory, where both
374 # stripped and unstripped chrome available, use the stripped chrome
375 # package.
376 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
377 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
378 files = [f for f in files if not 'unstripped' in f]
379 assert len(files) == 1
380 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800381
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800382 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800383 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800384 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
385 chrome_path = os.path.join(tempdir, filename)
386 assert os.path.exists(chrome_path)
387 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700388
389
Ryan Cuif890a3e2013-03-07 18:57:06 -0800390@contextlib.contextmanager
391def _StripBinContext(options):
392 if not options.dostrip:
393 yield None
394 elif options.strip_bin:
395 yield options.strip_bin
396 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800397 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800398 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
399 with sdk.Prepare(components=components) as ctx:
400 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
401 constants.CHROME_ENV_FILE)
402 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
403 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
404 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800405 yield strip_bin
406
407
408def _PrepareStagingDir(options, tempdir, staging_dir):
409 """Place the necessary files in the staging directory.
410
411 The staging directory is the directory used to rsync the build artifacts over
412 to the device. Only the necessary Chrome build artifacts are put into the
413 staging directory.
414 """
Ryan Cui5866be02013-03-18 14:12:00 -0700415 osutils.SafeMakedirs(staging_dir)
416 os.chmod(staging_dir, 0755)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800417 if options.build_dir:
418 with _StripBinContext(options) as strip_bin:
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700419 strip_flags = (None if options.strip_flags is None else
420 shlex.split(options.strip_flags))
Ryan Cui686ec052013-02-12 16:39:41 -0800421 chrome_util.StageChromeFromBuildDir(
422 staging_dir, options.build_dir, strip_bin, strict=options.strict,
David Jamesa6e08892013-03-01 13:34:11 -0800423 sloppy=options.sloppy, gyp_defines=options.gyp_defines,
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700424 staging_flags=options.staging_flags,
425 strip_flags=strip_flags)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700426 else:
427 pkg_path = options.local_pkg_path
428 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800429 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
430 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700431
432 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800433 logging.info('Extracting %s...', pkg_path)
Ryan Cui5866be02013-03-18 14:12:00 -0700434 # Extract only the ./opt/google/chrome contents, directly into the staging
435 # dir, collapsing the directory hierarchy.
436 cros_build_lib.DebugRunCommand(
437 ['tar', '--strip-components', '4', '--extract',
438 '--preserve-permissions', '--file', pkg_path, '.%s' % _CHROME_DIR],
439 cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700440
Ryan Cui71aa8de2013-04-19 16:12:55 -0700441
Ryan Cui3045c5d2012-07-13 18:00:33 -0700442def main(argv):
443 options, args = _ParseCommandLine(argv)
444 _PostParseCheck(options, args)
445
446 # Set cros_build_lib debug level to hide RunCommand spew.
447 if options.verbose:
Ryan Cuia56a71e2012-10-18 18:40:35 -0700448 logging.getLogger().setLevel(logging.DEBUG)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700449 else:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800450 logging.getLogger().setLevel(logging.INFO)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700451
Ryan Cui71aa8de2013-04-19 16:12:55 -0700452 with stats.UploadContext() as queue:
453 cmd_stats = stats.Stats(cmd_line=argv, cmd_base='deploy_chrome')
454 queue.put([cmd_stats, stats.StatsUploader.URL, 1])
Ryan Cuia56a71e2012-10-18 18:40:35 -0700455
Ryan Cui71aa8de2013-04-19 16:12:55 -0700456 with osutils.TempDir(set_global=True) as tempdir:
457 staging_dir = options.staging_dir
458 if not staging_dir:
459 staging_dir = os.path.join(tempdir, 'chrome')
460
461 deploy = DeployChrome(options, tempdir, staging_dir)
462 try:
463 deploy.Perform()
464 except results_lib.StepFailure as ex:
465 raise SystemExit(str(ex).strip())