blob: 213fa8fba9d3ebbbf43d5595d67764e19fdce9ba [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 Cui3045c5d2012-07-13 18:00:33 -070040
41
Ryan Cuia56a71e2012-10-18 18:40:35 -070042_USAGE = "deploy_chrome [--]\n\n %s" % __doc__
43
Ryan Cui3045c5d2012-07-13 18:00:33 -070044KERNEL_A_PARTITION = 2
45KERNEL_B_PARTITION = 4
46
47KILL_PROC_MAX_WAIT = 10
48POST_KILL_WAIT = 2
49
Ryan Cuie535b172012-10-19 18:25:03 -070050MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Pawel Osciak577773a2013-03-05 10:52:12 -080051LSOF_COMMAND = 'lsof %s/chrome'
Ryan Cui3045c5d2012-07-13 18:00:33 -070052
David James2cb34002013-03-01 18:42:40 -080053_CHROME_DIR = '/opt/google/chrome'
54
Ryan Cui3045c5d2012-07-13 18:00:33 -070055
Ryan Cui3045c5d2012-07-13 18:00:33 -070056def _UrlBaseName(url):
57 """Return the last component of the URL."""
58 return url.rstrip('/').rpartition('/')[-1]
59
60
David James88e6f032013-03-02 08:13:20 -080061class DeployFailure(results_lib.StepFailure):
62 """Raised whenever the deploy fails."""
63
64
Ryan Cui3045c5d2012-07-13 18:00:33 -070065class DeployChrome(object):
66 """Wraps the core deployment functionality."""
Ryan Cuia56a71e2012-10-18 18:40:35 -070067 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -070068 """Initialize the class.
69
70 Arguments:
71 options: Optparse result structure.
72 tempdir: Scratch space for the class. Caller has responsibility to clean
73 it up.
Ryan Cuie535b172012-10-19 18:25:03 -070074 """
Ryan Cui3045c5d2012-07-13 18:00:33 -070075 self.tempdir = tempdir
76 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -070077 self.staging_dir = staging_dir
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070078 self.host = remote.RemoteAccess(options.to, tempdir, port=options.port)
David James88e6f032013-03-02 08:13:20 -080079 self._rootfs_is_still_readonly = multiprocessing.Event()
Ryan Cui3045c5d2012-07-13 18:00:33 -070080
Ryan Cui3045c5d2012-07-13 18:00:33 -070081 def _ChromeFileInUse(self):
Pawel Osciak577773a2013-03-05 10:52:12 -080082 result = self.host.RemoteSh(LSOF_COMMAND % (self.options.target_dir,),
83 error_code_ok=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -070084 return result.returncode == 0
85
86 def _DisableRootfsVerification(self):
87 if not self.options.force:
88 logging.error('Detected that the device has rootfs verification enabled.')
89 logging.info('This script can automatically remove the rootfs '
90 'verification, which requires that it reboot the device.')
91 logging.info('Make sure the device is in developer mode!')
92 logging.info('Skip this prompt by specifying --force.')
Brian Harring521e7242012-11-01 16:57:42 -070093 if not cros_build_lib.BooleanPrompt('Remove roots verification?', False):
David James88e6f032013-03-02 08:13:20 -080094 # Since we stopped Chrome earlier, it's good form to start it up again.
95 if self.options.startui:
96 logging.info('Starting Chrome...')
97 self.host.RemoteSh('start ui')
98 raise DeployFailure('Need rootfs verification to be disabled. '
99 'Aborting.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700100
101 logging.info('Removing rootfs verification from %s', self.options.to)
102 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
103 # Use --force to bypass the checks.
104 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
105 '--remove_rootfs_verification --force')
106 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
107 self.host.RemoteSh(cmd % partition, error_code_ok=True)
108
109 # A reboot in developer mode takes a while (and has delays), so the user
110 # will have time to read and act on the USB boot instructions below.
111 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
112 self.host.RemoteReboot()
113
David James88e6f032013-03-02 08:13:20 -0800114 # Now that the machine has been rebooted, we need to kill Chrome again.
115 self._KillProcsIfNeeded()
116
117 # Make sure the rootfs is writable now.
118 self._MountRootfsAsWritable(error_code_ok=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700119
120 def _CheckUiJobStarted(self):
121 # status output is in the format:
122 # <job_name> <status> ['process' <pid>].
123 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800124 try:
125 result = self.host.RemoteSh('status ui')
126 except cros_build_lib.RunCommandError as e:
127 if 'Unknown job' in e.result.error:
128 return False
129 else:
130 raise e
131
Ryan Cui3045c5d2012-07-13 18:00:33 -0700132 return result.output.split()[1].split('/')[0] == 'start'
133
134 def _KillProcsIfNeeded(self):
135 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800136 logging.info('Shutting down Chrome...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700137 self.host.RemoteSh('stop ui')
138
139 # Developers sometimes run session_manager manually, in which case we'll
140 # need to help shut the chrome processes down.
141 try:
142 with cros_build_lib.SubCommandTimeout(KILL_PROC_MAX_WAIT):
143 while self._ChromeFileInUse():
144 logging.warning('The chrome binary on the device is in use.')
145 logging.warning('Killing chrome and session_manager processes...\n')
146
147 self.host.RemoteSh("pkill 'chrome|session_manager'",
148 error_code_ok=True)
149 # Wait for processes to actually terminate
150 time.sleep(POST_KILL_WAIT)
151 logging.info('Rechecking the chrome binary...')
152 except cros_build_lib.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800153 msg = ('Could not kill processes after %s seconds. Please exit any '
154 'running chrome processes and try again.' % KILL_PROC_MAX_WAIT)
155 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700156
David James88e6f032013-03-02 08:13:20 -0800157 def _MountRootfsAsWritable(self, error_code_ok=True):
158 """Mount the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700159
David James88e6f032013-03-02 08:13:20 -0800160 If the command fails, and error_code_ok is True, then this function sets
161 self._rootfs_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700162
David James88e6f032013-03-02 08:13:20 -0800163 Arguments:
164 error_code_ok: See remote.RemoteAccess.RemoteSh for details.
165 """
166 result = self.host.RemoteSh(MOUNT_RW_COMMAND, error_code_ok=error_code_ok)
167 if result.returncode:
168 self._rootfs_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700169
170 def _Deploy(self):
Pawel Osciak577773a2013-03-05 10:52:12 -0800171 logging.info('Copying Chrome to %s on device...', self.options.target_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700172 # Show the output (status) for this command.
Pawel Osciak577773a2013-03-05 10:52:12 -0800173 self.host.Rsync('%s/' % os.path.abspath(self.staging_dir),
174 self.options.target_dir,
David Jamesa6e08892013-03-01 13:34:11 -0800175 inplace=True, debug_level=logging.INFO,
176 verbose=self.options.verbose)
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800177 if self.options.startui:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800178 logging.info('Starting Chrome...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700179 self.host.RemoteSh('start ui')
180
David James88e6f032013-03-02 08:13:20 -0800181 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700182 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800183 logging.info('Testing connection to the device...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700184 self.host.RemoteSh('true')
David James88e6f032013-03-02 08:13:20 -0800185 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700186 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800187 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700188
David James88e6f032013-03-02 08:13:20 -0800189 def _PrepareStagingDir(self):
190 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir)
191
192 def Perform(self):
193 # If requested, just do the staging step.
194 if self.options.staging_only:
195 self._PrepareStagingDir()
196 return 0
197
198 # Run setup steps in parallel. If any step fails, RunParallelSteps will
David James48f6b332013-03-03 20:11:18 -0800199 # stop printing output at that point, and halt any running steps.
David James88e6f032013-03-02 08:13:20 -0800200 steps = [self._PrepareStagingDir, self._CheckConnection,
201 self._KillProcsIfNeeded, self._MountRootfsAsWritable]
David James48f6b332013-03-03 20:11:18 -0800202 parallel.RunParallelSteps(steps, halt_on_error=True)
David James88e6f032013-03-02 08:13:20 -0800203
204 # If we failed to mark the rootfs as writable, try disabling rootfs
205 # verification.
206 if self._rootfs_is_still_readonly.is_set():
207 self._DisableRootfsVerification()
208
209 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700210 self._Deploy()
211
212
Ryan Cuia56a71e2012-10-18 18:40:35 -0700213def ValidateGypDefines(_option, _opt, value):
214 """Convert GYP_DEFINES-formatted string to dictionary."""
215 return chrome_util.ProcessGypDefines(value)
216
217
218class CustomOption(commandline.Option):
219 """Subclass Option class to implement path evaluation."""
220 TYPES = commandline.Option.TYPES + ('gyp_defines',)
221 TYPE_CHECKER = commandline.Option.TYPE_CHECKER.copy()
222 TYPE_CHECKER['gyp_defines'] = ValidateGypDefines
223
224
Ryan Cuie535b172012-10-19 18:25:03 -0700225def _CreateParser():
226 """Create our custom parser."""
Ryan Cui504db722013-01-22 11:48:01 -0800227 parser = commandline.OptionParser(usage=_USAGE, option_class=CustomOption,
228 caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700229
Ryan Cuia56a71e2012-10-18 18:40:35 -0700230 # TODO(rcui): Have this use the UI-V2 format of having source and target
231 # device be specified as positional arguments.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700232 parser.add_option('--force', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800233 help='Skip all prompts (i.e., for disabling of rootfs '
234 'verification). This may result in the target '
235 'machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800236 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
237 parser.add_option('--board', default=sdk_board_env,
Ryan Cui686ec052013-02-12 16:39:41 -0800238 help="The board the Chrome build is targeted for. When in "
239 "a 'cros chrome-sdk' shell, defaults to the SDK "
240 "board.")
Ryan Cuia56a71e2012-10-18 18:40:35 -0700241 parser.add_option('--build-dir', type='path',
Ryan Cui686ec052013-02-12 16:39:41 -0800242 help='The directory with Chrome build artifacts to deploy '
243 'from. Typically of format <chrome_root>/out/Debug. '
244 'When this option is used, the GYP_DEFINES '
245 'environment variable must be set.')
Pawel Osciak577773a2013-03-05 10:52:12 -0800246 parser.add_option('--target-dir', type='path',
247 help='Target directory on device to deploy Chrome into.',
248 default=_CHROME_DIR)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700249 parser.add_option('-g', '--gs-path', type='gs_path',
Ryan Cui686ec052013-02-12 16:39:41 -0800250 help='GS path that contains the chrome to deploy.')
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800251 parser.add_option('--nostartui', action='store_false', dest='startui',
252 default=True,
253 help="Don't restart the ui daemon after deployment.")
Ryan Cuif890a3e2013-03-07 18:57:06 -0800254 parser.add_option('--nostrip', action='store_false', dest='dostrip',
255 default=True,
256 help="Don't strip binaries during deployment. Warning: "
257 "the resulting binaries will be very large!")
Ryan Cui3045c5d2012-07-13 18:00:33 -0700258 parser.add_option('-p', '--port', type=int, default=remote.DEFAULT_SSH_PORT,
Ryan Cui686ec052013-02-12 16:39:41 -0800259 help='Port of the target device to connect to.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700260 parser.add_option('-t', '--to',
Ryan Cui686ec052013-02-12 16:39:41 -0800261 help='The IP address of the CrOS device to deploy to.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700262 parser.add_option('-v', '--verbose', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800263 help='Show more debug output.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700264
265 group = optparse.OptionGroup(parser, 'Advanced Options')
266 group.add_option('-l', '--local-pkg-path', type='path',
Ryan Cui686ec052013-02-12 16:39:41 -0800267 help='Path to local chrome prebuilt package to deploy.')
David Jamesa6e08892013-03-01 13:34:11 -0800268 group.add_option('--sloppy', action='store_true', default=False,
269 help='Ignore when mandatory artifacts are missing.')
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700270 group.add_option('--staging-flags', default=None, type='gyp_defines',
271 help='Extra flags to control staging. Valid flags are - %s'
272 % ', '.join(chrome_util.STAGING_FLAGS))
Ryan Cuief91e702013-02-04 12:06:36 -0800273 group.add_option('--strict', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800274 help='Stage artifacts based on the GYP_DEFINES environment '
David Jamesa6e08892013-03-01 13:34:11 -0800275 'variable and --staging-flags, if set. Enforce that '
276 'all optional artifacts are deployed.')
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700277 group.add_option('--strip-flags', default=None,
278 help="Flags to call the 'strip' binutil tool with. "
279 "Overrides the default arguments.")
Ryan Cuief91e702013-02-04 12:06:36 -0800280
Ryan Cuia56a71e2012-10-18 18:40:35 -0700281 parser.add_option_group(group)
282
Ryan Cuif890a3e2013-03-07 18:57:06 -0800283 # GYP_DEFINES that Chrome was built with. Influences which files are staged
284 # when --build-dir is set. Defaults to reading from the GYP_DEFINES
285 # enviroment variable.
286 parser.add_option('--gyp-defines', default=None, type='gyp_defines',
287 help=optparse.SUPPRESS_HELP)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700288 # Path of an empty directory to stage chrome artifacts to. Defaults to a
289 # temporary directory that is removed when the script finishes. If the path
290 # is specified, then it will not be removed.
291 parser.add_option('--staging-dir', type='path', default=None,
292 help=optparse.SUPPRESS_HELP)
293 # Only prepare the staging directory, and skip deploying to the device.
294 parser.add_option('--staging-only', action='store_true', default=False,
295 help=optparse.SUPPRESS_HELP)
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700296 # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
297 # is used as-is, and not normalized. Used by the Chrome ebuild to skip
298 # fetching the SDK toolchain.
299 parser.add_option('--strip-bin', default=None, help=optparse.SUPPRESS_HELP)
Ryan Cuie535b172012-10-19 18:25:03 -0700300 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700301
Ryan Cuie535b172012-10-19 18:25:03 -0700302
303def _ParseCommandLine(argv):
304 """Parse args, and run environment-independent checks."""
305 parser = _CreateParser()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700306 (options, args) = parser.parse_args(argv)
307
Ryan Cuia56a71e2012-10-18 18:40:35 -0700308 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
309 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800310 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700311 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
312 parser.error('Cannot specify both --build_dir and '
313 '--gs-path/--local-pkg-patch')
Ryan Cui686ec052013-02-12 16:39:41 -0800314 if options.build_dir and not options.board:
315 parser.error('--board is required when --build-dir is specified.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700316 if options.gs_path and options.local_pkg_path:
317 parser.error('Cannot specify both --gs-path and --local-pkg-path')
318 if not (options.staging_only or options.to):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700319 parser.error('Need to specify --to')
Ryan Cuief91e702013-02-04 12:06:36 -0800320 if (options.strict or options.staging_flags) and not options.build_dir:
321 parser.error('--strict and --staging-flags require --build-dir to be '
322 'set.')
323 if options.staging_flags and not options.strict:
David Jamesa6e08892013-03-01 13:34:11 -0800324 parser.error('--staging-flags requires --strict to be set.')
325 if options.sloppy and options.strict:
326 parser.error('Cannot specify both --strict and --sloppy.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700327 return options, args
328
329
Ryan Cuie535b172012-10-19 18:25:03 -0700330def _PostParseCheck(options, _args):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700331 """Perform some usage validation (after we've parsed the arguments
332
333 Args:
334 options/args: The options/args object returned by optparse
335 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700336 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
337 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
338
Ryan Cuib623e7b2013-03-14 12:54:11 -0700339 if not options.gyp_defines:
Ryan Cuia56a71e2012-10-18 18:40:35 -0700340 gyp_env = os.getenv('GYP_DEFINES', None)
341 if gyp_env is not None:
342 options.gyp_defines = chrome_util.ProcessGypDefines(gyp_env)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700343 logging.debug('GYP_DEFINES taken from environment: %s',
Ryan Cuia56a71e2012-10-18 18:40:35 -0700344 options.gyp_defines)
Ryan Cuib623e7b2013-03-14 12:54:11 -0700345
346 if options.strict and not options.gyp_defines:
347 cros_build_lib.Die('When --strict is set, the GYP_DEFINES environment '
Ryan Cui686ec052013-02-12 16:39:41 -0800348 'variable must be set.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700349
Ryan Cuib623e7b2013-03-14 12:54:11 -0700350 if (options.gyp_defines and
351 options.gyp_defines.get('component') == 'shared_library'):
352 cros_build_lib.Warning(
353 "Detected 'component=shared_library' in GYP_DEFINES. "
354 "deploy_chrome currently doesn't work well with component build. "
355 "See crosbug.com/196317. Use at your own risk!")
356
Ryan Cuia56a71e2012-10-18 18:40:35 -0700357
Ryan Cui504db722013-01-22 11:48:01 -0800358def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700359 """Get the chrome prebuilt tarball from GS.
360
361 Returns: Path to the fetched chrome tarball.
362 """
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800363 gs_ctx = gs.GSContext.Cached(cache_dir, init_boto=True)
364 files = gs_ctx.LS(gs_path).output.splitlines()
365 files = [found for found in files if
366 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
367 if not files:
368 raise Exception('No chrome package found at %s' % gs_path)
369 elif len(files) > 1:
370 # - Users should provide us with a direct link to either a stripped or
371 # unstripped chrome package.
372 # - In the case of being provided with an archive directory, where both
373 # stripped and unstripped chrome available, use the stripped chrome
374 # package.
375 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
376 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
377 files = [f for f in files if not 'unstripped' in f]
378 assert len(files) == 1
379 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800380
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800381 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800382 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800383 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
384 chrome_path = os.path.join(tempdir, filename)
385 assert os.path.exists(chrome_path)
386 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700387
388
Ryan Cuif890a3e2013-03-07 18:57:06 -0800389@contextlib.contextmanager
390def _StripBinContext(options):
391 if not options.dostrip:
392 yield None
393 elif options.strip_bin:
394 yield options.strip_bin
395 else:
Ryan Cuia0215a72013-02-14 16:20:45 -0800396 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800397 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
398 with sdk.Prepare(components=components) as ctx:
399 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
400 constants.CHROME_ENV_FILE)
401 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
402 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
403 'bin', os.path.basename(strip_bin))
Ryan Cuif890a3e2013-03-07 18:57:06 -0800404 yield strip_bin
405
406
407def _PrepareStagingDir(options, tempdir, staging_dir):
408 """Place the necessary files in the staging directory.
409
410 The staging directory is the directory used to rsync the build artifacts over
411 to the device. Only the necessary Chrome build artifacts are put into the
412 staging directory.
413 """
414 if options.build_dir:
415 with _StripBinContext(options) as strip_bin:
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700416 strip_flags = (None if options.strip_flags is None else
417 shlex.split(options.strip_flags))
Ryan Cui686ec052013-02-12 16:39:41 -0800418 chrome_util.StageChromeFromBuildDir(
419 staging_dir, options.build_dir, strip_bin, strict=options.strict,
David Jamesa6e08892013-03-01 13:34:11 -0800420 sloppy=options.sloppy, gyp_defines=options.gyp_defines,
Ryan Cui5b7c2ed2013-03-18 18:45:46 -0700421 staging_flags=options.staging_flags,
422 strip_flags=strip_flags)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700423 else:
424 pkg_path = options.local_pkg_path
425 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800426 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
427 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700428
429 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800430 logging.info('Extracting %s...', pkg_path)
Ryan Cuif2d1a582013-02-19 14:08:13 -0800431 osutils.SafeMakedirs(staging_dir)
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800432 cros_build_lib.DebugRunCommand(['tar', '-xpf', pkg_path], cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700433
434
435def main(argv):
436 options, args = _ParseCommandLine(argv)
437 _PostParseCheck(options, args)
438
439 # Set cros_build_lib debug level to hide RunCommand spew.
440 if options.verbose:
Ryan Cuia56a71e2012-10-18 18:40:35 -0700441 logging.getLogger().setLevel(logging.DEBUG)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700442 else:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800443 logging.getLogger().setLevel(logging.INFO)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700444
Ryan Cuif2d1a582013-02-19 14:08:13 -0800445 with osutils.TempDirContextManager() as tempdir:
446 staging_dir = options.staging_dir
447 if not staging_dir:
448 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700449
Ryan Cuif2d1a582013-02-19 14:08:13 -0800450 deploy = DeployChrome(options, tempdir, staging_dir)
David James88e6f032013-03-02 08:13:20 -0800451 try:
452 deploy.Perform()
453 except results_lib.StepFailure as ex:
454 raise SystemExit(str(ex).strip())