blob: 147c34027579c18db63b0cc91c1077a559a6c999 [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 Cui3045c5d2012-07-13 18:00:33 -070021import logging
David James88e6f032013-03-02 08:13:20 -080022import multiprocessing
Ryan Cui3045c5d2012-07-13 18:00:33 -070023import os
Ryan Cuia56a71e2012-10-18 18:40:35 -070024import optparse
Ryan Cui3045c5d2012-07-13 18:00:33 -070025import time
Ryan Cui3045c5d2012-07-13 18:00:33 -070026
Ryan Cuia56a71e2012-10-18 18:40:35 -070027
David James629febb2012-11-25 13:07:34 -080028from chromite.buildbot import constants
David James88e6f032013-03-02 08:13:20 -080029from chromite.buildbot import cbuildbot_results as results_lib
Ryan Cui686ec052013-02-12 16:39:41 -080030from chromite.cros.commands import cros_chrome_sdk
Ryan Cuia56a71e2012-10-18 18:40:35 -070031from chromite.lib import chrome_util
Ryan Cui3045c5d2012-07-13 18:00:33 -070032from chromite.lib import cros_build_lib
Ryan Cuie535b172012-10-19 18:25:03 -070033from chromite.lib import commandline
Ryan Cui777ff422012-12-07 13:12:54 -080034from chromite.lib import gs
Ryan Cui3045c5d2012-07-13 18:00:33 -070035from chromite.lib import osutils
David James88e6f032013-03-02 08:13:20 -080036from chromite.lib import parallel
Ryan Cui3045c5d2012-07-13 18:00:33 -070037from chromite.lib import remote_access as remote
Ryan Cui3045c5d2012-07-13 18:00:33 -070038
39
Ryan Cuia56a71e2012-10-18 18:40:35 -070040_USAGE = "deploy_chrome [--]\n\n %s" % __doc__
41
Ryan Cui3045c5d2012-07-13 18:00:33 -070042KERNEL_A_PARTITION = 2
43KERNEL_B_PARTITION = 4
44
45KILL_PROC_MAX_WAIT = 10
46POST_KILL_WAIT = 2
47
Ryan Cuie535b172012-10-19 18:25:03 -070048MOUNT_RW_COMMAND = 'mount -o remount,rw /'
Pawel Osciak577773a2013-03-05 10:52:12 -080049LSOF_COMMAND = 'lsof %s/chrome'
Ryan Cui3045c5d2012-07-13 18:00:33 -070050
David James2cb34002013-03-01 18:42:40 -080051_CHROME_DIR = '/opt/google/chrome'
52
Ryan Cui3045c5d2012-07-13 18:00:33 -070053
Ryan Cui3045c5d2012-07-13 18:00:33 -070054def _UrlBaseName(url):
55 """Return the last component of the URL."""
56 return url.rstrip('/').rpartition('/')[-1]
57
58
David James88e6f032013-03-02 08:13:20 -080059class DeployFailure(results_lib.StepFailure):
60 """Raised whenever the deploy fails."""
61
62
Ryan Cui3045c5d2012-07-13 18:00:33 -070063class DeployChrome(object):
64 """Wraps the core deployment functionality."""
Ryan Cuia56a71e2012-10-18 18:40:35 -070065 def __init__(self, options, tempdir, staging_dir):
Ryan Cuie535b172012-10-19 18:25:03 -070066 """Initialize the class.
67
68 Arguments:
69 options: Optparse result structure.
70 tempdir: Scratch space for the class. Caller has responsibility to clean
71 it up.
Ryan Cuie535b172012-10-19 18:25:03 -070072 """
Ryan Cui3045c5d2012-07-13 18:00:33 -070073 self.tempdir = tempdir
74 self.options = options
Ryan Cuia56a71e2012-10-18 18:40:35 -070075 self.staging_dir = staging_dir
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070076 self.host = remote.RemoteAccess(options.to, tempdir, port=options.port)
David James88e6f032013-03-02 08:13:20 -080077 self._rootfs_is_still_readonly = multiprocessing.Event()
Ryan Cui3045c5d2012-07-13 18:00:33 -070078
Ryan Cui3045c5d2012-07-13 18:00:33 -070079 def _ChromeFileInUse(self):
Pawel Osciak577773a2013-03-05 10:52:12 -080080 result = self.host.RemoteSh(LSOF_COMMAND % (self.options.target_dir,),
81 error_code_ok=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -070082 return result.returncode == 0
83
84 def _DisableRootfsVerification(self):
85 if not self.options.force:
86 logging.error('Detected that the device has rootfs verification enabled.')
87 logging.info('This script can automatically remove the rootfs '
88 'verification, which requires that it reboot the device.')
89 logging.info('Make sure the device is in developer mode!')
90 logging.info('Skip this prompt by specifying --force.')
Brian Harring521e7242012-11-01 16:57:42 -070091 if not cros_build_lib.BooleanPrompt('Remove roots verification?', False):
David James88e6f032013-03-02 08:13:20 -080092 # Since we stopped Chrome earlier, it's good form to start it up again.
93 if self.options.startui:
94 logging.info('Starting Chrome...')
95 self.host.RemoteSh('start ui')
96 raise DeployFailure('Need rootfs verification to be disabled. '
97 'Aborting.')
Ryan Cui3045c5d2012-07-13 18:00:33 -070098
99 logging.info('Removing rootfs verification from %s', self.options.to)
100 # Running in VM's cause make_dev_ssd's firmware sanity checks to fail.
101 # Use --force to bypass the checks.
102 cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
103 '--remove_rootfs_verification --force')
104 for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
105 self.host.RemoteSh(cmd % partition, error_code_ok=True)
106
107 # A reboot in developer mode takes a while (and has delays), so the user
108 # will have time to read and act on the USB boot instructions below.
109 logging.info('Please remember to press Ctrl-U if you are booting from USB.')
110 self.host.RemoteReboot()
111
David James88e6f032013-03-02 08:13:20 -0800112 # Now that the machine has been rebooted, we need to kill Chrome again.
113 self._KillProcsIfNeeded()
114
115 # Make sure the rootfs is writable now.
116 self._MountRootfsAsWritable(error_code_ok=False)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700117
118 def _CheckUiJobStarted(self):
119 # status output is in the format:
120 # <job_name> <status> ['process' <pid>].
121 # <status> is in the format <goal>/<state>.
Ryan Cuif2d1a582013-02-19 14:08:13 -0800122 try:
123 result = self.host.RemoteSh('status ui')
124 except cros_build_lib.RunCommandError as e:
125 if 'Unknown job' in e.result.error:
126 return False
127 else:
128 raise e
129
Ryan Cui3045c5d2012-07-13 18:00:33 -0700130 return result.output.split()[1].split('/')[0] == 'start'
131
132 def _KillProcsIfNeeded(self):
133 if self._CheckUiJobStarted():
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800134 logging.info('Shutting down Chrome...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700135 self.host.RemoteSh('stop ui')
136
137 # Developers sometimes run session_manager manually, in which case we'll
138 # need to help shut the chrome processes down.
139 try:
140 with cros_build_lib.SubCommandTimeout(KILL_PROC_MAX_WAIT):
141 while self._ChromeFileInUse():
142 logging.warning('The chrome binary on the device is in use.')
143 logging.warning('Killing chrome and session_manager processes...\n')
144
145 self.host.RemoteSh("pkill 'chrome|session_manager'",
146 error_code_ok=True)
147 # Wait for processes to actually terminate
148 time.sleep(POST_KILL_WAIT)
149 logging.info('Rechecking the chrome binary...')
150 except cros_build_lib.TimeoutError:
David James88e6f032013-03-02 08:13:20 -0800151 msg = ('Could not kill processes after %s seconds. Please exit any '
152 'running chrome processes and try again.' % KILL_PROC_MAX_WAIT)
153 raise DeployFailure(msg)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700154
David James88e6f032013-03-02 08:13:20 -0800155 def _MountRootfsAsWritable(self, error_code_ok=True):
156 """Mount the rootfs as writable.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700157
David James88e6f032013-03-02 08:13:20 -0800158 If the command fails, and error_code_ok is True, then this function sets
159 self._rootfs_is_still_readonly.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700160
David James88e6f032013-03-02 08:13:20 -0800161 Arguments:
162 error_code_ok: See remote.RemoteAccess.RemoteSh for details.
163 """
164 result = self.host.RemoteSh(MOUNT_RW_COMMAND, error_code_ok=error_code_ok)
165 if result.returncode:
166 self._rootfs_is_still_readonly.set()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700167
168 def _Deploy(self):
Pawel Osciak577773a2013-03-05 10:52:12 -0800169 logging.info('Copying Chrome to %s on device...', self.options.target_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700170 # Show the output (status) for this command.
Pawel Osciak577773a2013-03-05 10:52:12 -0800171 self.host.Rsync('%s/' % os.path.abspath(self.staging_dir),
172 self.options.target_dir,
David Jamesa6e08892013-03-01 13:34:11 -0800173 inplace=True, debug_level=logging.INFO,
174 verbose=self.options.verbose)
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800175 if self.options.startui:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800176 logging.info('Starting Chrome...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700177 self.host.RemoteSh('start ui')
178
David James88e6f032013-03-02 08:13:20 -0800179 def _CheckConnection(self):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700180 try:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800181 logging.info('Testing connection to the device...')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700182 self.host.RemoteSh('true')
David James88e6f032013-03-02 08:13:20 -0800183 except cros_build_lib.RunCommandError as ex:
Ryan Cui3045c5d2012-07-13 18:00:33 -0700184 logging.error('Error connecting to the test device.')
David James88e6f032013-03-02 08:13:20 -0800185 raise DeployFailure(ex)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700186
David James88e6f032013-03-02 08:13:20 -0800187 def _PrepareStagingDir(self):
188 _PrepareStagingDir(self.options, self.tempdir, self.staging_dir)
189
190 def Perform(self):
191 # If requested, just do the staging step.
192 if self.options.staging_only:
193 self._PrepareStagingDir()
194 return 0
195
196 # Run setup steps in parallel. If any step fails, RunParallelSteps will
David James48f6b332013-03-03 20:11:18 -0800197 # stop printing output at that point, and halt any running steps.
David James88e6f032013-03-02 08:13:20 -0800198 steps = [self._PrepareStagingDir, self._CheckConnection,
199 self._KillProcsIfNeeded, self._MountRootfsAsWritable]
David James48f6b332013-03-03 20:11:18 -0800200 parallel.RunParallelSteps(steps, halt_on_error=True)
David James88e6f032013-03-02 08:13:20 -0800201
202 # If we failed to mark the rootfs as writable, try disabling rootfs
203 # verification.
204 if self._rootfs_is_still_readonly.is_set():
205 self._DisableRootfsVerification()
206
207 # Actually deploy Chrome to the device.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700208 self._Deploy()
209
210
Ryan Cuia56a71e2012-10-18 18:40:35 -0700211def ValidateGypDefines(_option, _opt, value):
212 """Convert GYP_DEFINES-formatted string to dictionary."""
213 return chrome_util.ProcessGypDefines(value)
214
215
216class CustomOption(commandline.Option):
217 """Subclass Option class to implement path evaluation."""
218 TYPES = commandline.Option.TYPES + ('gyp_defines',)
219 TYPE_CHECKER = commandline.Option.TYPE_CHECKER.copy()
220 TYPE_CHECKER['gyp_defines'] = ValidateGypDefines
221
222
Ryan Cuie535b172012-10-19 18:25:03 -0700223def _CreateParser():
224 """Create our custom parser."""
Ryan Cui504db722013-01-22 11:48:01 -0800225 parser = commandline.OptionParser(usage=_USAGE, option_class=CustomOption,
226 caching=True)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700227
Ryan Cuia56a71e2012-10-18 18:40:35 -0700228 # TODO(rcui): Have this use the UI-V2 format of having source and target
229 # device be specified as positional arguments.
Ryan Cui3045c5d2012-07-13 18:00:33 -0700230 parser.add_option('--force', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800231 help='Skip all prompts (i.e., for disabling of rootfs '
232 'verification). This may result in the target '
233 'machine being rebooted.')
Ryan Cuia0215a72013-02-14 16:20:45 -0800234 sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
235 parser.add_option('--board', default=sdk_board_env,
Ryan Cui686ec052013-02-12 16:39:41 -0800236 help="The board the Chrome build is targeted for. When in "
237 "a 'cros chrome-sdk' shell, defaults to the SDK "
238 "board.")
Ryan Cuia56a71e2012-10-18 18:40:35 -0700239 parser.add_option('--build-dir', type='path',
Ryan Cui686ec052013-02-12 16:39:41 -0800240 help='The directory with Chrome build artifacts to deploy '
241 'from. Typically of format <chrome_root>/out/Debug. '
242 'When this option is used, the GYP_DEFINES '
243 'environment variable must be set.')
Pawel Osciak577773a2013-03-05 10:52:12 -0800244 parser.add_option('--target-dir', type='path',
245 help='Target directory on device to deploy Chrome into.',
246 default=_CHROME_DIR)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700247 parser.add_option('-g', '--gs-path', type='gs_path',
Ryan Cui686ec052013-02-12 16:39:41 -0800248 help='GS path that contains the chrome to deploy.')
Ryan Cui82a1f2d2013-02-22 17:34:23 -0800249 parser.add_option('--nostartui', action='store_false', dest='startui',
250 default=True,
251 help="Don't restart the ui daemon after deployment.")
Ryan Cui3045c5d2012-07-13 18:00:33 -0700252 parser.add_option('-p', '--port', type=int, default=remote.DEFAULT_SSH_PORT,
Ryan Cui686ec052013-02-12 16:39:41 -0800253 help='Port of the target device to connect to.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700254 parser.add_option('-t', '--to',
Ryan Cui686ec052013-02-12 16:39:41 -0800255 help='The IP address of the CrOS device to deploy to.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700256 parser.add_option('-v', '--verbose', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800257 help='Show more debug output.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700258
259 group = optparse.OptionGroup(parser, 'Advanced Options')
260 group.add_option('-l', '--local-pkg-path', type='path',
Ryan Cui686ec052013-02-12 16:39:41 -0800261 help='Path to local chrome prebuilt package to deploy.')
David Jamesa6e08892013-03-01 13:34:11 -0800262 group.add_option('--sloppy', action='store_true', default=False,
263 help='Ignore when mandatory artifacts are missing.')
Ryan Cuief91e702013-02-04 12:06:36 -0800264 group.add_option('--strict', action='store_true', default=False,
Ryan Cui686ec052013-02-12 16:39:41 -0800265 help='Stage artifacts based on the GYP_DEFINES environment '
David Jamesa6e08892013-03-01 13:34:11 -0800266 'variable and --staging-flags, if set. Enforce that '
267 'all optional artifacts are deployed.')
Ryan Cuief91e702013-02-04 12:06:36 -0800268 group.add_option('--staging-flags', default=None, type='gyp_defines',
David Jamesa6e08892013-03-01 13:34:11 -0800269 help='Extra flags to control staging. Valid flags are - %s'
Ryan Cui686ec052013-02-12 16:39:41 -0800270 % ', '.join(chrome_util.STAGING_FLAGS))
Ryan Cuief91e702013-02-04 12:06:36 -0800271
Ryan Cuia56a71e2012-10-18 18:40:35 -0700272 parser.add_option_group(group)
273
274 # Path of an empty directory to stage chrome artifacts to. Defaults to a
275 # temporary directory that is removed when the script finishes. If the path
276 # is specified, then it will not be removed.
277 parser.add_option('--staging-dir', type='path', default=None,
278 help=optparse.SUPPRESS_HELP)
279 # Only prepare the staging directory, and skip deploying to the device.
280 parser.add_option('--staging-only', action='store_true', default=False,
281 help=optparse.SUPPRESS_HELP)
282 # GYP_DEFINES that Chrome was built with. Influences which files are staged
283 # when --build-dir is set. Defaults to reading from the GYP_DEFINES
284 # enviroment variable.
Ryan Cuief91e702013-02-04 12:06:36 -0800285 parser.add_option('--gyp-defines', default=None, type='gyp_defines',
Ryan Cuia56a71e2012-10-18 18:40:35 -0700286 help=optparse.SUPPRESS_HELP)
Ryan Cuie535b172012-10-19 18:25:03 -0700287 return parser
Ryan Cui3045c5d2012-07-13 18:00:33 -0700288
Ryan Cuie535b172012-10-19 18:25:03 -0700289
290def _ParseCommandLine(argv):
291 """Parse args, and run environment-independent checks."""
292 parser = _CreateParser()
Ryan Cui3045c5d2012-07-13 18:00:33 -0700293 (options, args) = parser.parse_args(argv)
294
Ryan Cuia56a71e2012-10-18 18:40:35 -0700295 if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
296 parser.error('Need to specify either --gs-path, --local-pkg-path, or '
Ryan Cuief91e702013-02-04 12:06:36 -0800297 '--build-dir')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700298 if options.build_dir and any([options.gs_path, options.local_pkg_path]):
299 parser.error('Cannot specify both --build_dir and '
300 '--gs-path/--local-pkg-patch')
Ryan Cui686ec052013-02-12 16:39:41 -0800301 if options.build_dir and not options.board:
302 parser.error('--board is required when --build-dir is specified.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700303 if options.gs_path and options.local_pkg_path:
304 parser.error('Cannot specify both --gs-path and --local-pkg-path')
305 if not (options.staging_only or options.to):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700306 parser.error('Need to specify --to')
Ryan Cuief91e702013-02-04 12:06:36 -0800307 if (options.strict or options.staging_flags) and not options.build_dir:
308 parser.error('--strict and --staging-flags require --build-dir to be '
309 'set.')
310 if options.staging_flags and not options.strict:
David Jamesa6e08892013-03-01 13:34:11 -0800311 parser.error('--staging-flags requires --strict to be set.')
312 if options.sloppy and options.strict:
313 parser.error('Cannot specify both --strict and --sloppy.')
Ryan Cui3045c5d2012-07-13 18:00:33 -0700314 return options, args
315
316
Ryan Cuie535b172012-10-19 18:25:03 -0700317def _PostParseCheck(options, _args):
Ryan Cui3045c5d2012-07-13 18:00:33 -0700318 """Perform some usage validation (after we've parsed the arguments
319
320 Args:
321 options/args: The options/args object returned by optparse
322 """
Ryan Cuia56a71e2012-10-18 18:40:35 -0700323 if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
324 cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
325
Ryan Cui686ec052013-02-12 16:39:41 -0800326 if options.strict and not options.gyp_defines:
Ryan Cuia56a71e2012-10-18 18:40:35 -0700327 gyp_env = os.getenv('GYP_DEFINES', None)
328 if gyp_env is not None:
329 options.gyp_defines = chrome_util.ProcessGypDefines(gyp_env)
330 logging.info('GYP_DEFINES taken from environment: %s',
331 options.gyp_defines)
332 else:
Ryan Cui686ec052013-02-12 16:39:41 -0800333 cros_build_lib.Die('When --strict is set, the GYP_DEFINES environment '
334 'variable must be set.')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700335
336
Ryan Cui504db722013-01-22 11:48:01 -0800337def _FetchChromePackage(cache_dir, tempdir, gs_path):
Ryan Cuia56a71e2012-10-18 18:40:35 -0700338 """Get the chrome prebuilt tarball from GS.
339
340 Returns: Path to the fetched chrome tarball.
341 """
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800342 gs_ctx = gs.GSContext.Cached(cache_dir, init_boto=True)
343 files = gs_ctx.LS(gs_path).output.splitlines()
344 files = [found for found in files if
345 _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
346 if not files:
347 raise Exception('No chrome package found at %s' % gs_path)
348 elif len(files) > 1:
349 # - Users should provide us with a direct link to either a stripped or
350 # unstripped chrome package.
351 # - In the case of being provided with an archive directory, where both
352 # stripped and unstripped chrome available, use the stripped chrome
353 # package.
354 # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
355 # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
356 files = [f for f in files if not 'unstripped' in f]
357 assert len(files) == 1
358 logging.warning('Multiple chrome packages found. Using %s', files[0])
Ryan Cui777ff422012-12-07 13:12:54 -0800359
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800360 filename = _UrlBaseName(files[0])
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800361 logging.info('Fetching %s...', filename)
Ryan Cui4c2d42c2013-02-08 16:22:26 -0800362 gs_ctx.Copy(files[0], tempdir, print_cmd=False)
363 chrome_path = os.path.join(tempdir, filename)
364 assert os.path.exists(chrome_path)
365 return chrome_path
Ryan Cuia56a71e2012-10-18 18:40:35 -0700366
367
368def _PrepareStagingDir(options, tempdir, staging_dir):
369 """Place the necessary files in the staging directory.
370
371 The staging directory is the directory used to rsync the build artifacts over
372 to the device. Only the necessary Chrome build artifacts are put into the
373 staging directory.
374 """
375 if options.build_dir:
Ryan Cuia0215a72013-02-14 16:20:45 -0800376 sdk = cros_chrome_sdk.SDKFetcher(options.cache_dir, options.board)
Ryan Cui686ec052013-02-12 16:39:41 -0800377 components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
378 with sdk.Prepare(components=components) as ctx:
379 env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
380 constants.CHROME_ENV_FILE)
381 strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
382 strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
383 'bin', os.path.basename(strip_bin))
384 chrome_util.StageChromeFromBuildDir(
385 staging_dir, options.build_dir, strip_bin, strict=options.strict,
David Jamesa6e08892013-03-01 13:34:11 -0800386 sloppy=options.sloppy, gyp_defines=options.gyp_defines,
387 staging_flags=options.staging_flags)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700388 else:
389 pkg_path = options.local_pkg_path
390 if options.gs_path:
Ryan Cui504db722013-01-22 11:48:01 -0800391 pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
392 options.gs_path)
Ryan Cuia56a71e2012-10-18 18:40:35 -0700393
394 assert pkg_path
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800395 logging.info('Extracting %s...', pkg_path)
Ryan Cuif2d1a582013-02-19 14:08:13 -0800396 osutils.SafeMakedirs(staging_dir)
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800397 cros_build_lib.DebugRunCommand(['tar', '-xpf', pkg_path], cwd=staging_dir)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700398
399
400def main(argv):
401 options, args = _ParseCommandLine(argv)
402 _PostParseCheck(options, args)
403
404 # Set cros_build_lib debug level to hide RunCommand spew.
405 if options.verbose:
Ryan Cuia56a71e2012-10-18 18:40:35 -0700406 logging.getLogger().setLevel(logging.DEBUG)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700407 else:
Ryan Cui4d6b0db2013-02-28 15:13:24 -0800408 logging.getLogger().setLevel(logging.INFO)
Ryan Cui3045c5d2012-07-13 18:00:33 -0700409
Ryan Cuif2d1a582013-02-19 14:08:13 -0800410 with osutils.TempDirContextManager() as tempdir:
411 staging_dir = options.staging_dir
412 if not staging_dir:
413 staging_dir = os.path.join(tempdir, 'chrome')
Ryan Cuia56a71e2012-10-18 18:40:35 -0700414
Ryan Cuif2d1a582013-02-19 14:08:13 -0800415 deploy = DeployChrome(options, tempdir, staging_dir)
David James88e6f032013-03-02 08:13:20 -0800416 try:
417 deploy.Perform()
418 except results_lib.StepFailure as ex:
419 raise SystemExit(str(ex).strip())