blob: 8a08ef656690852e604bf5322f53d560497cc75b [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 Wu3eb6b502018-06-06 16:15:18 +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"""Chrome bisector to bisect a range of chrome commits.
7
8This bisector bisects commits between branched chrome and releases.
9"""
10from __future__ import print_function
11import logging
12import os
13
Kuang-che Wu2526a672019-09-10 16:23:59 +080014from bisect_kit import bisector_cli
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080015from bisect_kit import cli
16from bisect_kit import codechange
17from bisect_kit import configure
18from bisect_kit import core
19from bisect_kit import cros_util
20from bisect_kit import cr_util
Kuang-che Wue121fae2018-11-09 16:18:39 +080021from bisect_kit import errors
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080022from bisect_kit import gclient_util
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080023
24logger = logging.getLogger(__name__)
25
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080026
27def guess_chrome_version(opts, rev):
28 if cros_util.is_cros_version(rev):
29 assert opts.board, 'need to specify BOARD for cros version'
Kuang-che Wu8a28a9d2018-09-11 17:43:36 +080030 chrome_version = cros_util.query_chrome_version(opts.board, rev)
31 assert cr_util.is_chrome_version(chrome_version)
32 logger.info('Converted given CrOS version %s to Chrome version %s', rev,
33 chrome_version)
34 rev = chrome_version
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080035
36 return rev
37
38
Kuang-che Wue80bb872018-11-15 19:45:25 +080039def generate_action_link(action):
40 if action['action_type'] == 'commit':
41 repo_url = action['repo_url']
42 # normalize
43 if repo_url == 'https://chromium.googlesource.com/a/chromium/src.git':
44 repo_url = 'https://chromium.googlesource.com/chromium/src.git'
45 action['link'] = repo_url + '/+/' + action['rev']
46
47
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080048class ChromeSrcDomain(core.BisectDomain):
49 """BisectDomain for Chrome branched tree"""
Kuang-che Wu752228c2018-09-05 13:54:22 +080050 revtype = staticmethod(
51 cli.argtype_multiplexer(cr_util.argtype_chrome_version,
52 cros_util.argtype_cros_version))
53 intra_revtype = staticmethod(
54 codechange.argtype_intra_rev(cr_util.argtype_chrome_version))
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080055 help = globals()['__doc__']
56
57 @staticmethod
58 def add_init_arguments(parser):
59 parser.add_argument(
60 '--chrome_root',
61 required=True,
62 metavar='CHROME_ROOT',
63 type=cli.argtype_dir_path,
64 default=configure.get('CHROME_ROOT'),
65 help='Root of chrome source tree, like ~/chromium')
66 parser.add_argument(
Kuang-che Wud8fc9572018-10-03 21:00:41 +080067 '--chrome_mirror',
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080068 required=True,
Kuang-che Wud8fc9572018-10-03 21:00:41 +080069 metavar='CHROME_MIRROR',
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080070 type=cli.argtype_dir_path,
Kuang-che Wud8fc9572018-10-03 21:00:41 +080071 default=configure.get('CHROME_MIRROR'),
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080072 help='gclient cache dir')
73
74 # Only used for Chrome on ChromeOS.
75 parser.add_argument(
76 '--dut',
77 type=cli.argtype_notempty,
78 metavar='DUT',
79 default=configure.get('DUT'),
80 help='For ChromeOS, address of DUT (Device Under Test)')
81 parser.add_argument(
82 '--board',
83 metavar='BOARD',
84 default=configure.get('BOARD'),
85 help='For ChromeOS, board name')
86
87 @staticmethod
88 def init(opts):
89 chrome_src = os.path.join(opts.chrome_root, 'src')
90 if not os.path.exists(chrome_src):
Kuang-che Wue121fae2018-11-09 16:18:39 +080091 raise errors.ArgumentError('--chrome_root',
92 "chrome src directory doesn't exist")
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080093
94 if opts.dut:
Kuang-che Wue121fae2018-11-09 16:18:39 +080095 if not cros_util.is_dut(opts.dut):
96 raise errors.ArgumentError('--dut', 'invalid DUT or unable to connect')
97
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080098 if not opts.board:
99 opts.board = cros_util.query_dut_board(opts.dut)
100
101 old = guess_chrome_version(opts, opts.old)
102 new = guess_chrome_version(opts, opts.new)
Kuang-che Wue121fae2018-11-09 16:18:39 +0800103 if old == new:
104 raise errors.ArgumentError(
105 '--old and --new', 'start and end of chrome versions are identical')
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800106
Kuang-che Wuca7619e2019-03-19 21:38:10 +0800107 if not cr_util.is_version_lesseq(old, new):
Kuang-che Wue121fae2018-11-09 16:18:39 +0800108 raise errors.ArgumentError(
109 '--old and --new',
110 'start chrome version (%s) is newer than end version (%s)' % (old,
111 new))
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800112
113 config = dict(
114 chrome_root=opts.chrome_root,
115 old=old,
116 new=new,
117 board=opts.board,
118 dut=opts.dut,
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800119 chrome_mirror=opts.chrome_mirror)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800120
121 spec_manager = cr_util.ChromeSpecManager(config)
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800122 cache = gclient_util.GclientCache(opts.chrome_mirror)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800123
124 # Initial sync to get all buildspecs.
125 release_deps_file = cr_util.ChromeSpecManager.get_release_deps(new)
126 path = os.path.join(opts.chrome_root, 'buildspec', release_deps_file)
127 if not os.path.exists(path):
128 spec_manager.sync_to_release(new)
129
130 for rev in (old, new):
131 release_deps_file = cr_util.ChromeSpecManager.get_release_deps(rev)
132 path = os.path.join(opts.chrome_root, 'buildspec', release_deps_file)
133 if not os.path.exists(path):
Kuang-che Wue121fae2018-11-09 16:18:39 +0800134 raise errors.InternalError("%s doesn't exist" % path)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800135
Kuang-che Wuca7619e2019-03-19 21:38:10 +0800136 if not cr_util.is_direct_relative_version(old, new):
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800137 logger.warning('old=%s is not parent of new=%s', old, new)
138 old_ancestor = spec_manager.enumerate_ancestor(old)
139 new_ancestor = spec_manager.enumerate_ancestor(new)
140 for rev in reversed(old_ancestor):
141 if rev in new_ancestor:
142 lowest_common_ancestor = rev
143 break
144 else:
Kuang-che Wue121fae2018-11-09 16:18:39 +0800145 raise errors.InternalError(
146 'Unable to find their common ancestor: %s, %s' % (old, new))
Kuang-che Wu74768d32018-09-07 12:03:24 +0800147 logger.warning(
148 'Assume their lowest common ancestor, %s,'
149 'still have expected old behavior as %s', lowest_common_ancestor, old)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800150 config['old'] = old = lowest_common_ancestor
151
Kuang-che Wu8a28a9d2018-09-11 17:43:36 +0800152 # TODO(kcwu): verify full git history is available.
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800153 code_manager = codechange.CodeManager(opts.chrome_root, spec_manager, cache)
154 revlist = code_manager.build_revlist(old, new)
155
156 return config, revlist
157
158 def __init__(self, config):
159 self.config = config
160
161 def setenv(self, env, rev):
162 env['CHROME_ROOT'] = self.config['chrome_root']
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800163 env['CHROME_MIRROR'] = self.config['chrome_mirror']
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800164 env['REV'] = rev
165
166 if self.config['board']:
167 env['BOARD'] = self.config['board']
168 if self.config['dut']:
169 env['DUT'] = self.config['dut']
170
Kuang-che Wue80bb872018-11-15 19:45:25 +0800171 def fill_candidate_summary(self, summary, interesting_indexes):
Kuang-che Wu948a79c2019-06-19 19:13:56 +0800172 if 'current_range' in summary:
173 old, new = summary['current_range']
174 old_base, _, _ = codechange.parse_intra_rev(old)
175 _, new_next, _ = codechange.parse_intra_rev(new)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800176
Kuang-che Wu948a79c2019-06-19 19:13:56 +0800177 log_url = (
178 'https://chromium.googlesource.com/chromium/src/+log/%s..%s?n=10000' %
179 (old_base, new_next))
180 summary['links'] = [
181 {
182 'name': 'change_list',
183 'url': log_url,
184 'note':
185 'The link of change list only lists chrome src/ commits. For '
186 'example, commits inside v8 and third party repos are not '
187 'listed.',
188 },
189 {
190 'name': 'fuller',
191 'url': log_url + '&pretty=fuller',
192 },
193 ]
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800194
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800195 cache = gclient_util.GclientCache(self.config['chrome_mirror'])
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800196 spec_manager = cr_util.ChromeSpecManager(self.config)
197 code_manager = codechange.CodeManager(self.config['chrome_root'],
198 spec_manager, cache)
Kuang-che Wue80bb872018-11-15 19:45:25 +0800199 for i in interesting_indexes:
200 rev_info = summary['rev_info'][i]
201 rev_info.update(code_manager.get_rev_detail(rev_info['rev']))
202 for action in rev_info.get('actions', []):
203 generate_action_link(action)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800204
205
206if __name__ == '__main__':
Kuang-che Wu2526a672019-09-10 16:23:59 +0800207 bisector_cli.BisectorCommandLine(ChromeSrcDomain).main()