blob: d10908fb8ea9c9302de2a151f66bc6685e143a95 [file] [log] [blame]
Kuang-che Wu2ea804f2017-11-28 17:11:41 +08001# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""ChromeOS utility.
5
6Terminology used in this module.
7 short_version: ChromeOS version number without milestone, like "9876.0.0".
8 full_version: ChromeOS version number with milestone, like "R62-9876.0.0".
9 version: if not specified, it could be in short or full format.
10"""
11
12from __future__ import print_function
13import errno
14import json
15import logging
16import os
17import re
18import subprocess
19import time
20
21from bisect_kit import cli
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080022from bisect_kit import core
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080023from bisect_kit import cr_util
Kuang-che Wubfc4a642018-04-19 11:54:08 +080024from bisect_kit import git_util
25from bisect_kit import repo_util
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080026from bisect_kit import util
27
28logger = logging.getLogger(__name__)
29
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080030re_chromeos_full_version = r'^R\d+-\d+\.\d+\.\d+$'
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080031re_chromeos_localbuild_version = r'^\d+\.\d+\.\d{4}_\d\d_\d\d_\d{4}$'
32re_chromeos_short_version = r'^\d+\.\d+\.\d+$'
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080033
34gs_archive_path = 'gs://chromeos-image-archive/{board}-release'
35gs_release_path = (
36 'gs://chromeos-releases/{channel}-channel/{board}/{short_version}')
37
38# Assume gsutil is in PATH.
39gsutil_bin = 'gsutil'
40
41VERSION_KEY_CROS_SHORT_VERSION = 'cros_short_version'
42VERSION_KEY_CROS_FULL_VERSION = 'cros_full_version'
43VERSION_KEY_MILESTONE = 'milestone'
44VERSION_KEY_CR_VERSION = 'cr_version'
Kuang-che Wu708310b2018-03-28 17:24:34 +080045VERSION_KEY_ANDROID_BUILD_ID = 'android_build_id'
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080046VERSION_KEY_ANDROID_BRANCH = 'android_branch'
47
48
49def is_cros_short_version(s):
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080050 """Determines if `s` is chromeos short version.
51
52 This function doesn't accept version number of local build.
53 """
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080054 return bool(re.match(re_chromeos_short_version, s))
55
56
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080057def is_cros_localbuild_version(s):
58 """Determines if `s` is chromeos local build version."""
59 return bool(re.match(re_chromeos_localbuild_version, s))
60
61
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080062def is_cros_full_version(s):
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080063 """Determines if `s` is chromeos full version.
64
65 This function doesn't accept version number of local build.
66 """
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080067 return bool(re.match(re_chromeos_full_version, s))
68
69
70def is_cros_version(s):
71 """Determines if `s` is chromeos version (either short or full)"""
72 return is_cros_short_version(s) or is_cros_full_version(s)
73
74
75def make_cros_full_version(milestone, short_version):
76 """Makes full_version from milestone and short_version"""
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080077 assert milestone
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080078 return 'R%s-%s' % (milestone, short_version)
79
80
81def version_split(full_version):
82 """Splits full_version into milestone and short_version"""
83 assert is_cros_full_version(full_version)
84 milestone, short_version = full_version.split('-')
85 return milestone[1:], short_version
86
87
88def argtype_cros_version(s):
89 if not is_cros_version(s):
90 msg = 'invalid cros version'
91 raise cli.ArgTypeError(msg, '9876.0.0 or R62-9876.0.0')
92 return s
93
94
95def query_dut_lsb_release(host):
96 """Query /etc/lsb-release of given DUT
97
98 Args:
99 host: the DUT address
100
101 Returns:
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800102 dict for keys and values of /etc/lsb-release.
103
104 Raises:
105 core.ExecutionFatalError: cannot connect to host or lsb-release file
106 doesn't exist
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800107 """
108 try:
109 output = util.check_output('ssh', host, 'cat', '/etc/lsb-release')
110 except subprocess.CalledProcessError:
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800111 raise core.ExecutionFatalError('cannot connect to DUT or not a DUT')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800112 return dict(re.findall(r'^(\w+)=(.*)$', output, re.M))
113
114
115def is_dut(host):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800116 """Determines whether a host is a chromeos device.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800117
118 Args:
119 host: the DUT address
120
121 Returns:
122 True if the host is a chromeos device.
123 """
124 return query_dut_lsb_release(host).get('DEVICETYPE') in [
125 'CHROMEBASE',
126 'CHROMEBIT',
127 'CHROMEBOOK',
128 'CHROMEBOX',
129 'REFERENCE',
130 ]
131
132
133def query_dut_board(host):
134 """Query board name of a given DUT"""
135 return query_dut_lsb_release(host).get('CHROMEOS_RELEASE_BOARD')
136
137
138def query_dut_short_version(host):
Kuang-che Wuacb6efd2018-04-25 18:52:58 +0800139 """Query short version of a given DUT.
140
141 This function may return version of local build, which
142 is_cros_short_version() is false.
143 """
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800144 return query_dut_lsb_release(host).get('CHROMEOS_RELEASE_VERSION')
145
146
147def query_dut_boot_id(host, connect_timeout=None):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800148 """Query boot id.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800149
150 Args:
151 host: DUT address
152 connect_timeout: connection timeout
153
154 Returns:
155 boot uuid
156 """
157 cmd = ['ssh']
158 if connect_timeout:
159 cmd += ['-oConnectTimeout=%d' % connect_timeout]
160 cmd += [host, 'cat', '/proc/sys/kernel/random/boot_id']
161 return util.check_output(*cmd).strip()
162
163
164def reboot(host):
165 """Reboot a DUT and verify"""
166 logger.debug('reboot %s', host)
167 boot_id = query_dut_boot_id(host)
168
169 # Depends on timing, ssh may return failure due to broken pipe,
170 # so don't check ssh return code.
171 util.call('ssh', host, 'reboot')
Kuang-che Wu708310b2018-03-28 17:24:34 +0800172 wait_reboot_done(host, boot_id)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800173
Kuang-che Wu708310b2018-03-28 17:24:34 +0800174
175def wait_reboot_done(host, boot_id):
Kuang-che Wu4fe945b2018-03-31 16:46:38 +0800176 # For dev-mode test image, the reboot time is roughly at least 16 seconds
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800177 # (dev screen short delay) or more (long delay).
178 time.sleep(15)
179 for _ in range(100):
180 try:
181 # During boot, DUT does not response and thus ssh may hang a while. So
182 # set a connect timeout. 3 seconds are enough and 2 are not. It's okay to
183 # set tight limit because it's inside retry loop.
184 assert boot_id != query_dut_boot_id(host, connect_timeout=3)
185 return
186 except subprocess.CalledProcessError:
187 logger.debug('reboot not ready? sleep wait 1 sec')
188 time.sleep(1)
189
190 raise core.ExecutionFatalError('reboot failed?')
191
192
193def gsutil(*args, **kwargs):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800194 """gsutil command line wrapper.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800195
196 Args:
197 args: command line arguments passed to gsutil
198 kwargs:
199 ignore_errors: if true, return '' for failures, for example 'gsutil ls'
200 but the path not found.
201
202 Returns:
203 stdout of gsutil
204
205 Raises:
206 core.ExecutionFatalError: gsutil failed to run
207 subprocess.CalledProcessError: command failed
208 """
209 stderr_lines = []
210 try:
211 return util.check_output(
212 gsutil_bin, *args, stderr_callback=stderr_lines.append)
213 except subprocess.CalledProcessError as e:
214 stderr = ''.join(stderr_lines)
215 if re.search(r'ServiceException:.* does not have .*access', stderr):
216 raise core.ExecutionFatalError(
217 'gsutil failed due to permission. ' +
218 'Run "%s config" and follow its instruction. ' % gsutil_bin +
219 'Fill any string if it asks for project-id')
220 if kwargs.get('ignore_errors'):
221 return ''
222 raise
223 except OSError as e:
224 if e.errno == errno.ENOENT:
225 raise core.ExecutionFatalError(
226 'Unable to run %s. gsutil is not installed or not in PATH?' %
227 gsutil_bin)
228 raise
229
230
231def gsutil_ls(*args, **kwargs):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800232 """gsutil ls.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800233
234 Args:
235 args: arguments passed to 'gsutil ls'
236 kwargs: extra parameters, where
Kuang-che Wu4fe945b2018-03-31 16:46:38 +0800237 ignore_errors: if true, return empty list instead of raising
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800238 exception, ex. path not found.
239
240 Returns:
241 list of 'gsutil ls' result. One element for one line of gsutil output.
242
243 Raises:
244 subprocess.CalledProcessError: gsutil failed, usually means path not found
245 """
246 return gsutil('ls', *args, **kwargs).splitlines()
247
248
249def query_milestone_by_version(board, short_version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800250 """Query milestone by ChromeOS version number.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800251
252 Args:
253 board: ChromeOS board name
254 short_version: ChromeOS version number in short format, ex. 9300.0.0
255
256 Returns:
257 ChromeOS milestone number (string). For example, '58' for '9300.0.0'.
258 None if failed.
259 """
260 path = gs_archive_path.format(board=board) + '/R*-' + short_version
261 for line in gsutil_ls('-d', path, ignore_errors=True):
262 m = re.search(r'/R(\d+)-', line)
263 if not m:
264 continue
265 return m.group(1)
266
267 for channel in ['canary', 'dev', 'beta', 'stable']:
268 path = gs_release_path.format(
269 channel=channel, board=board, short_version=short_version)
270 for line in gsutil_ls(path, ignore_errors=True):
271 m = re.search(r'\bR(\d+)-' + short_version, line)
272 if not m:
273 continue
274 return m.group(1)
275
276 logger.error('unable to query milestone of %s for %s', short_version, board)
277 return None
278
279
280def recognize_version(board, version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800281 """Recognize ChromeOS version.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800282
283 Args:
284 board: ChromeOS board name
285 version: ChromeOS version number in short or full format
286
287 Returns:
288 (milestone, version in short format)
289 """
290 if is_cros_short_version(version):
291 milestone = query_milestone_by_version(board, version)
292 short_version = version
293 else:
294 milestone, short_version = version_split(version)
295 return milestone, short_version
296
297
298def version_to_short(version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800299 """Convert ChromeOS version number to short format.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800300
301 Args:
302 version: ChromeOS version number in short or full format
303
304 Returns:
305 version number in short format
306 """
307 if is_cros_short_version(version):
308 return version
309 _, short_version = version_split(version)
310 return short_version
311
312
313def version_to_full(board, version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800314 """Convert ChromeOS version number to full format.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800315
316 Args:
317 board: ChromeOS board name
318 version: ChromeOS version number in short or full format
319
320 Returns:
321 version number in full format
322 """
323 if is_cros_full_version(version):
324 return version
325 milestone = query_milestone_by_version(board, version)
Kuang-che Wuacb6efd2018-04-25 18:52:58 +0800326 assert milestone
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800327 return make_cros_full_version(milestone, version)
328
329
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800330def prepare_prebuilt_image(board, version):
331 """Prepare chromeos prebuilt image.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800332
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800333 It searches for xbuddy image which "cros flash" can use, or fetch image to
334 local disk.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800335
336 Args:
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800337 board: ChromeOS board name
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800338 version: ChromeOS version number in short or full format
339
340 Returns:
341 xbuddy path or file path (outside chroot)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800342 """
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800343 assert is_cros_version(version)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800344 full_version = version_to_full(board, version)
345 short_version = version_to_short(full_version)
346
347 image_path = None
348 gs_path = gs_archive_path.format(board=board) + '/' + full_version
349 if gsutil_ls('-d', gs_path, ignore_errors=True):
350 image_path = 'xbuddy://remote/{board}/{full_version}/test'.format(
351 board=board, full_version=full_version)
352 else:
353 tmp_dir = 'tmp/ChromeOS-test-%s-%s' % (full_version, board)
354 if not os.path.exists(tmp_dir):
355 os.makedirs(tmp_dir)
356 # gs://chromeos-releases may have more old images than
Kuang-che Wu4fe945b2018-03-31 16:46:38 +0800357 # gs://chromeos-image-archive, but 'cros flash' doesn't support it. We have
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800358 # to fetch the image by ourselves
359 for channel in ['canary', 'dev', 'beta', 'stable']:
360 fn = 'ChromeOS-test-{full_version}-{board}.tar.xz'.format(
361 full_version=full_version, board=board)
362 gs_path = gs_release_path.format(
363 channel=channel, board=board, short_version=short_version)
364 gs_path += '/' + fn
365 if gsutil_ls(gs_path, ignore_errors=True):
366 # TODO(kcwu): delete tmp
367 gsutil('cp', gs_path, tmp_dir)
368 util.check_call('tar', 'Jxvf', fn, cwd=tmp_dir)
369 image_path = os.path.abspath(
370 os.path.join(tmp_dir, 'chromiumos_test_image.bin'))
371 break
372
373 assert image_path
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800374 return image_path
375
376
377def cros_flash(chromeos_root,
378 host,
379 board,
380 image_path,
381 version=None,
382 clobber_stateful=False,
383 disable_rootfs_verification=True,
384 run_inside_chroot=False):
385 """Flash a DUT with given ChromeOS image.
386
387 This is implemented by 'cros flash' command line.
388
389 Args:
390 chromeos_root: use 'cros flash' of which chromeos tree
391 host: DUT address
392 board: ChromeOS board name
393 image_path: chromeos image xbuddy path or file path. If
394 run_inside_chroot is True, the file path is relative to src/scrips.
395 Otherwise, the file path is relative to chromeos_root.
396 version: ChromeOS version in short or full format
397 clobber_stateful: Clobber stateful partition when performing update
398 disable_rootfs_verification: Disable rootfs verification after update
399 is completed
400 run_inside_chroot: if True, run 'cros flash' command inside the chroot
401 """
402 logger.info('cros_flash %s %s %s %s', host, board, version, image_path)
403
404 # Reboot is necessary because sometimes previous 'cros flash' failed and
405 # entered a bad state.
406 reboot(host)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800407
408 args = ['--no-ping', host, image_path]
409 if clobber_stateful:
410 args.append('--clobber-stateful')
411 if disable_rootfs_verification:
412 args.append('--disable-rootfs-verification')
413
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800414 if run_inside_chroot:
415 cros_sdk(chromeos_root, 'cros', 'flash', *args)
416 else:
417 util.check_call('chromite/bin/cros', 'flash', *args, cwd=chromeos_root)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800418
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800419 if version:
420 # In the past, cros flash may fail with returncode=0
421 # So let's have an extra check.
422 short_version = version_to_short(version)
423 dut_version = query_dut_short_version(host)
424 assert dut_version == short_version
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800425
426
427def version_info(board, version):
428 """Query subcomponents version info of given version of ChromeOS
429
430 Args:
431 board: ChromeOS board name
432 version: ChromeOS version number in short or full format
433
434 Returns:
435 dict of component and version info, including (if available):
436 cros_short_version: ChromeOS version
437 cros_full_version: ChromeOS version
438 milestone: milestone of ChromeOS
439 cr_version: Chrome version
Kuang-che Wu708310b2018-03-28 17:24:34 +0800440 android_build_id: Android build id
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800441 android_branch: Android branch, in format like 'git_nyc-mr1-arc'
442 """
443 info = {}
444 full_version = version_to_full(board, version)
445
446 # Some boards may have only partial-metadata.json but no metadata.json.
447 # e.g. caroline R60-9462.0.0
448 # Let's try both.
449 metadata = None
450 for metadata_filename in ['metadata.json', 'partial-metadata.json']:
451 path = gs_archive_path.format(board=board) + '/%s/%s' % (full_version,
452 metadata_filename)
453 metadata = gsutil('cat', path, ignore_errors=True)
454 if metadata:
455 o = json.loads(metadata)
456 v = o['version']
457 board_metadata = o['board-metadata'][board]
458 info.update({
459 VERSION_KEY_CROS_SHORT_VERSION: v['platform'],
460 VERSION_KEY_CROS_FULL_VERSION: v['full'],
461 VERSION_KEY_MILESTONE: v['milestone'],
462 VERSION_KEY_CR_VERSION: v['chrome'],
463 })
464
465 if 'android' in v:
Kuang-che Wu708310b2018-03-28 17:24:34 +0800466 info[VERSION_KEY_ANDROID_BUILD_ID] = v['android']
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800467 if 'android-branch' in v: # this appears since R58-9317.0.0
468 info[VERSION_KEY_ANDROID_BRANCH] = v['android-branch']
469 elif 'android-container-branch' in board_metadata:
470 info[VERSION_KEY_ANDROID_BRANCH] = v['android-container-branch']
471 break
472 else:
473 logger.error('Failed to read metadata from gs://chromeos-image-archive')
474 logger.error(
475 'Note, so far no quick way to look up version info for too old builds')
476
477 return info
Kuang-che Wu848b1af2018-02-01 20:59:36 +0800478
479
480def query_chrome_version(board, version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800481 """Queries chrome version of chromeos build.
Kuang-che Wu848b1af2018-02-01 20:59:36 +0800482
483 Args:
484 board: ChromeOS board name
485 version: ChromeOS version number in short or full format
486
487 Returns:
488 Chrome version number
489 """
490 info = version_info(board, version)
491 return info['cr_version']
Kuang-che Wu708310b2018-03-28 17:24:34 +0800492
493
494def query_android_build_id(board, rev):
495 info = version_info(board, rev)
496 rev = info['android_build_id']
497 return rev
498
499
500def query_android_branch(board, rev):
501 info = version_info(board, rev)
502 rev = info['android_branch']
503 return rev
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800504
505
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800506def guess_chrome_version(board, rev):
507 """Guess chrome version number.
508
509 Args:
510 board: chromeos board name
511 rev: chrome or chromeos version
512
513 Returns:
514 chrome version number
515 """
516 if is_cros_version(rev):
517 assert board, 'need to specify BOARD for cros version'
518 rev = query_chrome_version(board, rev)
519 assert cr_util.is_chrome_version(rev)
520
521 return rev
522
523
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800524def is_inside_chroot():
525 """Returns True if we are inside chroot."""
526 return os.path.exists('/etc/cros_chroot_version')
527
528
529def cros_sdk(chromeos_root, *args, **kwargs):
530 """Run commands inside chromeos chroot.
531
532 Args:
533 chromeos_root: chromeos tree root
534 *args: command to run
535 **kwargs:
536 env: (dict) environment variables for the command
537 stdin: standard input file handle for the command
538 """
539 envs = []
540 for k, v in kwargs.get('env', {}).items():
541 assert re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', k)
542 envs.append('%s=%s' % (k, v))
543
544 # Use --no-ns-pid to prevent cros_sdk change our pgid, otherwise subsequent
545 # commands would be considered as background process.
546 cmd = ['chromite/bin/cros_sdk', '--no-ns-pid'] + envs + ['--'] + list(args)
547 return util.check_output(*cmd, cwd=chromeos_root, stdin=kwargs.get('stdin'))
548
549
550def copy_into_chroot(chromeos_root, src, dst):
551 """Copies file into chromeos chroot.
552
553 Args:
554 chromeos_root: chromeos tree root
555 src: path outside chroot
556 dst: path inside chroot
557 """
558 # chroot may be an image, so we cannot copy to corresponding path
559 # directly.
560 cros_sdk(chromeos_root, 'sh', '-c', 'cat > %s' % dst, stdin=open(src))
561
562
563def exists_in_chroot(chromeos_root, path):
564 """Determine whether a path exists in the chroot.
565
566 Args:
567 chromeos_root: chromeos tree root
568 path: path inside chroot, relative to src/scripts
569
570 Returns:
571 True if a path exists
572 """
573 try:
Kuang-che Wuacb6efd2018-04-25 18:52:58 +0800574 cros_sdk(chromeos_root, 'test', '-e', path)
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800575 except subprocess.CalledProcessError:
576 return False
577 return True
578
579
580def build_image(chromeos_root, board, rev):
581 """Build ChromeOS image.
582
583 Args:
584 chromeos_root: chromeos tree root
585 board: ChromeOS board name
586 rev: the version name to build
587
588 Returns:
589 Image path
590 """
591
592 # If the given version is already built, reuse it.
593 image_name = 'bisect-%s' % rev
594 image_path = os.path.join('../build/images', board, image_name,
595 'chromiumos_test_image.bin')
596 if exists_in_chroot(chromeos_root, image_path):
597 logger.info('"%s" already exists, skip build step', image_path)
598 return image_path
599
600 dirname = os.path.dirname(os.path.abspath(__file__))
601 script_name = 'build_cros_helper.sh'
602 copy_into_chroot(chromeos_root, os.path.join(dirname, '..', script_name),
603 script_name)
604 cros_sdk(chromeos_root, 'chmod', '+x', script_name)
605 cros_sdk(chromeos_root, './%s' % script_name, board, image_name)
606 return image_path
607
608
609class ChromeOSManifestManager(repo_util.ManifestManager):
610 """Manifest operations for chromeos repo"""
611
612 def __init__(self, config):
613 self.config = config
614
615 def _query_manifest_name(self, rev):
616 assert is_cros_full_version(rev)
617 milestone, short_version = version_split(rev)
618 manifest_name = os.path.join('buildspecs', milestone,
619 '%s.xml' % short_version)
620 return manifest_name
621
622 def sync_disk_state(self, rev):
623 manifest_name = self._query_manifest_name(rev)
624
625 # For ChromeOS, mark_as_stable step requires 'repo init -m', which sticks
626 # manifest. 'repo sync -m' is not enough
627 repo_util.init(
628 self.config['repo_dir'],
629 'https://chrome-internal.googlesource.com/chromeos/manifest-versions/',
630 manifest_name=manifest_name,
631 repo_url='https://chromium.googlesource.com/external/repo.git')
632
633 # Note, don't sync with current_branch=True for chromeos. One of its
634 # build steps (inside mark_as_stable) executes "git describe" which
635 # needs git tag information.
636 repo_util.sync(self.config['repo_dir'])
637
638 def fetch_git_repos(self, rev):
639 # Early return if necessary git history are already fetched.
640 mf = self.fetch_manifest(rev)
641 repo_set = repo_util.parse_repo_set(self.config['repo_dir'], mf)
642 all_have = True
643 for path, git_rev in repo_set.items():
644 git_root = os.path.join(self.config['repo_dir'], path)
645 if not os.path.exists(git_root):
646 all_have = False
647 break
648 if not git_util.is_containing_commit(git_root, git_rev):
649 all_have = False
650 break
651 if all_have:
652 return
653
654 # TODO(kcwu): fetch git history but don't switch current disk state.
655 self.sync_disk_state(rev)
656
657 def enumerate_manifest(self, old, new):
658 assert is_cros_full_version(old)
659 assert is_cros_full_version(new)
660 old_milestone, old_short_version = version_split(old)
661 new_milestone, new_short_version = version_split(new)
662
663 # TODO(kcwu): fetch manifests but don't switch current disk state.
664 self.sync_disk_state(new)
665
666 spec_dir = os.path.join(self.config['repo_dir'], '.repo', 'manifests',
667 'buildspecs')
668 result = []
669 for root, dirs, files in os.walk(spec_dir):
670 dirs[:] = [
671 dn for dn in dirs if dn.isdigit() and
672 int(old_milestone) <= int(dn) <= int(new_milestone)
673 ]
674
675 for fn in files:
676 short_version, ext = os.path.splitext(fn)
677 if ext != '.xml':
678 continue
679 milestone = os.path.basename(root)
680 if (util.is_version_lesseq(old_short_version, short_version) and
681 util.is_version_lesseq(short_version, new_short_version) and
682 util.is_direct_relative_version(short_version, new_short_version)):
683 result.append(make_cros_full_version(milestone, short_version))
684
685 def version_key_func(full_version):
686 _milestone, short_version = version_split(full_version)
687 return util.version_key_func(short_version)
688
689 result.sort(key=version_key_func)
690 assert result[0] == old
691 assert result[-1] == new
692 return result
693
694 def fetch_manifest(self, rev):
695 # The manifest is already synced, no need to fetch.
696 return self._query_manifest_name(rev)