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