blob: 0283aa3cf1fff44edf87bcc88a97e9c38da3b86f [file] [log] [blame]
Kuang-che Wu3eb6b502018-06-06 16:15:18 +08001#!/usr/bin/env python2
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
14from bisect_kit import cli
15from bisect_kit import codechange
16from bisect_kit import configure
17from bisect_kit import core
18from bisect_kit import cros_util
19from bisect_kit import cr_util
Kuang-che Wue121fae2018-11-09 16:18:39 +080020from bisect_kit import errors
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080021from bisect_kit import gclient_util
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080022
23logger = logging.getLogger(__name__)
24
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080025
26def guess_chrome_version(opts, rev):
27 if cros_util.is_cros_version(rev):
28 assert opts.board, 'need to specify BOARD for cros version'
Kuang-che Wu8a28a9d2018-09-11 17:43:36 +080029 chrome_version = cros_util.query_chrome_version(opts.board, rev)
30 assert cr_util.is_chrome_version(chrome_version)
31 logger.info('Converted given CrOS version %s to Chrome version %s', rev,
32 chrome_version)
33 rev = chrome_version
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080034
35 return rev
36
37
Kuang-che Wue80bb872018-11-15 19:45:25 +080038def generate_action_link(action):
39 if action['action_type'] == 'commit':
40 repo_url = action['repo_url']
41 # normalize
42 if repo_url == 'https://chromium.googlesource.com/a/chromium/src.git':
43 repo_url = 'https://chromium.googlesource.com/chromium/src.git'
44 action['link'] = repo_url + '/+/' + action['rev']
45
46
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080047class ChromeSrcDomain(core.BisectDomain):
48 """BisectDomain for Chrome branched tree"""
Kuang-che Wu752228c2018-09-05 13:54:22 +080049 revtype = staticmethod(
50 cli.argtype_multiplexer(cr_util.argtype_chrome_version,
51 cros_util.argtype_cros_version))
52 intra_revtype = staticmethod(
53 codechange.argtype_intra_rev(cr_util.argtype_chrome_version))
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080054 help = globals()['__doc__']
55
56 @staticmethod
57 def add_init_arguments(parser):
58 parser.add_argument(
59 '--chrome_root',
60 required=True,
61 metavar='CHROME_ROOT',
62 type=cli.argtype_dir_path,
63 default=configure.get('CHROME_ROOT'),
64 help='Root of chrome source tree, like ~/chromium')
65 parser.add_argument(
Kuang-che Wud8fc9572018-10-03 21:00:41 +080066 '--chrome_mirror',
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080067 required=True,
Kuang-che Wud8fc9572018-10-03 21:00:41 +080068 metavar='CHROME_MIRROR',
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080069 type=cli.argtype_dir_path,
Kuang-che Wud8fc9572018-10-03 21:00:41 +080070 default=configure.get('CHROME_MIRROR'),
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080071 help='gclient cache dir')
72
73 # Only used for Chrome on ChromeOS.
74 parser.add_argument(
75 '--dut',
76 type=cli.argtype_notempty,
77 metavar='DUT',
78 default=configure.get('DUT'),
79 help='For ChromeOS, address of DUT (Device Under Test)')
80 parser.add_argument(
81 '--board',
82 metavar='BOARD',
83 default=configure.get('BOARD'),
84 help='For ChromeOS, board name')
85
86 @staticmethod
87 def init(opts):
88 chrome_src = os.path.join(opts.chrome_root, 'src')
89 if not os.path.exists(chrome_src):
Kuang-che Wue121fae2018-11-09 16:18:39 +080090 raise errors.ArgumentError('--chrome_root',
91 "chrome src directory doesn't exist")
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080092
93 if opts.dut:
Kuang-che Wue121fae2018-11-09 16:18:39 +080094 if not cros_util.is_dut(opts.dut):
95 raise errors.ArgumentError('--dut', 'invalid DUT or unable to connect')
96
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080097 if not opts.board:
98 opts.board = cros_util.query_dut_board(opts.dut)
99
100 old = guess_chrome_version(opts, opts.old)
101 new = guess_chrome_version(opts, opts.new)
Kuang-che Wue121fae2018-11-09 16:18:39 +0800102 if old == new:
103 raise errors.ArgumentError(
104 '--old and --new', 'start and end of chrome versions are identical')
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800105
Kuang-che Wuca7619e2019-03-19 21:38:10 +0800106 if not cr_util.is_version_lesseq(old, new):
Kuang-che Wue121fae2018-11-09 16:18:39 +0800107 raise errors.ArgumentError(
108 '--old and --new',
109 'start chrome version (%s) is newer than end version (%s)' % (old,
110 new))
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800111
112 config = dict(
113 chrome_root=opts.chrome_root,
114 old=old,
115 new=new,
116 board=opts.board,
117 dut=opts.dut,
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800118 chrome_mirror=opts.chrome_mirror)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800119
120 spec_manager = cr_util.ChromeSpecManager(config)
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800121 cache = gclient_util.GclientCache(opts.chrome_mirror)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800122
123 # Initial sync to get all buildspecs.
124 release_deps_file = cr_util.ChromeSpecManager.get_release_deps(new)
125 path = os.path.join(opts.chrome_root, 'buildspec', release_deps_file)
126 if not os.path.exists(path):
127 spec_manager.sync_to_release(new)
128
129 for rev in (old, new):
130 release_deps_file = cr_util.ChromeSpecManager.get_release_deps(rev)
131 path = os.path.join(opts.chrome_root, 'buildspec', release_deps_file)
132 if not os.path.exists(path):
Kuang-che Wue121fae2018-11-09 16:18:39 +0800133 raise errors.InternalError("%s doesn't exist" % path)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800134
Kuang-che Wuca7619e2019-03-19 21:38:10 +0800135 if not cr_util.is_direct_relative_version(old, new):
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800136 logger.warning('old=%s is not parent of new=%s', old, new)
137 old_ancestor = spec_manager.enumerate_ancestor(old)
138 new_ancestor = spec_manager.enumerate_ancestor(new)
139 for rev in reversed(old_ancestor):
140 if rev in new_ancestor:
141 lowest_common_ancestor = rev
142 break
143 else:
Kuang-che Wue121fae2018-11-09 16:18:39 +0800144 raise errors.InternalError(
145 'Unable to find their common ancestor: %s, %s' % (old, new))
Kuang-che Wu74768d32018-09-07 12:03:24 +0800146 logger.warning(
147 'Assume their lowest common ancestor, %s,'
148 'still have expected old behavior as %s', lowest_common_ancestor, old)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800149 config['old'] = old = lowest_common_ancestor
150
Kuang-che Wu8a28a9d2018-09-11 17:43:36 +0800151 # TODO(kcwu): verify full git history is available.
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800152 code_manager = codechange.CodeManager(opts.chrome_root, spec_manager, cache)
153 revlist = code_manager.build_revlist(old, new)
154
155 return config, revlist
156
157 def __init__(self, config):
158 self.config = config
159
160 def setenv(self, env, rev):
161 env['CHROME_ROOT'] = self.config['chrome_root']
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800162 env['CHROME_MIRROR'] = self.config['chrome_mirror']
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800163 env['REV'] = rev
164
165 if self.config['board']:
166 env['BOARD'] = self.config['board']
167 if self.config['dut']:
168 env['DUT'] = self.config['dut']
169
Kuang-che Wue80bb872018-11-15 19:45:25 +0800170 def fill_candidate_summary(self, summary, interesting_indexes):
171 old, new = summary['current_range']
172 old_base, _, _ = codechange.parse_intra_rev(old)
173 _, new_next, _ = codechange.parse_intra_rev(new)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800174
Kuang-che Wue80bb872018-11-15 19:45:25 +0800175 log_url = (
176 'https://chromium.googlesource.com/chromium/src/+log/%s..%s?n=10000' %
177 (old_base, new_next))
Kuang-che Wuaccf9202019-01-04 15:40:42 +0800178 summary['links'] = [
179 {
180 'name': 'change_list',
181 'url': log_url,
182 'note':
183 'The link of change list only lists chrome src/ commits. For '
184 'example, commits inside v8 and third party repos are not '
185 'listed.',
Kuang-che Wue80bb872018-11-15 19:45:25 +0800186 },
Kuang-che Wuaccf9202019-01-04 15:40:42 +0800187 {
188 'name': 'fuller',
189 'url': log_url + '&pretty=fuller',
190 },
191 ]
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800192
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800193 cache = gclient_util.GclientCache(self.config['chrome_mirror'])
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800194 spec_manager = cr_util.ChromeSpecManager(self.config)
195 code_manager = codechange.CodeManager(self.config['chrome_root'],
196 spec_manager, cache)
Kuang-che Wue80bb872018-11-15 19:45:25 +0800197 for i in interesting_indexes:
198 rev_info = summary['rev_info'][i]
199 rev_info.update(code_manager.get_rev_detail(rev_info['rev']))
200 for action in rev_info.get('actions', []):
201 generate_action_link(action)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800202
203
204if __name__ == '__main__':
205 cli.BisectorCommandLine(ChromeSrcDomain).main()