blob: 5b37ac6cbf7a2228380febeff621af58430d0376 [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
Kuang-che Wub9705bd2018-06-28 17:59:18 +080014import ast
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080015import errno
16import json
17import logging
18import os
19import re
20import subprocess
21import time
22
23from bisect_kit import cli
Kuang-che Wue4bae0b2018-07-19 12:10:14 +080024from bisect_kit import codechange
Kuang-che Wu3b46aa42019-03-14 15:59:52 +080025from bisect_kit import common
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080026from bisect_kit import cr_util
Kuang-che Wue121fae2018-11-09 16:18:39 +080027from bisect_kit import errors
Kuang-che Wubfc4a642018-04-19 11:54:08 +080028from bisect_kit import git_util
Kuang-che Wufb553102018-10-02 18:14:29 +080029from bisect_kit import locking
Kuang-che Wubfc4a642018-04-19 11:54:08 +080030from bisect_kit import repo_util
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080031from bisect_kit import util
32
33logger = logging.getLogger(__name__)
34
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080035re_chromeos_full_version = r'^R\d+-\d+\.\d+\.\d+$'
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080036re_chromeos_localbuild_version = r'^\d+\.\d+\.\d{4}_\d\d_\d\d_\d{4}$'
37re_chromeos_short_version = r'^\d+\.\d+\.\d+$'
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080038
39gs_archive_path = 'gs://chromeos-image-archive/{board}-release'
40gs_release_path = (
Kuang-che Wu80bf6a52019-05-31 12:48:06 +080041 'gs://chromeos-releases/{channel}-channel/{boardpath}/{short_version}')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080042
43# Assume gsutil is in PATH.
44gsutil_bin = 'gsutil'
45
Kuang-che Wub9705bd2018-06-28 17:59:18 +080046chromeos_root_inside_chroot = '/mnt/host/source'
47# relative to chromeos_root
48prebuilt_autotest_dir = 'tmp/autotest-prebuilt'
49
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080050VERSION_KEY_CROS_SHORT_VERSION = 'cros_short_version'
51VERSION_KEY_CROS_FULL_VERSION = 'cros_full_version'
52VERSION_KEY_MILESTONE = 'milestone'
53VERSION_KEY_CR_VERSION = 'cr_version'
Kuang-che Wu708310b2018-03-28 17:24:34 +080054VERSION_KEY_ANDROID_BUILD_ID = 'android_build_id'
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080055VERSION_KEY_ANDROID_BRANCH = 'android_branch'
56
57
Kuang-che Wu9890ce82018-07-07 15:14:10 +080058class NeedRecreateChrootException(Exception):
59 """Failed to build ChromeOS because of chroot mismatch or corruption"""
60
61
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080062def is_cros_short_version(s):
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080063 """Determines if `s` is chromeos short 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_short_version, s))
68
69
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080070def is_cros_localbuild_version(s):
71 """Determines if `s` is chromeos local build version."""
72 return bool(re.match(re_chromeos_localbuild_version, s))
73
74
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080075def is_cros_full_version(s):
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080076 """Determines if `s` is chromeos full version.
77
78 This function doesn't accept version number of local build.
79 """
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080080 return bool(re.match(re_chromeos_full_version, s))
81
82
83def is_cros_version(s):
84 """Determines if `s` is chromeos version (either short or full)"""
85 return is_cros_short_version(s) or is_cros_full_version(s)
86
87
88def make_cros_full_version(milestone, short_version):
89 """Makes full_version from milestone and short_version"""
Kuang-che Wuacb6efd2018-04-25 18:52:58 +080090 assert milestone
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080091 return 'R%s-%s' % (milestone, short_version)
92
93
94def version_split(full_version):
95 """Splits full_version into milestone and short_version"""
96 assert is_cros_full_version(full_version)
97 milestone, short_version = full_version.split('-')
98 return milestone[1:], short_version
99
100
101def argtype_cros_version(s):
102 if not is_cros_version(s):
103 msg = 'invalid cros version'
104 raise cli.ArgTypeError(msg, '9876.0.0 or R62-9876.0.0')
105 return s
106
107
108def query_dut_lsb_release(host):
109 """Query /etc/lsb-release of given DUT
110
111 Args:
112 host: the DUT address
113
114 Returns:
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800115 dict for keys and values of /etc/lsb-release.
116
117 Raises:
Kuang-che Wu44278142019-03-04 11:33:57 +0800118 errors.SshConnectionError: cannot connect to host
119 errors.ExternalError: lsb-release file doesn't exist
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800120 """
121 try:
Kuang-che Wu44278142019-03-04 11:33:57 +0800122 output = util.ssh_cmd(host, 'cat', '/etc/lsb-release')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800123 except subprocess.CalledProcessError:
Kuang-che Wu44278142019-03-04 11:33:57 +0800124 raise errors.ExternalError('unable to read /etc/lsb-release; not a DUT')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800125 return dict(re.findall(r'^(\w+)=(.*)$', output, re.M))
126
127
128def is_dut(host):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800129 """Determines whether a host is a chromeos device.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800130
131 Args:
132 host: the DUT address
133
134 Returns:
135 True if the host is a chromeos device.
136 """
Kuang-che Wu44278142019-03-04 11:33:57 +0800137 try:
138 return query_dut_lsb_release(host).get('DEVICETYPE') in [
139 'CHROMEBASE',
140 'CHROMEBIT',
141 'CHROMEBOOK',
142 'CHROMEBOX',
143 'REFERENCE',
144 ]
145 except (errors.ExternalError, errors.SshConnectionError):
146 return False
147
148
149def is_good_dut(host):
150 if not is_dut(host):
151 return False
152
153 # Sometimes python is broken after 'cros flash'.
154 try:
155 util.ssh_cmd(host, 'python', '-c', '1')
156 return True
157 except (subprocess.CalledProcessError, errors.SshConnectionError):
158 return False
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800159
160
161def query_dut_board(host):
162 """Query board name of a given DUT"""
163 return query_dut_lsb_release(host).get('CHROMEOS_RELEASE_BOARD')
164
165
166def query_dut_short_version(host):
Kuang-che Wuacb6efd2018-04-25 18:52:58 +0800167 """Query short version of a given DUT.
168
169 This function may return version of local build, which
170 is_cros_short_version() is false.
171 """
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800172 return query_dut_lsb_release(host).get('CHROMEOS_RELEASE_VERSION')
173
174
175def query_dut_boot_id(host, connect_timeout=None):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800176 """Query boot id.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800177
178 Args:
179 host: DUT address
180 connect_timeout: connection timeout
181
182 Returns:
183 boot uuid
184 """
Kuang-che Wu44278142019-03-04 11:33:57 +0800185 return util.ssh_cmd(
186 host,
187 'cat',
188 '/proc/sys/kernel/random/boot_id',
189 connect_timeout=connect_timeout).strip()
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800190
191
192def reboot(host):
193 """Reboot a DUT and verify"""
194 logger.debug('reboot %s', host)
195 boot_id = query_dut_boot_id(host)
196
Kuang-che Wu44278142019-03-04 11:33:57 +0800197 try:
198 util.ssh_cmd(host, 'reboot')
Kuang-che Wu5f662e82019-03-05 11:49:56 +0800199 except errors.SshConnectionError:
200 # Depends on timing, ssh may return failure due to broken pipe, which is
201 # working as intended. Ignore such kind of errors.
Kuang-che Wu44278142019-03-04 11:33:57 +0800202 pass
Kuang-che Wu708310b2018-03-28 17:24:34 +0800203 wait_reboot_done(host, boot_id)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800204
Kuang-che Wu708310b2018-03-28 17:24:34 +0800205
206def wait_reboot_done(host, boot_id):
Kuang-che Wu4fe945b2018-03-31 16:46:38 +0800207 # For dev-mode test image, the reboot time is roughly at least 16 seconds
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800208 # (dev screen short delay) or more (long delay).
209 time.sleep(15)
210 for _ in range(100):
211 try:
212 # During boot, DUT does not response and thus ssh may hang a while. So
213 # set a connect timeout. 3 seconds are enough and 2 are not. It's okay to
214 # set tight limit because it's inside retry loop.
215 assert boot_id != query_dut_boot_id(host, connect_timeout=3)
216 return
Kuang-che Wu5f662e82019-03-05 11:49:56 +0800217 except errors.SshConnectionError:
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800218 logger.debug('reboot not ready? sleep wait 1 sec')
219 time.sleep(1)
220
Kuang-che Wue121fae2018-11-09 16:18:39 +0800221 raise errors.ExternalError('reboot failed?')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800222
223
Kuang-che Wu80bf6a52019-05-31 12:48:06 +0800224def gs_release_boardpath(board):
225 """Normalizes board name for gs://chromeos-releases/
226
227 This follows behavior of PushImage() in chromite/scripts/pushimage.py
228 Note, only gs://chromeos-releases/ needs normalization,
229 gs://chromeos-image-archive does not.
230
231 Args:
232 board: ChromeOS board name
233
234 Returns:
235 normalized board name
236 """
237 return board.replace('_', '-')
238
239
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800240def gsutil(*args, **kwargs):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800241 """gsutil command line wrapper.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800242
243 Args:
244 args: command line arguments passed to gsutil
245 kwargs:
246 ignore_errors: if true, return '' for failures, for example 'gsutil ls'
247 but the path not found.
248
249 Returns:
250 stdout of gsutil
251
252 Raises:
Kuang-che Wue121fae2018-11-09 16:18:39 +0800253 errors.InternalError: gsutil failed to run
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800254 subprocess.CalledProcessError: command failed
255 """
256 stderr_lines = []
257 try:
258 return util.check_output(
259 gsutil_bin, *args, stderr_callback=stderr_lines.append)
260 except subprocess.CalledProcessError as e:
261 stderr = ''.join(stderr_lines)
262 if re.search(r'ServiceException:.* does not have .*access', stderr):
Kuang-che Wue121fae2018-11-09 16:18:39 +0800263 raise errors.ExternalError(
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800264 'gsutil failed due to permission. ' +
265 'Run "%s config" and follow its instruction. ' % gsutil_bin +
266 'Fill any string if it asks for project-id')
267 if kwargs.get('ignore_errors'):
268 return ''
269 raise
270 except OSError as e:
271 if e.errno == errno.ENOENT:
Kuang-che Wue121fae2018-11-09 16:18:39 +0800272 raise errors.ExternalError(
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800273 'Unable to run %s. gsutil is not installed or not in PATH?' %
274 gsutil_bin)
275 raise
276
277
278def gsutil_ls(*args, **kwargs):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800279 """gsutil ls.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800280
281 Args:
282 args: arguments passed to 'gsutil ls'
283 kwargs: extra parameters, where
Kuang-che Wu4fe945b2018-03-31 16:46:38 +0800284 ignore_errors: if true, return empty list instead of raising
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800285 exception, ex. path not found.
286
287 Returns:
288 list of 'gsutil ls' result. One element for one line of gsutil output.
289
290 Raises:
291 subprocess.CalledProcessError: gsutil failed, usually means path not found
292 """
293 return gsutil('ls', *args, **kwargs).splitlines()
294
295
296def query_milestone_by_version(board, short_version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800297 """Query milestone by ChromeOS version number.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800298
299 Args:
300 board: ChromeOS board name
301 short_version: ChromeOS version number in short format, ex. 9300.0.0
302
303 Returns:
304 ChromeOS milestone number (string). For example, '58' for '9300.0.0'.
305 None if failed.
306 """
307 path = gs_archive_path.format(board=board) + '/R*-' + short_version
308 for line in gsutil_ls('-d', path, ignore_errors=True):
309 m = re.search(r'/R(\d+)-', line)
310 if not m:
311 continue
312 return m.group(1)
313
314 for channel in ['canary', 'dev', 'beta', 'stable']:
315 path = gs_release_path.format(
Kuang-che Wu80bf6a52019-05-31 12:48:06 +0800316 channel=channel,
317 boardpath=gs_release_boardpath(board),
318 short_version=short_version)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800319 for line in gsutil_ls(path, ignore_errors=True):
320 m = re.search(r'\bR(\d+)-' + short_version, line)
321 if not m:
322 continue
323 return m.group(1)
324
325 logger.error('unable to query milestone of %s for %s', short_version, board)
326 return None
327
328
Kuang-che Wu80bf6a52019-05-31 12:48:06 +0800329def list_board_names(chromeos_root):
330 """List board names.
331
332 Args:
333 chromeos_root: chromeos tree root
334
335 Returns:
336 list of board names
337 """
338 # Following logic is simplified from chromite/lib/portage_util.py
339 cros_list_overlays = os.path.join(chromeos_root,
340 'chromite/bin/cros_list_overlays')
341 overlays = util.check_output(cros_list_overlays).splitlines()
342 result = set()
343 for overlay in overlays:
344 conf_file = os.path.join(overlay, 'metadata', 'layout.conf')
345 name = None
346 if os.path.exists(conf_file):
347 for line in open(conf_file):
348 m = re.match(r'^repo-name\s*=\s*(\S+)\s*$', line)
349 if m:
350 name = m.group(1)
351 break
352
353 if not name:
354 name_file = os.path.join(overlay, 'profiles', 'repo_name')
355 if os.path.exists(name_file):
356 name = open(name_file).read().strip()
357
358 if name:
359 name = re.sub(r'-private$', '', name)
360 result.add(name)
361
362 return list(result)
363
364
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800365def recognize_version(board, version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800366 """Recognize ChromeOS version.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800367
368 Args:
369 board: ChromeOS board name
370 version: ChromeOS version number in short or full format
371
372 Returns:
373 (milestone, version in short format)
374 """
375 if is_cros_short_version(version):
376 milestone = query_milestone_by_version(board, version)
377 short_version = version
378 else:
379 milestone, short_version = version_split(version)
380 return milestone, short_version
381
382
383def version_to_short(version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800384 """Convert ChromeOS version number to short format.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800385
386 Args:
387 version: ChromeOS version number in short or full format
388
389 Returns:
390 version number in short format
391 """
392 if is_cros_short_version(version):
393 return version
394 _, short_version = version_split(version)
395 return short_version
396
397
398def version_to_full(board, version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800399 """Convert ChromeOS version number to full format.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800400
401 Args:
402 board: ChromeOS board name
403 version: ChromeOS version number in short or full format
404
405 Returns:
406 version number in full format
407 """
408 if is_cros_full_version(version):
409 return version
410 milestone = query_milestone_by_version(board, version)
Kuang-che Wu0205f052019-05-23 12:48:37 +0800411 assert milestone, 'incorrect board=%s or version=%s ?' % (board, version)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800412 return make_cros_full_version(milestone, version)
413
414
Kuang-che Wu575dc442019-03-05 10:30:55 +0800415def list_prebuilt_from_image_archive(board):
416 """Lists ChromeOS prebuilt image available from gs://chromeos-image-archive.
417
418 gs://chromeos-image-archive contains only recent builds (in two years).
419 We prefer this function to list_prebuilt_from_chromeos_releases() because
420 - this is what "cros flash" supports directly.
421 - the paths have milestone information, so we don't need to do slow query
422 by ourselves.
423
424 Args:
425 board: ChromeOS board name
426
427 Returns:
428 list of (version, gs_path):
429 version: Chrome OS version in full format
430 gs_path: gs path of test image
431 """
432 result = []
433 for line in gsutil_ls(gs_archive_path.format(board=board)):
434 m = re.match(r'^gs:\S+(R\d+-\d+\.\d+\.\d+)', line)
435 if m:
436 full_version = m.group(1)
437 test_image = 'chromiumos_test_image.tar.xz'
438 assert line.endswith('/')
439 gs_path = line + test_image
440 result.append((full_version, gs_path))
441 return result
442
443
444def list_prebuilt_from_chromeos_releases(board):
445 """Lists ChromeOS versions available from gs://chromeos-releases.
446
447 gs://chromeos-releases contains more builds. However, 'cros flash' doesn't
448 support it.
449
450 Args:
451 board: ChromeOS board name
452
453 Returns:
454 list of (version, gs_path):
455 version: Chrome OS version in short format
456 gs_path: gs path of test image (with wildcard)
457 """
458 result = []
459 for line in gsutil_ls(
Kuang-che Wu80bf6a52019-05-31 12:48:06 +0800460 gs_release_path.format(
461 channel='*', boardpath=gs_release_boardpath(board), short_version=''),
Kuang-che Wu575dc442019-03-05 10:30:55 +0800462 ignore_errors=True):
463 m = re.match(r'gs:\S+/(\d+\.\d+\.\d+)/$', line)
464 if m:
465 short_version = m.group(1)
466 test_image = 'ChromeOS-test-R*-{short_version}-{board}.tar.xz'.format(
467 short_version=short_version, board=board)
468 gs_path = line + test_image
469 result.append((short_version, gs_path))
470 return result
471
472
473def list_chromeos_prebuilt_versions(board,
474 old,
475 new,
476 only_good_build=True,
477 include_older_build=True):
478 """Lists ChromeOS version numbers with prebuilt between given range
479
480 Args:
481 board: ChromeOS board name
482 old: start version (inclusive)
483 new: end version (inclusive)
484 only_good_build: only if test image is available
485 include_older_build: include prebuilt in gs://chromeos-releases
486
487 Returns:
488 list of sorted version numbers (in full format) between [old, new] range
489 (inclusive).
490 """
491 old = version_to_short(old)
492 new = version_to_short(new)
493
494 rev_map = {} # dict: short version -> (short or full version, gs line)
495 for full_version, gs_path in list_prebuilt_from_image_archive(board):
496 short_version = version_to_short(full_version)
497 rev_map[short_version] = full_version, gs_path
498
499 if include_older_build and old not in rev_map:
500 for short_version, gs_path in list_prebuilt_from_chromeos_releases(board):
501 if short_version not in rev_map:
502 rev_map[short_version] = short_version, gs_path
503
504 result = []
505 for rev in sorted(rev_map, key=util.version_key_func):
506 if not util.is_direct_relative_version(new, rev):
507 continue
508 if not util.is_version_lesseq(old, rev):
509 continue
510 if not util.is_version_lesseq(rev, new):
511 continue
512
513 version, gs_path = rev_map[rev]
514
515 # version_to_full() and gsutil_ls() may take long time if versions are a
516 # lot. This is acceptable because we usually bisect only short range.
517
518 if only_good_build:
519 gs_result = gsutil_ls(gs_path, ignore_errors=True)
520 if not gs_result:
521 logger.warning('%s is not a good build, ignore', version)
522 continue
523 assert len(gs_result) == 1
524 m = re.search(r'(R\d+-\d+\.\d+\.\d+)', gs_result[0])
525 if not m:
526 logger.warning('format of image path is unexpected: %s', gs_result[0])
527 continue
528 version = m.group(1)
529 elif is_cros_short_version(version):
530 version = version_to_full(board, version)
531
532 result.append(version)
533
534 return result
535
536
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800537def prepare_prebuilt_image(board, version):
538 """Prepare chromeos prebuilt image.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800539
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800540 It searches for xbuddy image which "cros flash" can use, or fetch image to
541 local disk.
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800542
543 Args:
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800544 board: ChromeOS board name
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800545 version: ChromeOS version number in short or full format
546
547 Returns:
548 xbuddy path or file path (outside chroot)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800549 """
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800550 assert is_cros_version(version)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800551 full_version = version_to_full(board, version)
552 short_version = version_to_short(full_version)
553
554 image_path = None
555 gs_path = gs_archive_path.format(board=board) + '/' + full_version
556 if gsutil_ls('-d', gs_path, ignore_errors=True):
557 image_path = 'xbuddy://remote/{board}/{full_version}/test'.format(
558 board=board, full_version=full_version)
559 else:
560 tmp_dir = 'tmp/ChromeOS-test-%s-%s' % (full_version, board)
561 if not os.path.exists(tmp_dir):
562 os.makedirs(tmp_dir)
563 # gs://chromeos-releases may have more old images than
Kuang-che Wu4fe945b2018-03-31 16:46:38 +0800564 # gs://chromeos-image-archive, but 'cros flash' doesn't support it. We have
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800565 # to fetch the image by ourselves
566 for channel in ['canary', 'dev', 'beta', 'stable']:
567 fn = 'ChromeOS-test-{full_version}-{board}.tar.xz'.format(
568 full_version=full_version, board=board)
569 gs_path = gs_release_path.format(
Kuang-che Wu80bf6a52019-05-31 12:48:06 +0800570 channel=channel,
571 boardpath=gs_release_boardpath(board),
572 short_version=short_version)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800573 gs_path += '/' + fn
574 if gsutil_ls(gs_path, ignore_errors=True):
575 # TODO(kcwu): delete tmp
576 gsutil('cp', gs_path, tmp_dir)
577 util.check_call('tar', 'Jxvf', fn, cwd=tmp_dir)
578 image_path = os.path.abspath(
579 os.path.join(tmp_dir, 'chromiumos_test_image.bin'))
580 break
581
582 assert image_path
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800583 return image_path
584
585
586def cros_flash(chromeos_root,
587 host,
588 board,
589 image_path,
590 version=None,
591 clobber_stateful=False,
Kuang-che Wu155fb6e2018-11-29 16:00:41 +0800592 disable_rootfs_verification=True):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800593 """Flash a DUT with given ChromeOS image.
594
595 This is implemented by 'cros flash' command line.
596
597 Args:
598 chromeos_root: use 'cros flash' of which chromeos tree
599 host: DUT address
600 board: ChromeOS board name
601 image_path: chromeos image xbuddy path or file path. If
602 run_inside_chroot is True, the file path is relative to src/scrips.
603 Otherwise, the file path is relative to chromeos_root.
604 version: ChromeOS version in short or full format
605 clobber_stateful: Clobber stateful partition when performing update
606 disable_rootfs_verification: Disable rootfs verification after update
607 is completed
Kuang-che Wu414d67f2019-05-28 11:28:57 +0800608
609 Raises:
610 errors.ExternalError: cros flash failed
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800611 """
612 logger.info('cros_flash %s %s %s %s', host, board, version, image_path)
613
614 # Reboot is necessary because sometimes previous 'cros flash' failed and
615 # entered a bad state.
616 reboot(host)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800617
Kuang-che Wuf3d03ca2019-03-11 17:31:40 +0800618 args = [
619 '--debug', '--no-ping', '--send-payload-in-parallel', host, image_path
620 ]
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800621 if clobber_stateful:
622 args.append('--clobber-stateful')
623 if disable_rootfs_verification:
624 args.append('--disable-rootfs-verification')
625
Kuang-che Wu414d67f2019-05-28 11:28:57 +0800626 try:
627 cros_sdk(chromeos_root, 'cros', 'flash', *args)
628 except subprocess.CalledProcessError:
629 raise errors.ExternalError('cros flash failed')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800630
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800631 if version:
632 # In the past, cros flash may fail with returncode=0
633 # So let's have an extra check.
634 short_version = version_to_short(version)
635 dut_version = query_dut_short_version(host)
Kuang-che Wu414d67f2019-05-28 11:28:57 +0800636 if dut_version != short_version:
637 raise errors.ExternalError(
638 'although cros flash succeeded, the OS version is unexpected: '
639 'actual=%s expect=%s' % (dut_version, short_version))
640
641 # "cros flash" may terminate sucessfully but the DUT starts self-repairing
642 # (b/130786578), so it's necessary to do sanity check.
643 if not is_good_dut(host):
644 raise errors.ExternalError(
645 'although cros flash succeeded, the DUT is in bad state')
646
647
648def cros_flash_with_retry(chromeos_root,
649 host,
650 board,
651 image_path,
652 version=None,
653 clobber_stateful=False,
654 disable_rootfs_verification=True,
655 repair_callback=None):
656 # 'cros flash' is not 100% reliable, retry if necessary.
657 for attempt in range(2):
658 if attempt > 0:
659 logger.info('will retry 60 seconds later')
660 time.sleep(60)
661
662 try:
663 cros_flash(
664 chromeos_root,
665 host,
666 board,
667 image_path,
668 version=version,
669 clobber_stateful=clobber_stateful,
670 disable_rootfs_verification=disable_rootfs_verification)
671 return True
672 except errors.ExternalError:
673 logger.exception('cros flash failed')
674 if repair_callback and not repair_callback(host):
675 logger.warning('not repaired, assume it is harmless')
676 continue
677 return False
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800678
679
680def version_info(board, version):
681 """Query subcomponents version info of given version of ChromeOS
682
683 Args:
684 board: ChromeOS board name
685 version: ChromeOS version number in short or full format
686
687 Returns:
688 dict of component and version info, including (if available):
689 cros_short_version: ChromeOS version
690 cros_full_version: ChromeOS version
691 milestone: milestone of ChromeOS
692 cr_version: Chrome version
Kuang-che Wu708310b2018-03-28 17:24:34 +0800693 android_build_id: Android build id
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800694 android_branch: Android branch, in format like 'git_nyc-mr1-arc'
695 """
696 info = {}
697 full_version = version_to_full(board, version)
698
699 # Some boards may have only partial-metadata.json but no metadata.json.
700 # e.g. caroline R60-9462.0.0
701 # Let's try both.
702 metadata = None
703 for metadata_filename in ['metadata.json', 'partial-metadata.json']:
704 path = gs_archive_path.format(board=board) + '/%s/%s' % (full_version,
705 metadata_filename)
706 metadata = gsutil('cat', path, ignore_errors=True)
707 if metadata:
708 o = json.loads(metadata)
709 v = o['version']
710 board_metadata = o['board-metadata'][board]
711 info.update({
712 VERSION_KEY_CROS_SHORT_VERSION: v['platform'],
713 VERSION_KEY_CROS_FULL_VERSION: v['full'],
714 VERSION_KEY_MILESTONE: v['milestone'],
715 VERSION_KEY_CR_VERSION: v['chrome'],
716 })
717
718 if 'android' in v:
Kuang-che Wu708310b2018-03-28 17:24:34 +0800719 info[VERSION_KEY_ANDROID_BUILD_ID] = v['android']
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800720 if 'android-branch' in v: # this appears since R58-9317.0.0
721 info[VERSION_KEY_ANDROID_BRANCH] = v['android-branch']
722 elif 'android-container-branch' in board_metadata:
723 info[VERSION_KEY_ANDROID_BRANCH] = v['android-container-branch']
724 break
725 else:
726 logger.error('Failed to read metadata from gs://chromeos-image-archive')
727 logger.error(
728 'Note, so far no quick way to look up version info for too old builds')
729
730 return info
Kuang-che Wu848b1af2018-02-01 20:59:36 +0800731
732
733def query_chrome_version(board, version):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800734 """Queries chrome version of chromeos build.
Kuang-che Wu848b1af2018-02-01 20:59:36 +0800735
736 Args:
737 board: ChromeOS board name
738 version: ChromeOS version number in short or full format
739
740 Returns:
741 Chrome version number
742 """
743 info = version_info(board, version)
744 return info['cr_version']
Kuang-che Wu708310b2018-03-28 17:24:34 +0800745
746
747def query_android_build_id(board, rev):
748 info = version_info(board, rev)
749 rev = info['android_build_id']
750 return rev
751
752
753def query_android_branch(board, rev):
754 info = version_info(board, rev)
755 rev = info['android_branch']
756 return rev
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800757
758
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800759def guess_chrome_version(board, rev):
760 """Guess chrome version number.
761
762 Args:
763 board: chromeos board name
764 rev: chrome or chromeos version
765
766 Returns:
767 chrome version number
768 """
769 if is_cros_version(rev):
770 assert board, 'need to specify BOARD for cros version'
771 rev = query_chrome_version(board, rev)
772 assert cr_util.is_chrome_version(rev)
773
774 return rev
775
776
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800777def is_inside_chroot():
778 """Returns True if we are inside chroot."""
779 return os.path.exists('/etc/cros_chroot_version')
780
781
782def cros_sdk(chromeos_root, *args, **kwargs):
783 """Run commands inside chromeos chroot.
784
785 Args:
786 chromeos_root: chromeos tree root
787 *args: command to run
788 **kwargs:
Kuang-che Wud4603d72018-11-29 17:51:21 +0800789 chrome_root: pass to cros_sdk; mount this path into the SDK chroot
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800790 env: (dict) environment variables for the command
791 stdin: standard input file handle for the command
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800792 stderr_callback: Callback function for stderr. Called once per line.
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800793 """
794 envs = []
795 for k, v in kwargs.get('env', {}).items():
796 assert re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', k)
797 envs.append('%s=%s' % (k, v))
798
799 # Use --no-ns-pid to prevent cros_sdk change our pgid, otherwise subsequent
800 # commands would be considered as background process.
Kuang-che Wud4603d72018-11-29 17:51:21 +0800801 cmd = ['chromite/bin/cros_sdk', '--no-ns-pid']
802
803 if kwargs.get('chrome_root'):
804 cmd += ['--chrome_root', kwargs['chrome_root']]
805
806 cmd += envs + ['--'] + list(args)
807
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800808 return util.check_output(
809 *cmd,
810 cwd=chromeos_root,
811 stdin=kwargs.get('stdin'),
812 stderr_callback=kwargs.get('stderr_callback'))
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800813
814
815def copy_into_chroot(chromeos_root, src, dst):
816 """Copies file into chromeos chroot.
817
818 Args:
819 chromeos_root: chromeos tree root
820 src: path outside chroot
821 dst: path inside chroot
822 """
823 # chroot may be an image, so we cannot copy to corresponding path
824 # directly.
825 cros_sdk(chromeos_root, 'sh', '-c', 'cat > %s' % dst, stdin=open(src))
826
827
828def exists_in_chroot(chromeos_root, path):
829 """Determine whether a path exists in the chroot.
830
831 Args:
832 chromeos_root: chromeos tree root
833 path: path inside chroot, relative to src/scripts
834
835 Returns:
836 True if a path exists
837 """
838 try:
Kuang-che Wuacb6efd2018-04-25 18:52:58 +0800839 cros_sdk(chromeos_root, 'test', '-e', path)
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800840 except subprocess.CalledProcessError:
841 return False
842 return True
843
844
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800845def check_if_need_recreate_chroot(stdout, stderr):
846 """Analyze build log and determine if chroot should be recreated.
847
848 Args:
849 stdout: stdout output of build
850 stderr: stderr output of build
851
852 Returns:
853 the reason if chroot needs recreated; None otherwise
854 """
Kuang-che Wu74768d32018-09-07 12:03:24 +0800855 if re.search(
856 r"The current version of portage supports EAPI '\d+'. "
857 "You must upgrade", stderr):
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800858 return 'EAPI version mismatch'
859
Kuang-che Wu5ac81322018-11-26 14:04:06 +0800860 if 'Chroot is too new. Consider running:' in stderr:
861 return 'chroot version is too new'
862
863 # old message before Oct 2018
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800864 if 'Chroot version is too new. Consider running cros_sdk --replace' in stderr:
865 return 'chroot version is too new'
866
Kuang-che Wu6fe987f2018-08-28 15:24:20 +0800867 # https://groups.google.com/a/chromium.org/forum/#!msg/chromium-os-dev/uzwT5APspB4/NFakFyCIDwAJ
868 if "undefined reference to 'std::__1::basic_string" in stdout:
869 return 'might be due to compiler change'
870
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800871 return None
872
873
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800874def build_image(chromeos_root, board, rev):
875 """Build ChromeOS image.
876
877 Args:
878 chromeos_root: chromeos tree root
879 board: ChromeOS board name
880 rev: the version name to build
881
882 Returns:
883 Image path
884 """
885
886 # If the given version is already built, reuse it.
Kuang-che Wuf41599c2018-08-03 16:11:11 +0800887 image_name = 'bisect-%s' % rev.replace('/', '_')
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800888 image_path = os.path.join('../build/images', board, image_name,
889 'chromiumos_test_image.bin')
890 if exists_in_chroot(chromeos_root, image_path):
891 logger.info('"%s" already exists, skip build step', image_path)
892 return image_path
893
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800894 script_name = 'build_cros_helper.sh'
Kuang-che Wu3b46aa42019-03-14 15:59:52 +0800895 copy_into_chroot(chromeos_root,
896 os.path.join(common.BISECT_KIT_ROOT, script_name),
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800897 script_name)
898 cros_sdk(chromeos_root, 'chmod', '+x', script_name)
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800899
900 stderr_lines = []
901 try:
Kuang-che Wufb553102018-10-02 18:14:29 +0800902 with locking.lock_file(locking.LOCK_FILE_FOR_BUILD):
903 cros_sdk(
904 chromeos_root,
905 './%s' % script_name,
906 board,
907 image_name,
908 stderr_callback=stderr_lines.append)
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800909 except subprocess.CalledProcessError as e:
910 # Detect failures due to incompatibility between chroot and source tree. If
911 # so, notify the caller to recreate chroot and retry.
912 reason = check_if_need_recreate_chroot(e.output, ''.join(stderr_lines))
913 if reason:
914 raise NeedRecreateChrootException(reason)
915
916 # For other failures, don't know how to handle. Just bail out.
917 raise
918
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800919 return image_path
920
921
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800922class AutotestControlInfo(object):
923 """Parsed content of autotest control file.
924
925 Attributes:
926 name: test name
927 path: control file path
928 variables: dict of top-level control variables. Sample keys: NAME, AUTHOR,
929 DOC, ATTRIBUTES, DEPENDENCIES, etc.
930 """
931
932 def __init__(self, path, variables):
933 self.name = variables['NAME']
934 self.path = path
935 self.variables = variables
936
937
938def parse_autotest_control_file(path):
939 """Parses autotest control file.
940
941 This only parses simple top-level string assignments.
942
943 Returns:
944 AutotestControlInfo object
945 """
946 variables = {}
947 code = ast.parse(open(path).read())
948 for stmt in code.body:
949 # Skip if not simple "NAME = *" assignment.
950 if not (isinstance(stmt, ast.Assign) and len(stmt.targets) == 1 and
951 isinstance(stmt.targets[0], ast.Name)):
952 continue
953
954 # Only support string value.
955 if isinstance(stmt.value, ast.Str):
956 variables[stmt.targets[0].id] = stmt.value.s
957
958 return AutotestControlInfo(path, variables)
959
960
961def enumerate_autotest_control_files(autotest_dir):
962 """Enumerate autotest control files.
963
964 Args:
965 autotest_dir: autotest folder
966
967 Returns:
968 list of paths to control files
969 """
970 # Where to find control files. Relative to autotest_dir.
971 subpaths = [
972 'server/site_tests',
973 'client/site_tests',
974 'server/tests',
975 'client/tests',
976 ]
977
978 blacklist = ['site-packages', 'venv', 'results', 'logs', 'containers']
979 result = []
980 for subpath in subpaths:
981 path = os.path.join(autotest_dir, subpath)
982 for root, dirs, files in os.walk(path):
983
984 for black in blacklist:
985 if black in dirs:
986 dirs.remove(black)
987
988 for filename in files:
989 if filename == 'control' or filename.startswith('control.'):
990 result.append(os.path.join(root, filename))
991
992 return result
993
994
995def get_autotest_test_info(autotest_dir, test_name):
996 """Get metadata of given test.
997
998 Args:
999 autotest_dir: autotest folder
1000 test_name: test name
1001
1002 Returns:
1003 AutotestControlInfo object. None if test not found.
1004 """
1005 for control_file in enumerate_autotest_control_files(autotest_dir):
1006 info = parse_autotest_control_file(control_file)
1007 if info.name == test_name:
1008 return info
1009 return None
1010
1011
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001012class ChromeOSSpecManager(codechange.SpecManager):
1013 """Repo manifest related operations.
1014
1015 This class enumerates chromeos manifest files, parses them,
1016 and sync to disk state according to them.
1017 """
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001018
1019 def __init__(self, config):
1020 self.config = config
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001021 self.manifest_dir = os.path.join(self.config['chromeos_root'], '.repo',
1022 'manifests')
1023 self.historical_manifest_git_dir = os.path.join(
Kuang-che Wud8fc9572018-10-03 21:00:41 +08001024 self.config['chromeos_mirror'], 'chromeos/manifest-versions.git')
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001025 if not os.path.exists(self.historical_manifest_git_dir):
Kuang-che Wue121fae2018-11-09 16:18:39 +08001026 raise errors.InternalError('Manifest snapshots should be cloned into %s' %
1027 self.historical_manifest_git_dir)
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001028
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001029 def lookup_build_timestamp(self, rev):
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001030 assert is_cros_full_version(rev)
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001031
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001032 milestone, short_version = version_split(rev)
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001033 path = os.path.join('buildspecs', milestone, short_version + '.xml')
1034 try:
1035 timestamp = git_util.get_commit_time(self.historical_manifest_git_dir,
1036 'refs/heads/master', path)
1037 except ValueError:
Kuang-che Wue121fae2018-11-09 16:18:39 +08001038 raise errors.InternalError(
Kuang-che Wu74768d32018-09-07 12:03:24 +08001039 '%s does not have %s' % (self.historical_manifest_git_dir, path))
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001040 return timestamp
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001041
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001042 def collect_float_spec(self, old, new):
1043 old_timestamp = self.lookup_build_timestamp(old)
1044 new_timestamp = self.lookup_build_timestamp(new)
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001045
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001046 path = os.path.join(self.manifest_dir, 'default.xml')
1047 if not os.path.islink(path) or os.readlink(path) != 'full.xml':
Kuang-che Wue121fae2018-11-09 16:18:39 +08001048 raise errors.InternalError(
1049 'default.xml not symlink to full.xml is not supported')
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001050
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001051 result = []
1052 path = 'full.xml'
1053 parser = repo_util.ManifestParser(self.manifest_dir)
1054 for timestamp, git_rev in parser.enumerate_manifest_commits(
1055 old_timestamp, new_timestamp, path):
1056 result.append(
1057 codechange.Spec(codechange.SPEC_FLOAT, git_rev, timestamp, path))
1058 return result
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001059
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001060 def collect_fixed_spec(self, old, new):
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001061 assert is_cros_full_version(old)
1062 assert is_cros_full_version(new)
1063 old_milestone, old_short_version = version_split(old)
1064 new_milestone, new_short_version = version_split(new)
1065
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001066 result = []
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001067 for milestone in git_util.list_dir_from_revision(
1068 self.historical_manifest_git_dir, 'refs/heads/master', 'buildspecs'):
1069 if not milestone.isdigit():
1070 continue
1071 if not int(old_milestone) <= int(milestone) <= int(new_milestone):
1072 continue
1073
Kuang-che Wu74768d32018-09-07 12:03:24 +08001074 files = git_util.list_dir_from_revision(
1075 self.historical_manifest_git_dir, 'refs/heads/master',
1076 os.path.join('buildspecs', milestone))
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001077
1078 for fn in files:
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001079 path = os.path.join('buildspecs', milestone, fn)
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001080 short_version, ext = os.path.splitext(fn)
1081 if ext != '.xml':
1082 continue
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001083 if (util.is_version_lesseq(old_short_version, short_version) and
1084 util.is_version_lesseq(short_version, new_short_version) and
1085 util.is_direct_relative_version(short_version, new_short_version)):
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001086 rev = make_cros_full_version(milestone, short_version)
1087 timestamp = git_util.get_commit_time(self.historical_manifest_git_dir,
1088 'refs/heads/master', path)
1089 result.append(
1090 codechange.Spec(codechange.SPEC_FIXED, rev, timestamp, path))
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001091
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001092 def version_key_func(spec):
1093 _milestone, short_version = version_split(spec.name)
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001094 return util.version_key_func(short_version)
1095
1096 result.sort(key=version_key_func)
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001097 assert result[0].name == old
1098 assert result[-1].name == new
Kuang-che Wubfc4a642018-04-19 11:54:08 +08001099 return result
1100
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001101 def get_manifest(self, rev):
1102 assert is_cros_full_version(rev)
1103 milestone, short_version = version_split(rev)
1104 path = os.path.join('buildspecs', milestone, '%s.xml' % short_version)
1105 manifest = git_util.get_file_from_revision(self.historical_manifest_git_dir,
1106 'refs/heads/master', path)
1107
1108 manifest_name = 'manifest_%s.xml' % rev
1109 manifest_path = os.path.join(self.manifest_dir, manifest_name)
1110 with open(manifest_path, 'w') as f:
1111 f.write(manifest)
1112
1113 return manifest_name
1114
1115 def parse_spec(self, spec):
1116 parser = repo_util.ManifestParser(self.manifest_dir)
1117 if spec.spec_type == codechange.SPEC_FIXED:
1118 manifest_name = self.get_manifest(spec.name)
1119 manifest_path = os.path.join(self.manifest_dir, manifest_name)
1120 content = open(manifest_path).read()
1121 root = parser.parse_single_xml(content, allow_include=False)
1122 else:
1123 root = parser.parse_xml_recursive(spec.name, spec.path)
1124
1125 spec.entries = parser.process_parsed_result(root)
1126 if spec.spec_type == codechange.SPEC_FIXED:
1127 assert spec.is_static()
1128
1129 def sync_disk_state(self, rev):
1130 manifest_name = self.get_manifest(rev)
1131
1132 # For ChromeOS, mark_as_stable step requires 'repo init -m', which sticks
1133 # manifest. 'repo sync -m' is not enough
1134 repo_util.init(
1135 self.config['chromeos_root'],
1136 'https://chrome-internal.googlesource.com/chromeos/manifest-internal',
1137 manifest_name=manifest_name,
1138 repo_url='https://chromium.googlesource.com/external/repo.git',
Kuang-che Wud8fc9572018-10-03 21:00:41 +08001139 reference=self.config['chromeos_mirror'],
Kuang-che Wue4bae0b2018-07-19 12:10:14 +08001140 )
1141
1142 # Note, don't sync with current_branch=True for chromeos. One of its
1143 # build steps (inside mark_as_stable) executes "git describe" which
1144 # needs git tag information.
1145 repo_util.sync(self.config['chromeos_root'])