blob: 5229f2e9c15e80bf26b29060550dc1baffe4bff8 [file] [log] [blame]
Kuang-che Wu3eb6b502018-06-06 16:15:18 +08001#!/usr/bin/env python2
2# Copyright 2018 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"""Chrome bisector to bisect a range of chrome commits.
6
7This bisector bisects commits between branched chrome and releases.
8"""
9from __future__ import print_function
10import logging
11import os
12
13from bisect_kit import cli
14from bisect_kit import codechange
15from bisect_kit import configure
16from bisect_kit import core
17from bisect_kit import cros_util
18from bisect_kit import cr_util
19from bisect_kit import gclient_util
20from bisect_kit import util
21
22logger = logging.getLogger(__name__)
23
24revtype = cli.argtype_multiplexer(cr_util.argtype_chrome_version,
25 cros_util.argtype_cros_version,
26 codechange.argtype_intra_rev(
27 cr_util.argtype_chrome_version))
28
29
30def guess_chrome_version(opts, rev):
31 if cros_util.is_cros_version(rev):
32 assert opts.board, 'need to specify BOARD for cros version'
33 rev = cros_util.query_chrome_version(opts.board, rev)
34 assert cr_util.is_chrome_version(rev)
35
36 return rev
37
38
39class ChromeSrcDomain(core.BisectDomain):
40 """BisectDomain for Chrome branched tree"""
41 revtype = staticmethod(revtype)
42 help = globals()['__doc__']
43
44 @staticmethod
45 def add_init_arguments(parser):
46 parser.add_argument(
47 '--chrome_root',
48 required=True,
49 metavar='CHROME_ROOT',
50 type=cli.argtype_dir_path,
51 default=configure.get('CHROME_ROOT'),
52 help='Root of chrome source tree, like ~/chromium')
53 parser.add_argument(
54 '--gclient_cache_dir',
55 required=True,
56 metavar='GCLIENT_CACHE_DIR',
57 type=cli.argtype_dir_path,
58 default=configure.get('GCLIENT_CACHE_DIR'),
59 help='gclient cache dir')
60
61 # Only used for Chrome on ChromeOS.
62 parser.add_argument(
63 '--dut',
64 type=cli.argtype_notempty,
65 metavar='DUT',
66 default=configure.get('DUT'),
67 help='For ChromeOS, address of DUT (Device Under Test)')
68 parser.add_argument(
69 '--board',
70 metavar='BOARD',
71 default=configure.get('BOARD'),
72 help='For ChromeOS, board name')
73
74 @staticmethod
75 def init(opts):
76 chrome_src = os.path.join(opts.chrome_root, 'src')
77 if not os.path.exists(chrome_src):
78 raise core.ExecutionFatalError("chrome src directory doesn't exist")
79
80 if opts.dut:
81 assert cros_util.is_dut(opts.dut)
82 if not opts.board:
83 opts.board = cros_util.query_dut_board(opts.dut)
84
85 old = guess_chrome_version(opts, opts.old)
86 new = guess_chrome_version(opts, opts.new)
87 assert old != new
88
89 if not util.is_version_lesseq(old, new):
90 logger.error('old=%s is newer than new=%s !?', old, new)
91 raise Exception
92
93 config = dict(
94 chrome_root=opts.chrome_root,
95 old=old,
96 new=new,
97 board=opts.board,
98 dut=opts.dut,
99 gclient_cache_dir=opts.gclient_cache_dir)
100
101 spec_manager = cr_util.ChromeSpecManager(config)
102 cache = gclient_util.GclientCache(opts.gclient_cache_dir)
103
104 # Initial sync to get all buildspecs.
105 release_deps_file = cr_util.ChromeSpecManager.get_release_deps(new)
106 path = os.path.join(opts.chrome_root, 'buildspec', release_deps_file)
107 if not os.path.exists(path):
108 spec_manager.sync_to_release(new)
109
110 for rev in (old, new):
111 release_deps_file = cr_util.ChromeSpecManager.get_release_deps(rev)
112 path = os.path.join(opts.chrome_root, 'buildspec', release_deps_file)
113 if not os.path.exists(path):
114 raise Exception("%s doesn't exist" % path)
115
116 # Make sure all repos in between are cached
117 float_specs = spec_manager.collect_float_spec(old, new)
118 for spec in reversed(float_specs):
119 spec_manager.parse_spec(spec)
120 if cache.are_spec_commits_available(spec):
121 continue
122 branch = spec.path.split(os.sep)[-2]
123 spec_manager.sync_to_branch_rev(branch, spec.name)
124
125 if not util.is_direct_relative_version(old, new):
126 logger.warning('old=%s is not parent of new=%s', old, new)
127 old_ancestor = spec_manager.enumerate_ancestor(old)
128 new_ancestor = spec_manager.enumerate_ancestor(new)
129 for rev in reversed(old_ancestor):
130 if rev in new_ancestor:
131 lowest_common_ancestor = rev
132 break
133 else:
134 raise Exception('Unable to find their common ancestor')
135 logger.warning('Assume their lowest common ancestor, %s,'
136 'still have expected old behavior as %s',
137 lowest_common_ancestor, old)
138 config['old'] = old = lowest_common_ancestor
139
140 code_manager = codechange.CodeManager(opts.chrome_root, spec_manager, cache)
141 revlist = code_manager.build_revlist(old, new)
142
143 return config, revlist
144
145 def __init__(self, config):
146 self.config = config
147
148 def setenv(self, env, rev):
149 env['CHROME_ROOT'] = self.config['chrome_root']
150 env['GCLIENT_CACHE_DIR'] = self.config['gclient_cache_dir']
151 env['REV'] = rev
152
153 if self.config['board']:
154 env['BOARD'] = self.config['board']
155 if self.config['dut']:
156 env['DUT'] = self.config['dut']
157
158 def view(self, old, new):
159 print('old', old)
160 print('new', new)
161
162 old_base, old_next, _ = codechange.parse_intra_rev(old)
163 new_base, new_next, _ = codechange.parse_intra_rev(new)
164 # Only print log url if the range is within two releases.
165 if old_next in (new_base, new_next):
166 log_url = ('https://chromium.googlesource.com/chromium/src/+log/%s..%s'
167 '?pretty=fuller&n=10000')
168 print('Be careful the following url only lists chrome src/ commits. For '
169 'example, commits inside v8 and third party repos are not listed.')
170 if old_base != old_next:
171 print(log_url % (old_base, old_next))
172 if new_base != new_next and old_next != new_next:
173 print(log_url % (new_base, new_next))
174
175 cache = gclient_util.GclientCache(self.config['gclient_cache_dir'])
176 spec_manager = cr_util.ChromeSpecManager(self.config)
177 code_manager = codechange.CodeManager(self.config['chrome_root'],
178 spec_manager, cache)
179 code_manager.view_rev_diff(old, new)
180
181
182if __name__ == '__main__':
183 cli.BisectorCommandLine(ChromeSrcDomain).main()