blob: f66bea1e55db0378046a0b5bc8fed15414b3212d [file] [log] [blame]
Kuang-che Wu875c89a2020-01-08 14:30:55 +08001#!/usr/bin/env python3
Kuang-che Wu6e4beca2018-06-27 17:45:02 +08002# -*- coding: utf-8 -*-
Kuang-che Wubfc4a642018-04-19 11:54:08 +08003# Copyright 2018 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6"""Switcher for chromeos localbuild bisecting."""
7
8from __future__ import print_function
9import argparse
10import logging
Kuang-che Wu9890ce82018-07-07 15:14:10 +080011import os
Kuang-che Wu44278142019-03-04 11:33:57 +080012import sys
Kuang-che Wubfc4a642018-04-19 11:54:08 +080013
14from bisect_kit import cli
Kuang-che Wue4bae0b2018-07-19 12:10:14 +080015from bisect_kit import codechange
Kuang-che Wubfc4a642018-04-19 11:54:08 +080016from bisect_kit import common
17from bisect_kit import configure
Kuang-che Wu414d67f2019-05-28 11:28:57 +080018from bisect_kit import cros_lab_util
Kuang-che Wubfc4a642018-04-19 11:54:08 +080019from bisect_kit import cros_util
Kuang-che Wu0ebbf7c2019-08-28 18:19:19 +080020from bisect_kit import errors
Kuang-che Wubfc4a642018-04-19 11:54:08 +080021from bisect_kit import repo_util
22from bisect_kit import util
23
24logger = logging.getLogger(__name__)
25
26
Kuang-che Wua9a20bb2019-09-05 22:24:04 +080027def add_build_and_deploy_arguments(parser_group):
28 parser_group.add_argument(
29 '--goma_chromeos_dir',
30 type=cli.argtype_dir_path,
31 default=configure.get('GOMA_CHROMEOS_DIR'),
32 help='Goma-chromeos installed directory to mount into the Chrome OS '
33 'chroot')
34 parser_group.add_argument(
35 '--clobber-stateful',
36 '--clobber_stateful',
37 action='store_true',
38 help='Clobber stateful partition when performing update')
39 parser_group.add_argument(
40 '--no-disable-rootfs-verification',
41 '--no_disable_rootfs_verification',
42 dest='disable_rootfs_verification',
43 action='store_false',
44 help="Don't disable rootfs verification after update is completed")
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -070045 parser_group.add_argument(
46 '--no-mark-as-stable',
47 '--no_mark_as_stable',
48 dest='mark_as_stable',
49 action='store_false',
50 help="Don't mark repo stable")
Kuang-che Wua9a20bb2019-09-05 22:24:04 +080051
52
Kuang-che Wubfc4a642018-04-19 11:54:08 +080053def create_argument_parser():
Kuang-che Wud2d6e412021-01-28 16:26:41 +080054 parents = [common.common_argument_parser, common.session_optional_parser]
55 parser = argparse.ArgumentParser(parents=parents)
Kuang-che Wufe1e88a2019-09-10 21:52:25 +080056 cli.patching_argparser_exit(parser)
Kuang-che Wubfc4a642018-04-19 11:54:08 +080057 parser.add_argument(
Kuang-che Wu0ebbf7c2019-08-28 18:19:19 +080058 '--dut',
Kuang-che Wubfc4a642018-04-19 11:54:08 +080059 type=cli.argtype_notempty,
60 metavar='DUT',
Kuang-che Wu0ebbf7c2019-08-28 18:19:19 +080061 default=configure.get('DUT'),
Kuang-che Wubfc4a642018-04-19 11:54:08 +080062 help='DUT address')
63 parser.add_argument(
64 'rev',
65 nargs='?',
66 type=cli.argtype_notempty,
Kuang-che Wu99e808f2019-06-26 12:17:32 +080067 metavar='CROS_VERSION',
68 default=configure.get('CROS_VERSION', ''),
Kuang-che Wubfc4a642018-04-19 11:54:08 +080069 help='ChromeOS local build version string, in format short version, '
70 'full version, or "full,full+N"')
71 parser.add_argument(
72 '--board',
73 metavar='BOARD',
74 default=configure.get('BOARD', ''),
75 help='ChromeOS board name')
76 parser.add_argument(
Kuang-che Wuc95fc152018-06-28 18:13:22 +080077 '--chromeos_root',
Kuang-che Wubfc4a642018-04-19 11:54:08 +080078 type=cli.argtype_dir_path,
Kuang-che Wuc95fc152018-06-28 18:13:22 +080079 metavar='CHROMEOS_ROOT',
80 default=configure.get('CHROMEOS_ROOT', ''),
Kuang-che Wubfc4a642018-04-19 11:54:08 +080081 help='ChromeOS tree root (default: %(default)s)')
82 parser.add_argument(
Kuang-che Wud8fc9572018-10-03 21:00:41 +080083 '--chromeos_mirror',
Kuang-che Wue4bae0b2018-07-19 12:10:14 +080084 type=cli.argtype_dir_path,
Kuang-che Wud8fc9572018-10-03 21:00:41 +080085 default=configure.get('CHROMEOS_MIRROR', ''),
Kuang-che Wue4bae0b2018-07-19 12:10:14 +080086 help='ChromeOS repo mirror path')
87 parser.add_argument(
Kuang-che Wu85b61f02020-03-11 17:41:46 +080088 '--skip_sync_code',
89 action='store_true',
90 help='Skip the step syncing source code')
91 parser.add_argument(
Kuang-che Wua2fe9882018-09-05 17:16:31 +080092 '--nobuild',
93 action='store_true',
Kuang-che Wu85b61f02020-03-11 17:41:46 +080094 help='Stop after source code sync; do not build; imply --noimage')
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +080095 parser.add_argument(
96 '--noimage',
97 action='store_true',
98 help='Build packages only; do not build image; imply --nodeploy')
Kuang-che Wua2fe9882018-09-05 17:16:31 +080099 parser.add_argument(
100 '--nodeploy', action='store_true', help='Do not deploy after build')
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800101
102 group = parser.add_argument_group(title='Build and deploy options')
103 add_build_and_deploy_arguments(group)
104
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800105 return parser
106
107
108def mark_as_stable(opts):
Kuang-che Wub7fb2432019-04-02 18:03:25 +0800109 overlays = util.check_output(
110 'chromite/bin/cros_list_overlays', '--all',
111 cwd=opts.chromeos_root).splitlines()
112
113 # Get overlays listed in manifest file.
114 known_projects = []
Kuang-che Wu3d04eda2019-09-05 23:56:40 +0800115 for path in repo_util.list_projects(opts.chromeos_root):
Kuang-che Wub7fb2432019-04-02 18:03:25 +0800116 known_projects.append(
117 os.path.realpath(os.path.join(opts.chromeos_root, path)))
118 logger.debug('known_projects %s', known_projects)
119
120 # Skip recent added overlays.
121 # cros_mark_as_stable expects all overlays is recorded in manifest file
122 # but we haven't synthesized manifest files for each intra versions yet.
123 # TODO(kcwu): synthesize manifest file
124 for overlay in list(overlays):
125 if 'private-overlays/' in overlay and overlay not in known_projects:
126 logger.warning(
127 'bisect-kit cannot handle recently added overlay %s yet; ignore',
128 overlay)
129 overlays.remove(overlay)
130 continue
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800131
132 cmd = [
133 'chromite/bin/cros_mark_as_stable',
134 '-b', opts.board,
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800135 '--list_revisions',
Kuang-che Wub7fb2432019-04-02 18:03:25 +0800136 '--overlays', ':'.join(overlays),
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800137 '--debug',
138 '--all',
139 'commit',
140 ] # yapf: disable
Kuang-che Wuc95fc152018-06-28 18:13:22 +0800141 util.check_call(*cmd, cwd=opts.chromeos_root)
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800142
143
144def cleanup(opts):
Kuang-che Wub9bc49a2021-01-27 16:27:51 +0800145 logger.info('clean up leftover in the source tree from the last build')
Kuang-che Wu3d04eda2019-09-05 23:56:40 +0800146 repo_util.cleanup_unexpected_files(opts.chromeos_root)
147
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800148 logger.info('clean up previous result of "mark as stable"')
Kuang-che Wuc95fc152018-06-28 18:13:22 +0800149 repo_util.abandon(opts.chromeos_root, 'stabilizing_branch')
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800150
151
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800152def _build_packages(opts):
153 logger.info('build packages')
154
155 # chrome_root is only defined if called from chrome's switcher.
156 # This means using chromeos build_packages flow to build chrome during chrome
157 # bisection.
158 chrome_root = getattr(opts, 'chrome_root', None)
159
160 if not opts.goma_chromeos_dir:
Kuang-che Wu0eb2fca2020-03-17 12:28:43 +0800161 default_goma_chromeos_dir = os.path.expanduser('~/goma')
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800162 if os.path.exists(default_goma_chromeos_dir):
163 logger.debug('found %s available, use goma automatically',
164 default_goma_chromeos_dir)
165 opts.goma_chromeos_dir = default_goma_chromeos_dir
166
167 cros_util.build_packages(
168 opts.chromeos_root,
169 opts.board,
170 chrome_root=chrome_root,
171 goma_dir=opts.goma_chromeos_dir,
172 afdo_use=True)
173
174
175def build(opts, do_mark_as_stable=True):
Kuang-che Wu28980b22019-07-31 19:51:45 +0800176 # Used many times in this function, shorten it.
177 chromeos_root = opts.chromeos_root
178
Kuang-che Wuad5ac7b2020-02-20 19:02:52 +0800179 # create and hotfix chroot if necessary
180 cros_util.prepare_chroot(chromeos_root)
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800181
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800182 if do_mark_as_stable:
183 logger.info('mark as stable')
184 mark_as_stable(opts)
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800185
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800186 if opts.noimage:
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800187 _build_packages(opts)
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800188 return None
189
Kuang-che Wuaf917102020-04-10 18:03:22 +0800190 cached_name = 'bisect-%s' % util.escape_rev(opts.rev)
Kuang-che Wu28980b22019-07-31 19:51:45 +0800191 image_folder = os.path.join(cros_util.cached_images_dir, opts.board,
192 cached_name)
193 image_path = os.path.join(image_folder, cros_util.test_image_filename)
194
195 # If the given version is already built, reuse it.
Jae Hoon Kim25474b72021-04-12 18:56:15 -0700196 chromeos_root_image_folder = os.path.join(chromeos_root, image_folder)
197 chromeos_root_image_path = os.path.join(chromeos_root, image_path)
198 if not os.path.exists(chromeos_root_image_path):
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800199 _build_packages(opts)
Kuang-che Wu28980b22019-07-31 19:51:45 +0800200 built_image_folder = cros_util.build_image(chromeos_root, opts.board)
Jae Hoon Kim25474b72021-04-12 18:56:15 -0700201 os.makedirs(os.path.dirname(chromeos_root_image_folder), exist_ok=True)
202 if os.path.exists(chromeos_root_image_folder):
203 os.unlink(chromeos_root_image_folder)
204 # Create a relative symlink, so the condition actually only comes in here if
205 # the image folder is actually missing.
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -0700206 os.symlink(
207 os.path.join('../../..', built_image_folder),
208 chromeos_root_image_folder)
Kuang-che Wu28980b22019-07-31 19:51:45 +0800209
Kuang-che Wu28980b22019-07-31 19:51:45 +0800210 return image_path
Kuang-che Wu9890ce82018-07-07 15:14:10 +0800211
212
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -0700213def build_and_deploy(opts):
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800214 try:
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -0700215 image_path = build(opts, do_mark_as_stable=opts.mark_as_stable)
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800216 except cros_util.NeedRecreateChrootException as e:
217 logger.warning('recreate chroot and retry again, reason: %s', e)
218 util.check_output(
219 'chromite/bin/cros_sdk', '--delete', cwd=opts.chromeos_root)
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -0700220 image_path = build(opts, do_mark_as_stable=opts.mark_as_stable)
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800221
222 if opts.noimage or opts.nodeploy:
223 return 0
224
Kuang-che Wu721e8902021-03-19 12:18:53 +0800225 image_info = cros_util.ImageInfo()
226 image_info[cros_util.ImageType.DISK_IMAGE] = image_path
227
228 if cros_util.provision_image_with_retry(
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800229 opts.chromeos_root,
230 opts.dut,
231 opts.board,
Kuang-che Wu721e8902021-03-19 12:18:53 +0800232 image_info,
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800233 clobber_stateful=opts.clobber_stateful,
234 disable_rootfs_verification=opts.disable_rootfs_verification,
Kuang-che Wu2ac9a922020-09-03 16:50:12 +0800235 repair_callback=cros_lab_util.repair,
236 force_reboot_callback=cros_lab_util.reboot_via_servo):
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800237 return 0
238 return 1
239
240
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800241def switch(opts):
242 cleanup(opts)
243
Kuang-che Wu85b61f02020-03-11 17:41:46 +0800244 if not opts.skip_sync_code:
245 logger.info('switch source code')
246 config = dict(
247 board=opts.board,
248 chromeos_root=opts.chromeos_root,
249 chromeos_mirror=opts.chromeos_mirror)
250 spec_manager = cros_util.ChromeOSSpecManager(config)
251 cache = repo_util.RepoMirror(opts.chromeos_mirror)
252 code_manager = codechange.CodeManager(opts.chromeos_root, spec_manager,
253 cache)
254 code_manager.switch(opts.rev)
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800255
Kuang-che Wua2fe9882018-09-05 17:16:31 +0800256 if opts.nobuild:
Kuang-che Wu414d67f2019-05-28 11:28:57 +0800257 return 0
Kuang-che Wua2fe9882018-09-05 17:16:31 +0800258
Kuang-che Wua9a20bb2019-09-05 22:24:04 +0800259 return build_and_deploy(opts)
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800260
261
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -0700262def parse_args(args):
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800263 parser = create_argument_parser()
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -0700264 return parser.parse_args(args)
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800265
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -0700266
267def inner_main(opts):
Kuang-che Wu99e808f2019-06-26 12:17:32 +0800268 # --nobuild imply --nodeploy
269 if opts.nobuild:
270 opts.nodeploy = True
271
Kuang-che Wu85b61f02020-03-11 17:41:46 +0800272 if opts.skip_sync_code and opts.nobuild:
273 raise errors.ArgumentError('--skip_sync_code and --nobuild',
274 'nothing to do')
Kuang-che Wu0ebbf7c2019-08-28 18:19:19 +0800275 if not opts.dut:
276 if not opts.nodeploy:
277 raise errors.ArgumentError('--dut',
278 'DUT can be omitted only if --nodeploy')
279 if not opts.board:
280 raise errors.ArgumentError('--board',
281 'board must be specified if no --dut')
Kuang-che Wu44278142019-03-04 11:33:57 +0800282 if not opts.nodeploy:
283 if not cros_util.is_good_dut(opts.dut):
Kuang-che Wu414d67f2019-05-28 11:28:57 +0800284 logger.fatal('%r is not a good DUT', opts.dut)
285 if not cros_lab_util.repair(opts.dut):
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -0700286 return cli.EXIT_CODE_FATAL
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800287 if not opts.board:
288 opts.board = cros_util.query_dut_board(opts.dut)
289
290 if cros_util.is_cros_short_version(opts.rev):
291 opts.rev = cros_util.version_to_full(opts.board, opts.rev)
292
Kuang-che Wu721e8902021-03-19 12:18:53 +0800293 cros_util.prepare_chroot(opts.chromeos_root)
294
Kuang-che Wu414d67f2019-05-28 11:28:57 +0800295 try:
296 returncode = switch(opts)
297 except Exception:
298 logger.exception('switch failed')
299 returncode = 1
300
Kuang-che Wu5f445bf2019-06-04 19:36:44 +0800301 if not opts.nodeploy:
302 # No matter switching succeeded or not, DUT must be in good state.
303 # switch() already tried repairing if possible, no repair here.
304 if not cros_util.is_good_dut(opts.dut):
305 logger.fatal('%r is not a good DUT', opts.dut)
306 returncode = cli.EXIT_CODE_FATAL
Kuang-che Wu414d67f2019-05-28 11:28:57 +0800307 logger.info('done')
Jae Hoon Kim7cbf64e2021-04-16 10:06:32 -0700308 return returncode
309
310
311@cli.fatal_error_handler
312def main(args=None):
313 common.init()
314 opts = parse_args(args)
315 common.config_logging(opts)
316 sys.exit(inner_main(opts))
Kuang-che Wubfc4a642018-04-19 11:54:08 +0800317
318
319if __name__ == '__main__':
320 main()