blob: f83de68346f33b85590b2e746cc440e63adab871 [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'
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
38class ChromeSrcDomain(core.BisectDomain):
39 """BisectDomain for Chrome branched tree"""
Kuang-che Wu752228c2018-09-05 13:54:22 +080040 revtype = staticmethod(
41 cli.argtype_multiplexer(cr_util.argtype_chrome_version,
42 cros_util.argtype_cros_version))
43 intra_revtype = staticmethod(
44 codechange.argtype_intra_rev(cr_util.argtype_chrome_version))
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080045 help = globals()['__doc__']
46
47 @staticmethod
48 def add_init_arguments(parser):
49 parser.add_argument(
50 '--chrome_root',
51 required=True,
52 metavar='CHROME_ROOT',
53 type=cli.argtype_dir_path,
54 default=configure.get('CHROME_ROOT'),
55 help='Root of chrome source tree, like ~/chromium')
56 parser.add_argument(
Kuang-che Wud8fc9572018-10-03 21:00:41 +080057 '--chrome_mirror',
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080058 required=True,
Kuang-che Wud8fc9572018-10-03 21:00:41 +080059 metavar='CHROME_MIRROR',
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080060 type=cli.argtype_dir_path,
Kuang-che Wud8fc9572018-10-03 21:00:41 +080061 default=configure.get('CHROME_MIRROR'),
Kuang-che Wu3eb6b502018-06-06 16:15:18 +080062 help='gclient cache dir')
63
64 # Only used for Chrome on ChromeOS.
65 parser.add_argument(
66 '--dut',
67 type=cli.argtype_notempty,
68 metavar='DUT',
69 default=configure.get('DUT'),
70 help='For ChromeOS, address of DUT (Device Under Test)')
71 parser.add_argument(
72 '--board',
73 metavar='BOARD',
74 default=configure.get('BOARD'),
75 help='For ChromeOS, board name')
76
77 @staticmethod
78 def init(opts):
79 chrome_src = os.path.join(opts.chrome_root, 'src')
80 if not os.path.exists(chrome_src):
81 raise core.ExecutionFatalError("chrome src directory doesn't exist")
82
83 if opts.dut:
84 assert cros_util.is_dut(opts.dut)
85 if not opts.board:
86 opts.board = cros_util.query_dut_board(opts.dut)
87
88 old = guess_chrome_version(opts, opts.old)
89 new = guess_chrome_version(opts, opts.new)
90 assert old != new
91
92 if not util.is_version_lesseq(old, new):
93 logger.error('old=%s is newer than new=%s !?', old, new)
94 raise Exception
95
96 config = dict(
97 chrome_root=opts.chrome_root,
98 old=old,
99 new=new,
100 board=opts.board,
101 dut=opts.dut,
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800102 chrome_mirror=opts.chrome_mirror)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800103
104 spec_manager = cr_util.ChromeSpecManager(config)
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800105 cache = gclient_util.GclientCache(opts.chrome_mirror)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800106
107 # Initial sync to get all buildspecs.
108 release_deps_file = cr_util.ChromeSpecManager.get_release_deps(new)
109 path = os.path.join(opts.chrome_root, 'buildspec', release_deps_file)
110 if not os.path.exists(path):
111 spec_manager.sync_to_release(new)
112
113 for rev in (old, new):
114 release_deps_file = cr_util.ChromeSpecManager.get_release_deps(rev)
115 path = os.path.join(opts.chrome_root, 'buildspec', release_deps_file)
116 if not os.path.exists(path):
117 raise Exception("%s doesn't exist" % path)
118
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800119 if not util.is_direct_relative_version(old, new):
120 logger.warning('old=%s is not parent of new=%s', old, new)
121 old_ancestor = spec_manager.enumerate_ancestor(old)
122 new_ancestor = spec_manager.enumerate_ancestor(new)
123 for rev in reversed(old_ancestor):
124 if rev in new_ancestor:
125 lowest_common_ancestor = rev
126 break
127 else:
128 raise Exception('Unable to find their common ancestor')
Kuang-che Wu74768d32018-09-07 12:03:24 +0800129 logger.warning(
130 'Assume their lowest common ancestor, %s,'
131 'still have expected old behavior as %s', lowest_common_ancestor, old)
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800132 config['old'] = old = lowest_common_ancestor
133
Kuang-che Wu8a28a9d2018-09-11 17:43:36 +0800134 # TODO(kcwu): verify full git history is available.
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800135 code_manager = codechange.CodeManager(opts.chrome_root, spec_manager, cache)
136 revlist = code_manager.build_revlist(old, new)
137
138 return config, revlist
139
140 def __init__(self, config):
141 self.config = config
142
143 def setenv(self, env, rev):
144 env['CHROME_ROOT'] = self.config['chrome_root']
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800145 env['CHROME_MIRROR'] = self.config['chrome_mirror']
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800146 env['REV'] = rev
147
148 if self.config['board']:
149 env['BOARD'] = self.config['board']
150 if self.config['dut']:
151 env['DUT'] = self.config['dut']
152
153 def view(self, old, new):
154 print('old', old)
155 print('new', new)
156
157 old_base, old_next, _ = codechange.parse_intra_rev(old)
158 new_base, new_next, _ = codechange.parse_intra_rev(new)
159 # Only print log url if the range is within two releases.
160 if old_next in (new_base, new_next):
161 log_url = ('https://chromium.googlesource.com/chromium/src/+log/%s..%s'
162 '?pretty=fuller&n=10000')
163 print('Be careful the following url only lists chrome src/ commits. For '
164 'example, commits inside v8 and third party repos are not listed.')
165 if old_base != old_next:
166 print(log_url % (old_base, old_next))
167 if new_base != new_next and old_next != new_next:
168 print(log_url % (new_base, new_next))
169
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800170 cache = gclient_util.GclientCache(self.config['chrome_mirror'])
Kuang-che Wu3eb6b502018-06-06 16:15:18 +0800171 spec_manager = cr_util.ChromeSpecManager(self.config)
172 code_manager = codechange.CodeManager(self.config['chrome_root'],
173 spec_manager, cache)
174 code_manager.view_rev_diff(old, new)
175
176
177if __name__ == '__main__':
178 cli.BisectorCommandLine(ChromeSrcDomain).main()