blob: 0297555aa53d97a26ef46dedd67633925710590c [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
20from bisect_kit import gclient_util
21from bisect_kit import util
22
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'
29 rev = cros_util.query_chrome_version(opts.board, rev)
30 assert cr_util.is_chrome_version(rev)
31
32 return rev
33
34
35class ChromeSrcDomain(core.BisectDomain):
36 """BisectDomain for Chrome branched tree"""
Kuang-che Wu752228c2018-09-05 13:54:22 +080037 revtype = staticmethod(
38 cli.argtype_multiplexer(cr_util.argtype_chrome_version,
39 cros_util.argtype_cros_version))
40 intra_revtype = staticmethod(
41 codechange.argtype_intra_rev(cr_util.argtype_chrome_version))
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080042 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()