blob: a1321c4eae8a0115cda5ed07758657cc5807dcfc [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2018 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Switcher for chrome on chromeos local internal build bisecting
This script will sync the chrome source tree to the specified version, build,
and deploy.
Typical usage companion with bisect_cr_localbuild_internal:
$ ./bisect_cr_localbuild_internal.py config switch \
./switch_cros_cr_localbuild_internal.py
By default, it will build and deploy chrome. You can specify --target for
alternative binaries.
"""
from __future__ import annotations
import argparse
import logging
import os
import sys
import typing
import bisect_cr_localbuild_internal
import bisect_kit
from bisect_kit import bisector_cli
from bisect_kit import cli
from bisect_kit import codechange
from bisect_kit import common
from bisect_kit import configure
from bisect_kit import cr_util
from bisect_kit import cros_lab_util
from bisect_kit import cros_util
from bisect_kit import errors
from bisect_kit import gclient_util
from bisect_kit import lacros_util
from bisect_kit import util
import switch_cros_localbuild
logger = logging.getLogger(__name__)
def create_argument_parser():
parents = [common.create_session_optional_parser()]
parser = argparse.ArgumentParser(parents=parents)
cli.patching_argparser_exit(parser)
parser.add_argument(
'--rich_result',
action='store_true',
help='Instead of mere exit code, output detailed information in json',
)
parser.add_argument(
'--dut',
type=cli.argtype_notempty,
metavar='DUT',
default=configure.get('DUT'),
help='Address of DUT',
)
parser.add_argument(
'rev',
nargs='?',
type=cli.argtype_notempty,
metavar='REV',
default=configure.get('REV', ''),
help='ChromeOS version, Chrome version, or Chrome local build intra '
'version (in format "%s"). For ChromeOS version, the corresponding '
'Chrome version will be determined by ChromeOS build.'
% codechange.make_intra_rev('X', 'Y', 3),
)
parser.add_argument(
'--board',
metavar='BOARD',
default=configure.get('BOARD'),
help='ChromeOS board name',
)
parser.add_argument(
'--chrome_root',
metavar='CHROME_ROOT',
type=cli.argtype_dir_path,
default=configure.get('CHROME_ROOT', ''),
help='Root of Chrome source tree, like ~/chromium',
)
parser.add_argument(
'--chrome_mirror',
metavar='CHROME_MIRROR',
type=cli.argtype_dir_path,
default=configure.get('CHROME_MIRROR', ''),
help='gclient cache dir',
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
'--target',
action='append',
help='Binary to build and deploy, like unittest or fuzzers. '
'Example value: "video_decode_accelerator_unittest". '
'This option could be specified multiple times.',
)
group.add_argument(
'--without_tests',
action='store_false',
dest='with_tests',
help='(if --target is not specified) '
'Besides chrome, build test binaries as well.',
)
parser.add_argument(
'--deploy_method',
choices=['chrome_deploy', 'image'],
default='chrome_deploy',
help='Deploy method (default: %(default)s)',
)
parser.add_argument(
'--skip_sync_code',
action='store_true',
help='Skip the step syncing source code',
)
parser.add_argument(
'--nobuild',
action='store_true',
help='Stop after source code sync; do not build; imply --nodeploy',
)
parser.add_argument(
'--nodeploy', action='store_true', help='Do not deploy after build'
)
group = parser.add_argument_group(
title='Options for building chrome in chromeos sdk chroot'
)
group.add_argument(
'--noimage',
action='store_true',
help='Build packages only; do not build image; imply --nodeploy',
)
group.add_argument(
'--chromeos_root',
type=cli.argtype_dir_path,
metavar='CHROMEOS_ROOT',
default=configure.get('CHROMEOS_ROOT'),
help='ChromeOS tree root; only necessary if deploy_method is '
'cros_deploy or image',
)
switch_cros_localbuild.add_build_and_deploy_arguments(group)
return parser
def _apply_additional_patch(root_dir, rev):
# TODO(kcwu): generalize patching mechanism to make it user configurable.
base_rev, _, _ = codechange.parse_intra_rev(rev)
# Hack to disable assistant
# Assistant use custom build rules, which may break build randomly (see
# b/126633034 and b/127926563).
# This patch file can support up to 73.0.3669.0, for easier versions, let
# it go (just rely on retry).
if cr_util.is_version_lesseq(
'73.0.3669.0', base_rev
) and cr_util.is_version_lesseq(base_rev, '74.0.3699.0'):
patch_file = os.path.join(
bisect_kit.BISECT_KIT_ROOT, 'patching/0001-disable-assistant.patch'
)
path = os.path.join(root_dir, 'src')
util.check_call('git', 'am', patch_file, cwd=path)
def switch_main(args: typing.Optional[tuple[str]]):
parser = create_argument_parser()
opts = parser.parse_args(args)
common.config_logging(opts)
# --nobuild imply --nodeploy
if opts.nobuild:
opts.nodeploy = True
if opts.skip_sync_code and opts.nobuild:
raise errors.ArgumentError(
'--skip_sync_code and --nobuild', 'nothing to do'
)
if not opts.dut:
if not opts.nodeploy:
raise errors.ArgumentError(
'--dut', 'DUT can be omitted only if --nodeploy'
)
if not opts.board:
raise errors.ArgumentError(
'--board', 'board must be specified if no --dut'
)
else:
if cros_lab_util.is_satlab_dut(opts.dut):
cros_lab_util.write_satlab_ssh_config(opts.dut)
if not cros_util.is_dut(opts.dut):
raise errors.BrokenDutException(
'%r is not a valid DUT address' % opts.dut
)
if not opts.board:
opts.board = cros_util.query_dut_board(opts.dut)
if opts.deploy_method != 'chrome_deploy':
if not opts.chromeos_root:
raise errors.ArgumentError(
'--chromeos_root',
'should be specified for --deploy_method=' + opts.deploy_method,
)
opts.chrome_src = os.path.join(opts.chrome_root, 'src')
assert os.path.exists(opts.chrome_src)
if opts.dut:
dut_os_version = cros_util.query_dut_short_version(opts.dut)
opts.rev = bisect_cr_localbuild_internal.guess_chrome_version(
opts, opts.rev
)
config = dict(
chrome_root=opts.chrome_root, chrome_mirror=opts.chrome_mirror
)
cache = gclient_util.GclientCache(opts.chrome_mirror)
spec_manager = cr_util.ChromeSpecManager(config)
code_manager = codechange.CodeManager(opts.chrome_root, spec_manager, cache)
if not opts.skip_sync_code:
logger.info('switch source code')
code_manager.switch(opts.rev)
_apply_additional_patch(opts.chrome_root, opts.rev)
if opts.nobuild:
return
if cr_util.is_chrome_version(opts.rev):
# Use the default gclient file.
gclientfile = None
else:
# Because the DEPS file referenced in .gclient file is pointing to the DEPS
# snapshot, runhook may fail due to DEPS file changes in the infra versions
# (b/129100282). Here we create a temporary gclient file pointing to the
# live DEPS in the tree. Note this gclient file is used only for runhook,
# not for code sync.
gclientfile = '.gclient-for-runhook'
custom_var_list = [
'checkout_src_internal=True',
'cros_boards="arm-generic:amd64-generic"',
'checkout_lacros_sdk=True',
]
gclient_util.config(
opts.chrome_root,
url='https://chromium.googlesource.com/chromium/src.git',
gclientfile=gclientfile,
custom_var_list=custom_var_list,
cache_dir=opts.chrome_mirror,
target_os='chromeos',
)
gclient_util.runhook(opts.chrome_root, gclientfile=gclientfile)
# TODO(kcwu): support local patch.
if opts.deploy_method == 'chrome_deploy':
cr_util.build_and_deploy(
opts.chrome_src,
opts.board,
opts.dut,
opts.target,
opts.with_tests,
nodeploy=opts.nodeploy,
)
elif opts.deploy_method == 'image':
switch_cros_localbuild.build_and_deploy(opts)
lacros_util.build_and_deploy(opts.chrome_src, opts.dut)
# Sanity check. The OS version should not change.
if opts.dut:
assert dut_os_version == cros_util.query_dut_short_version(
opts.dut
), 'Someone else reflashed the DUT. DUT locking is not respected?'
def main(args: typing.Optional[tuple[str]] = None) -> int:
return bisector_cli.switch_main_wrapper(switch_main, args)
if __name__ == '__main__':
sys.exit(main())