blob: b18c56eaf964ae5d6df93f41f6b12272e5cfd9e2 [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 Cui3045c5d2012-07-13 18:00:33 -070026import time
Ryan Cui3045c5d2012-07-13 18:00:33 -070027
Ryan Cuia56a71e2012-10-18 18:40:35 -070028
David James629febb2012-11-25 13:07:34 -080029from chromite.buildbot import constants
David James88e6f032013-03-02 08:13:20 -080030from chromite.buildbot import cbuildbot_results as results_lib
Ryan Cui686ec052013-02-12 16:39:41 -080031from chromite.cros.commands import cros_chrome_sdk
Ryan Cuia56a71e2012-10-18 18:40:35 -070032from chromite.lib import chrome_util
Ryan Cui3045c5d2012-07-13 18:00:33 -070033from chromite.lib import cros_build_lib
Ryan Cuie535b172012-10-19 18:25:03 -070034from chromite.lib import commandline
Ryan Cui777ff422012-12-07 13:12:54 -080035from chromite.lib import gs
Ryan Cui3045c5d2012-07-13 18:00:33 -070036from chromite.lib import osutils
David James88e6f032013-03-02 08:13:20 -080037from chromite.lib import parallel
Ryan Cui3045c5d2012-07-13 18:00:33 -070038from chromite.lib import remote_access as remote
Ryan Cui3045c5d2012-07-13 18:00:33 -070039
40
Ryan Cuia56a71e2012-10-18 18:40:35 -070041_USAGE = "deploy_chrome [--]\n\n %s" % __doc__
42
Ryan Cui3045c5d2012-07-13 18:00:33 -070043KERNEL_A_PARTITION = 2
44KERNEL_B_PARTITION = 4
45
46KILL_PROC_MAX_WAIT = 10
47POST_KILL_WAIT = 2
48
Ryan Cuie535b172012-10-19 18:25:03 -070049MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Pawel Osciak577773a2013-03-05 10:52:12 -080050LSOF_COMMAND = 'lsof %s/chrome'
Ryan Cui3045c5d2012-07-13 18:00:33 -070051
David James2cb34002013-03-01 18:42:40 -080052_CHROME_DIR = '/opt/google/chrome'
53
Ryan Cui3045c5d2012-07-13 18:00:33 -070054
Ryan Cui3045c5d2012-07-13 18:00:33 -070055def _UrlBaseName(url):
56 """Return the last component of the URL."""
57 return url.rstrip('/').rpartition('/')[-1]
58
59
David James88e6f032013-03-02 08:13:20 -080060class DeployFailure(results_lib.StepFailure):
61 """Raised whenever the deploy fails."""
62
63
Ryan Cui3045c5d2012-07-13 18:00:33 -070064class DeployChrome(object):
65 """Wraps the core deployment functionality."""
Ryan Cuia56a71e2012-10-18 18:40:35 -070066 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -070067 """Initialize the class.
68
69 Arguments:
70 options: Optparse result structure.
71 tempdir: Scratch space for the class. Caller has responsibility to clean
72 it up.
Ryan Cuie535b172012-10-19 18:25:03 -070073 """
Ryan Cui3045c5d2012-07-13 18:00:33 -070074 self.tempdir = tempdir
75 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -070076 self.staging_dir = staging_dir
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070077 self.host = remote.RemoteAccess(options.to, tempdir, port=options.port)
David James88e6f032013-03-02 08:13:20 -080078 self._rootfs_is_still_readonly = multiprocessing.Event()
Ryan Cui3045c5d2012-07-13 18:00:33 -070079
Ryan Cui3045c5d2012-07-13 18:00:33 -070080 def _ChromeFileInUse(self):
Pawel Osciak577773a2013-03-05 10:52:12 -080081 result = self.host.RemoteSh(LSOF_COMMAND % (self.options.target_dir,),
82 error_code_ok=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -070083 return result.returncode == 0
84
85 def _DisableRootfsVerification(self):
86 if not self.options.force:
87 logging.error('Detected that the device has rootfs verification enabled.')
88 logging.info('This script can automatically remove the rootfs '
89 'verification, which requires that it reboot the device.')
90 logging.info('Make sure the device is in developer mode!')
91 logging.info('Skip this prompt by specifying --force.')
Brian Harring521e7242012-11-01 16:57:42 -070092 if not cros_build_lib.BooleanPrompt('Remove roots verification?', False):
David James88e6f032013-03-02 08:13:20 -080093 # Since we stopped Chrome earlier, it's good form to start it up again.
94 if self.options.startui:
95 logging.info('Starting Chrome...')
96 self.host.RemoteSh('start ui')
97 raise DeployFailure('Need rootfs verification to be disabled. '
98 'Aborting.')
Ryan Cui3045c5d2012-07-13 18:00:33 -070099
100 logging.info('Removing rootfs verification from %s', self.options.to)
101 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
102 # Use --force to bypass the checks.
103 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
104 '--remove_rootfs_verification --force')
105 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
106 self.host.RemoteSh(cmd % partition, error_code_ok=True)
107
108 # A reboot in developer mode takes a while (and has delays), so the user
109 # will have time to read and act on the USB boot instructions below.
110 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
111 self.host.RemoteReboot()
112
David James88e6f032013-03-02 08:13:20 -0800113 # Now that the machine has been rebooted, we need to kill Chrome again.
114 self._KillProcsIfNeeded()
115
116 # Make sure the rootfs is writable now.
117 self._MountRootfsAsWritable(error_code_ok=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700118
119 def _CheckUiJobStarted(self):
120 # status output is in the format:
121 # <job_name> <status> ['process' <pid>].
122 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800123 try:
124 result = self.host.RemoteSh('status ui')
125 except cros_build_lib.RunCommandError as e:
126 if 'Unknown job' in e.result.error:
127 return False
128 else:
129 raise e
130
Ryan Cui3045c5d2012-07-13 18:00:33 -0700131 return result.output.split()[1].split('/')[0] == 'start'
132
133 def _KillProcsIfNeeded(self):
134 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800135 logging.info('Shutting down Chrome...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700136 self.host.RemoteSh('stop ui')
137
138 # Developers sometimes run session_manager manually, in which case we'll
139 # need to help shut the chrome processes down.
140 try:
141 with cros_build_lib.SubCommandTimeout(KILL_PROC_MAX_WAIT):
142 while self._ChromeFileInUse():
143 logging.warning('The chrome binary on the device is in use.')
144 logging.warning('Killing chrome and session_manager processes...\n')
145
146 self.host.RemoteSh("pkill 'chrome|session_manager'",
147 error_code_ok=True)
148 # Wait for processes to actually terminate
149 time.sleep(POST_KILL_WAIT)
150 logging.info('Rechecking the chrome binary...')
151 except cros_build_lib.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800152 msg = ('Could not kill processes after %s seconds. Please exit any '
153 'running chrome processes and try again.' % KILL_PROC_MAX_WAIT)
154 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700155
David James88e6f032013-03-02 08:13:20 -0800156 def _MountRootfsAsWritable(self, error_code_ok=True):
157 """Mount the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700158
David James88e6f032013-03-02 08:13:20 -0800159 If the command fails, and error_code_ok is True, then this function sets
160 self._rootfs_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700161
David James88e6f032013-03-02 08:13:20 -0800162 Arguments:
163 error_code_ok: See remote.RemoteAccess.RemoteSh for details.
164 """
165 result = self.host.RemoteSh(MOUNT_RW_COMMAND, error_code_ok=error_code_ok)
166 if result.returncode:
167 self._rootfs_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700168
169 def _Deploy(self):
Pawel Osciak577773a2013-03-05 10:52:12 -0800170 logging.info('Copying Chrome to %s on device...', self.options.target_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700171 # Show the output (status) for this command.
Pawel Osciak577773a2013-03-05 10:52:12 -0800172 self.host.Rsync('%s/' % os.path.abspath(self.staging_dir),
173 self.options.target_dir,
David Jamesa6e08892013-03-01 13:34:11 -0800174 inplace=True, debug_level=logging.INFO,
175 verbose=self.options.verbose)
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800176 if self.options.startui:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800177 logging.info('Starting Chrome...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700178 self.host.RemoteSh('start ui')
179
David James88e6f032013-03-02 08:13:20 -0800180 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700181 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800182 logging.info('Testing connection to the device...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700183 self.host.RemoteSh('true')
David James88e6f032013-03-02 08:13:20 -0800184 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700185 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800186 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700187
David James88e6f032013-03-02 08:13:20 -0800188 def _PrepareStagingDir(self):
189 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir)
190
191 def Perform(self):
192 # If requested, just do the staging step.
193 if self.options.staging_only:
194 self._PrepareStagingDir()
195 return 0
196
197 # Run setup steps in parallel. If any step fails, RunParallelSteps will
David James48f6b332013-03-03 20:11:18 -0800198 # stop printing output at that point, and halt any running steps.
David James88e6f032013-03-02 08:13:20 -0800199 steps = [self._PrepareStagingDir, self._CheckConnection,
200 self._KillProcsIfNeeded, self._MountRootfsAsWritable]
David James48f6b332013-03-03 20:11:18 -0800201 parallel.RunParallelSteps(steps, halt_on_error=True)
David James88e6f032013-03-02 08:13:20 -0800202
203 # If we failed to mark the rootfs as writable, try disabling rootfs
204 # verification.
205 if self._rootfs_is_still_readonly.is_set():
206 self._DisableRootfsVerification()
207
208 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700209 self._Deploy()
210
211
Ryan Cuia56a71e2012-10-18 18:40:35 -0700212def ValidateGypDefines(_option, _opt, value):
213 """Convert GYP_DEFINES-formatted string to dictionary."""
214 return chrome_util.ProcessGypDefines(value)
215
216
217class CustomOption(commandline.Option):
218 """Subclass Option class to implement path evaluation."""
219 TYPES = commandline.Option.TYPES + ('gyp_defines',)
220 TYPE_CHECKER = commandline.Option.TYPE_CHECKER.copy()
221 TYPE_CHECKER['gyp_defines'] = ValidateGypDefines
222
223
Ryan Cuie535b172012-10-19 18:25:03 -0700224def _CreateParser():
225 """Create our custom parser."""
Ryan Cui504db722013-01-22 11:48:01 -0800226 parser = commandline.OptionParser(usage=_USAGE, option_class=CustomOption,
227 caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700228
Ryan Cuia56a71e2012-10-18 18:40:35 -0700229 # TODO(rcui): Have this use the UI-V2 format of having source and target
230 # device be specified as positional arguments.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700231 parser.add_option('--force', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800232 help='Skip all prompts (i.e., for disabling of rootfs '
233 'verification). This may result in the target '
234 'machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800235 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
236 parser.add_option('--board', default=sdk_board_env,
Ryan Cui686ec052013-02-12 16:39:41 -0800237 help="The board the Chrome build is targeted for. When in "
238 "a 'cros chrome-sdk' shell, defaults to the SDK "
239 "board.")
Ryan Cuia56a71e2012-10-18 18:40:35 -0700240 parser.add_option('--build-dir', type='path',
Ryan Cui686ec052013-02-12 16:39:41 -0800241 help='The directory with Chrome build artifacts to deploy '
242 'from. Typically of format <chrome_root>/out/Debug. '
243 'When this option is used, the GYP_DEFINES '
244 'environment variable must be set.')
Pawel Osciak577773a2013-03-05 10:52:12 -0800245 parser.add_option('--target-dir', type='path',
246 help='Target directory on device to deploy Chrome into.',
247 default=_CHROME_DIR)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700248 parser.add_option('-g', '--gs-path', type='gs_path',
Ryan Cui686ec052013-02-12 16:39:41 -0800249 help='GS path that contains the chrome to deploy.')
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800250 parser.add_option('--nostartui', action='store_false', dest='startui',
251 default=True,
252 help="Don't restart the ui daemon after deployment.")
Ryan Cuif890a3e2013-03-07 18:57:06 -0800253 parser.add_option('--nostrip', action='store_false', dest='dostrip',
254 default=True,
255 help="Don't strip binaries during deployment. Warning: "
256 "the resulting binaries will be very large!")
Ryan Cui3045c5d2012-07-13 18:00:33 -0700257 parser.add_option('-p', '--port', type=int, default=remote.DEFAULT_SSH_PORT,
Ryan Cui686ec052013-02-12 16:39:41 -0800258 help='Port of the target device to connect to.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700259 parser.add_option('-t', '--to',
Ryan Cui686ec052013-02-12 16:39:41 -0800260 help='The IP address of the CrOS device to deploy to.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700261 parser.add_option('-v', '--verbose', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800262 help='Show more debug output.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700263
264 group = optparse.OptionGroup(parser, 'Advanced Options')
265 group.add_option('-l', '--local-pkg-path', type='path',
Ryan Cui686ec052013-02-12 16:39:41 -0800266 help='Path to local chrome prebuilt package to deploy.')
David Jamesa6e08892013-03-01 13:34:11 -0800267 group.add_option('--sloppy', action='store_true', default=False,
268 help='Ignore when mandatory artifacts are missing.')
Ryan Cuief91e702013-02-04 12:06:36 -0800269 group.add_option('--strict', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800270 help='Stage artifacts based on the GYP_DEFINES environment '
David Jamesa6e08892013-03-01 13:34:11 -0800271 'variable and --staging-flags, if set. Enforce that '
272 'all optional artifacts are deployed.')
Ryan Cuief91e702013-02-04 12:06:36 -0800273 group.add_option('--staging-flags', default=None, type='gyp_defines',
David Jamesa6e08892013-03-01 13:34:11 -0800274 help='Extra flags to control staging. Valid flags are - %s'
Ryan Cui686ec052013-02-12 16:39:41 -0800275 % ', '.join(chrome_util.STAGING_FLAGS))
Ryan Cuief91e702013-02-04 12:06:36 -0800276
Ryan Cuia56a71e2012-10-18 18:40:35 -0700277 parser.add_option_group(group)
278
Ryan Cuif890a3e2013-03-07 18:57:06 -0800279 # GYP_DEFINES that Chrome was built with. Influences which files are staged
280 # when --build-dir is set. Defaults to reading from the GYP_DEFINES
281 # enviroment variable.
282 parser.add_option('--gyp-defines', default=None, type='gyp_defines',
283 help=optparse.SUPPRESS_HELP)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700284 # Path of an empty directory to stage chrome artifacts to. Defaults to a
285 # temporary directory that is removed when the script finishes. If the path
286 # is specified, then it will not be removed.
287 parser.add_option('--staging-dir', type='path', default=None,
288 help=optparse.SUPPRESS_HELP)
289 # Only prepare the staging directory, and skip deploying to the device.
290 parser.add_option('--staging-only', action='store_true', default=False,
291 help=optparse.SUPPRESS_HELP)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800292 # Path to a binutil 'strip' tool to strip binaries with. Used by the Chrome
293 # ebuild to skip fetching the SDK toolchain.
294 parser.add_option('--strip-bin', type='path', default=None,
Ryan Cuia56a71e2012-10-18 18:40:35 -0700295 help=optparse.SUPPRESS_HELP)
Ryan Cuif890a3e2013-03-07 18:57:06 -0800296
Ryan Cuie535b172012-10-19 18:25:03 -0700297 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700298
Ryan Cuie535b172012-10-19 18:25:03 -0700299
300def _ParseCommandLine(argv):
301 """Parse args, and run environment-independent checks."""
302 parser = _CreateParser()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700303 (options, args) = parser.parse_args(argv)
304
Ryan Cuia56a71e2012-10-18 18:40:35 -0700305 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
306 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800307 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700308 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
309 parser.error('Cannot specify both --build_dir and '
310 '--gs-path/--local-pkg-patch')
Ryan Cui686ec052013-02-12 16:39:41 -0800311 if options.build_dir and not options.board:
312 parser.error('--board is required when --build-dir is specified.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700313 if options.gs_path and options.local_pkg_path:
314 parser.error('Cannot specify both --gs-path and --local-pkg-path')
315 if not (options.staging_only or options.to):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700316 parser.error('Need to specify --to')
Ryan Cuief91e702013-02-04 12:06:36 -0800317 if (options.strict or options.staging_flags) and not options.build_dir:
318 parser.error('--strict and --staging-flags require --build-dir to be '
319 'set.')
320 if options.staging_flags and not options.strict:
David Jamesa6e08892013-03-01 13:34:11 -0800321 parser.error('--staging-flags requires --strict to be set.')
322 if options.sloppy and options.strict:
323 parser.error('Cannot specify both --strict and --sloppy.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700324 return options, args
325
326
Ryan Cuie535b172012-10-19 18:25:03 -0700327def _PostParseCheck(options, _args):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700328 """Perform some usage validation (after we've parsed the arguments
329
330 Args:
331 options/args: The options/args object returned by optparse
332 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700333 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
334 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
335
Ryan Cuib623e7b2013-03-14 12:54:11 -0700336 if not options.gyp_defines:
Ryan Cuia56a71e2012-10-18 18:40:35 -0700337 gyp_env = os.getenv('GYP_DEFINES', None)
338 if gyp_env is not None:
339 options.gyp_defines = chrome_util.ProcessGypDefines(gyp_env)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700340 logging.debug('GYP_DEFINES taken from environment: %s',
Ryan Cuia56a71e2012-10-18 18:40:35 -0700341 options.gyp_defines)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700342
343 if options.strict and not options.gyp_defines:
344 cros_build_lib.Die('When --strict is set, the GYP_DEFINES environment '
Ryan Cui686ec052013-02-12 16:39:41 -0800345 'variable must be set.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700346
Ryan Cuib623e7b2013-03-14 12:54:11 -0700347 if (options.gyp_defines and
348 options.gyp_defines.get('component') == 'shared_library'):
349 cros_build_lib.Warning(
350 "Detected 'component=shared_library' in GYP_DEFINES. "
351 "deploy_chrome currently doesn't work well with component build. "
352 "See crosbug.com/196317. Use at your own risk!")
353
Ryan Cuia56a71e2012-10-18 18:40:35 -0700354
Ryan Cui504db722013-01-22 11:48:01 -0800355def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700356 """Get the chrome prebuilt tarball from GS.
357
358 Returns: Path to the fetched chrome tarball.
359 """
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800360 gs_ctx = gs.GSContext.Cached(cache_dir, init_boto=True)
361 files = gs_ctx.LS(gs_path).output.splitlines()
362 files = [found for found in files if
363 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
364 if not files:
365 raise Exception('No chrome package found at %s' % gs_path)
366 elif len(files) > 1:
367 # - Users should provide us with a direct link to either a stripped or
368 # unstripped chrome package.
369 # - In the case of being provided with an archive directory, where both
370 # stripped and unstripped chrome available, use the stripped chrome
371 # package.
372 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
373 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
374 files = [f for f in files if not 'unstripped' in f]
375 assert len(files) == 1
376 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800377
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800378 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800379 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800380 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
381 chrome_path = os.path.join(tempdir, filename)
382 assert os.path.exists(chrome_path)
383 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700384
385
Ryan Cuif890a3e2013-03-07 18:57:06 -0800386@contextlib.contextmanager
387def _StripBinContext(options):
388 if not options.dostrip:
389 yield None
390 elif options.strip_bin:
391 yield options.strip_bin
392 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800393 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800394 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
395 with sdk.Prepare(components=components) as ctx:
396 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
397 constants.CHROME_ENV_FILE)
398 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
399 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
400 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800401 yield strip_bin
402
403
404def _PrepareStagingDir(options, tempdir, staging_dir):
405 """Place the necessary files in the staging directory.
406
407 The staging directory is the directory used to rsync the build artifacts over
408 to the device. Only the necessary Chrome build artifacts are put into the
409 staging directory.
410 """
411 if options.build_dir:
412 with _StripBinContext(options) as strip_bin:
Ryan Cui686ec052013-02-12 16:39:41 -0800413 chrome_util.StageChromeFromBuildDir(
414 staging_dir, options.build_dir, strip_bin, strict=options.strict,
David Jamesa6e08892013-03-01 13:34:11 -0800415 sloppy=options.sloppy, gyp_defines=options.gyp_defines,
416 staging_flags=options.staging_flags)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700417 else:
418 pkg_path = options.local_pkg_path
419 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800420 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
421 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700422
423 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800424 logging.info('Extracting %s...', pkg_path)
Ryan Cuif2d1a582013-02-19 14:08:13 -0800425 osutils.SafeMakedirs(staging_dir)
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800426 cros_build_lib.DebugRunCommand(['tar', '-xpf', pkg_path], cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700427
428
429def main(argv):
430 options, args = _ParseCommandLine(argv)
431 _PostParseCheck(options, args)
432
433 # Set cros_build_lib debug level to hide RunCommand spew.
434 if options.verbose:
Ryan Cuia56a71e2012-10-18 18:40:35 -0700435 logging.getLogger().setLevel(logging.DEBUG)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700436 else:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800437 logging.getLogger().setLevel(logging.INFO)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700438
Ryan Cuif2d1a582013-02-19 14:08:13 -0800439 with osutils.TempDirContextManager() as tempdir:
440 staging_dir = options.staging_dir
441 if not staging_dir:
442 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700443
Ryan Cuif2d1a582013-02-19 14:08:13 -0800444 deploy = DeployChrome(options, tempdir, staging_dir)
David James88e6f032013-03-02 08:13:20 -0800445 try:
446 deploy.Perform()
447 except results_lib.StepFailure as ex:
448 raise SystemExit(str(ex).strip())