OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 import collections | 7 import collections |
8 import copy | 8 import copy |
9 import json | 9 import json |
10 import optparse | |
11 import os | 10 import os |
12 import pipes | 11 import pipes |
| 12 import re |
13 import subprocess | 13 import subprocess |
14 import sys | 14 import sys |
15 | 15 |
16 import bb_utils | 16 import bb_utils |
17 | 17 |
18 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | 18 BotConfig = collections.namedtuple( |
19 from pylib import buildbot_report | 19 'BotConfig', ['bot_id', 'host_obj', 'test_obj']) |
20 | 20 |
21 CHROME_SRC = os.path.abspath( | 21 HostConfig = collections.namedtuple( |
22 os.path.join(os.path.dirname(__file__), '..', '..', '..')) | 22 'HostConfig', ['host_step_args', 'extra_gyp_defines', 'target_arch']) |
23 | 23 |
24 GLOBAL_SLAVE_PROPS = {} | 24 TestConfig = collections.namedtuple('Tests', ['tests', 'extra_args']) |
25 | 25 |
26 BotConfig = collections.namedtuple( | |
27 'BotConfig', ['bot_id', 'host_opts', 'test_obj', 'slave_props']) | |
28 TestConfig = collections.namedtuple('Tests', ['tests', 'extra_args']) | |
29 Command = collections.namedtuple( | |
30 'Command', ['step_name', 'command', 'testing_cmd']) | |
31 | 26 |
32 CommandToString = bb_utils.CommandToString | 27 def DictDiff(d1, d2): |
| 28 diff = [] |
| 29 for key in sorted(set(d1.keys() + d2.keys())): |
| 30 if key in d1 and d1[key] != d2.get(key): |
| 31 diff.append('- %s=%s' % (key, pipes.quote(d1[key]))) |
| 32 if key in d2 and d2[key] != d1.get(key): |
| 33 diff.append('+ %s=%s' % (key, pipes.quote(d2[key]))) |
| 34 return '\n'.join(diff) |
| 35 |
| 36 |
| 37 def GetEnvironment(host_obj): |
| 38 init_env = dict(os.environ) |
| 39 init_env['GYP_GENERATORS'] = 'ninja' |
| 40 init_env['GOMA_DIR'] = bb_utils.GOMA_DIR |
| 41 envsetup_cmd = '. build/android/envsetup.sh' |
| 42 if host_obj.target_arch: |
| 43 envsetup_cmd += ' --target_arch=%s' % host_obj.target_arch |
| 44 print 'Running %s' % envsetup_cmd |
| 45 proc = subprocess.Popen(['bash', '-exc', |
| 46 envsetup_cmd + ' >&2; python build/android/buildbot/env_to_json.py'], |
| 47 stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| 48 cwd=bb_utils.CHROME_SRC, env=init_env) |
| 49 json_env, envsetup_output = proc.communicate() |
| 50 if proc.returncode != 0: |
| 51 print 'FATAL Failure in envsetup.' |
| 52 print envsetup_output |
| 53 sys.exit(1) |
| 54 env = json.loads(json_env) |
| 55 env['GYP_DEFINES'] = env.get('GYP_DEFINES', '') + ' fastbuild=1' |
| 56 extra_gyp = host_obj.extra_gyp_defines |
| 57 if extra_gyp: |
| 58 env['GYP_DEFINES'] += ' %s' % extra_gyp |
| 59 if re.search('(asan|clang)=1', extra_gyp): |
| 60 env.pop('CXX_target', None) |
| 61 |
| 62 # Bots checkout chrome in /b/build/slave/<name>/build/src |
| 63 build_internal_android = os.path.abspath(os.path.join( |
| 64 bb_utils.CHROME_SRC, '..', '..', '..', '..', '..', 'build_internal', |
| 65 'scripts', 'slave', 'android')) |
| 66 if os.path.exists(build_internal_android): |
| 67 env['PATH'] = os.pathsep.join([build_internal_android, env['PATH']]) |
| 68 return env |
33 | 69 |
34 | 70 |
35 def GetCommands(options, bot_config): | 71 def GetCommands(options, bot_config): |
36 """Get a formatted list of commands. | 72 """Get a formatted list of commands. |
37 | 73 |
38 Args: | 74 Args: |
39 options: Options object. | 75 options: Options object. |
40 bot_config: A BotConfig named tuple. | 76 bot_config: A BotConfig named tuple. |
41 Returns: | 77 Returns: |
42 list of Command objects. | 78 list of Command objects. |
43 """ | 79 """ |
44 slave_props = dict(GLOBAL_SLAVE_PROPS) | 80 property_args = bb_utils.EncodeProperties(options) |
45 if bot_config.slave_props: | 81 commands = [['build/android/buildbot/bb_host_steps.py'] + |
46 slave_props.update(bot_config.slave_props) | 82 bot_config.host_obj.host_step_args + property_args] |
47 | |
48 slave_properties = json.dumps(slave_props) | |
49 property_args = [ | |
50 '--factory-properties=%s' % json.dumps(options.factory_properties), | |
51 '--build-properties=%s' % json.dumps(options.build_properties), | |
52 '--slave-properties=%s' % slave_properties] | |
53 | |
54 commands = [] | |
55 def WrapWithBash(command): | |
56 """Wrap a bash command string with envsetup scripts.""" | |
57 return ['bash', '-exc', '; '.join([ | |
58 '. build/android/buildbot/buildbot_functions.sh', | |
59 'bb_baseline_setup %s --slave-properties=%s' % ( | |
60 CHROME_SRC, pipes.quote(slave_properties)), | |
61 command]) | |
62 ] | |
63 | |
64 if bot_config.host_opts: | |
65 host_cmd = (['build/android/buildbot/bb_host_steps.py'] + | |
66 bot_config.host_opts + property_args) | |
67 commands.append(Command( | |
68 'Host steps', | |
69 WrapWithBash(' '.join(map(pipes.quote, host_cmd))), host_cmd)) | |
70 | 83 |
71 test_obj = bot_config.test_obj | 84 test_obj = bot_config.test_obj |
72 if test_obj: | 85 if test_obj: |
73 run_test_cmd = [ | 86 run_test_cmd = [ |
74 'build/android/buildbot/bb_device_steps.py', '--reboot'] + property_args | 87 'build/android/buildbot/bb_device_steps.py', '--reboot'] + property_args |
75 for test in test_obj.tests: | 88 for test in test_obj.tests: |
76 run_test_cmd.extend(['-f', test]) | 89 run_test_cmd.extend(['-f', test]) |
77 if test_obj.extra_args: | 90 if test_obj.extra_args: |
78 run_test_cmd.extend(test_obj.extra_args) | 91 run_test_cmd.extend(test_obj.extra_args) |
79 commands.append(Command( | 92 commands.append(run_test_cmd) |
80 'Run tests', | |
81 WrapWithBash(' '.join(map(pipes.quote, run_test_cmd))), run_test_cmd)) | |
82 | |
83 return commands | 93 return commands |
84 | 94 |
85 | 95 |
86 def GetBotStepMap(): | 96 def GetBotStepMap(): |
87 compile_opt = ['--compile'] | 97 compile_opt = ['--compile'] |
88 std_host_tests = ['--host-tests=check_webview_licenses,findbugs'] | 98 std_host_tests = ['--host-tests=check_webview_licenses,findbugs'] |
89 std_build_opts = ['--compile', '--zip-build'] | 99 std_build_opts = ['--compile', '--zip-build'] |
90 std_test_opts = ['--extract-build'] | 100 std_test_opts = ['--extract-build'] |
91 std_tests = ['ui', 'unit'] | 101 std_tests = ['ui', 'unit'] |
92 flakiness_server = '--upload-to-flakiness-server' | 102 flakiness_server = '--upload-to-flakiness-server' |
93 extra_gyp = 'extra_gyp_defines' | |
94 | 103 |
95 def B(bot_id, bash_funs, test_obj=None, slave_props=None): | 104 def B(bot_id, host_object, test_object=None): |
96 return BotConfig(bot_id, bash_funs, test_obj, slave_props) | 105 return BotConfig(bot_id, host_object, test_object) |
97 | 106 |
98 def T(tests, extra_args=None): | 107 def T(tests, extra_args=None): |
99 return TestConfig(tests, extra_args) | 108 return TestConfig(tests, extra_args) |
100 | 109 |
| 110 def H(host_step_args, extra_gyp=None, target_arch=None): |
| 111 return HostConfig(host_step_args, extra_gyp, target_arch) |
| 112 |
101 bot_configs = [ | 113 bot_configs = [ |
102 # Main builders | 114 # Main builders |
103 B('main-builder-dbg', std_build_opts + std_host_tests), | 115 B('main-builder-dbg', H(std_build_opts + std_host_tests)), |
104 B('main-builder-rel', std_build_opts), | 116 B('main-builder-rel', H(std_build_opts)), |
105 B('main-clang-builder', compile_opt, slave_props={extra_gyp: 'clang=1'}), | 117 B('main-clang-builder', H(compile_opt, 'clang=1')), |
106 B('main-clobber', compile_opt), | 118 B('main-clobber', H(compile_opt)), |
107 B('main-tests', std_test_opts, T(std_tests, [flakiness_server])), | 119 B('main-tests', H(std_test_opts), T(std_tests, [flakiness_server])), |
108 | 120 |
109 # Other waterfalls | 121 # Other waterfalls |
110 B('asan-builder-tests', compile_opt + ['--update-clang'], | 122 B('asan-builder-tests', H(compile_opt, 'asan=1'), |
111 T(std_tests, ['--asan']), {extra_gyp: 'asan=1'}), | 123 T(std_tests, ['--asan'])), |
112 B('chromedriver-fyi-tests-dbg', std_test_opts, | 124 B('chromedriver-fyi-tests-dbg', H(std_test_opts), |
113 T(['chromedriver'], ['--install=ChromiumTestShell'])), | 125 T(['chromedriver'], ['--install=ChromiumTestShell'])), |
114 B('fyi-builder-dbg', | 126 B('fyi-builder-dbg', |
115 std_build_opts + std_host_tests + ['--experimental']), | 127 H(std_build_opts + std_host_tests + ['--experimental'])), |
116 B('fyi-builder-rel', std_build_opts + ['--experimental']), | 128 B('fyi-builder-rel', H(std_build_opts + ['--experimental'])), |
117 B('fyi-tests-dbg-ics-gn', compile_opt + [ '--experimental'], | 129 B('fyi-tests-dbg-ics-gn', H(compile_opt + [ '--experimental']), |
118 T(std_tests, ['--experimental', flakiness_server])), | 130 T(std_tests, ['--experimental', flakiness_server])), |
119 B('fyi-tests', std_test_opts, | 131 B('fyi-tests', H(std_test_opts), |
120 T(std_tests, ['--experimental', flakiness_server])), | 132 T(std_tests, ['--experimental', flakiness_server])), |
121 B('fyi-component-builder-tests-dbg', compile_opt, | 133 B('fyi-component-builder-tests-dbg', |
122 T(std_tests, ['--experimental', flakiness_server]), | 134 H(compile_opt, 'component=shared_library'), |
123 {extra_gyp: 'component=shared_library'}), | 135 T(std_tests, ['--experimental', flakiness_server])), |
124 B('perf-tests-rel', std_test_opts, T([], ['--install=ContentShell'])), | 136 B('perf-tests-rel', H(std_test_opts), T([], ['--install=ContentShell'])), |
125 B('webkit-latest-webkit-tests', std_test_opts, | 137 B('webkit-latest-webkit-tests', H(std_test_opts), |
126 T(['webkit_layout', 'webkit'])), | 138 T(['webkit_layout', 'webkit'])), |
127 B('webkit-latest-contentshell', compile_opt, T(['webkit_layout'])), | 139 B('webkit-latest-contentshell', H(compile_opt), T(['webkit_layout'])), |
128 B('builder-unit-tests', compile_opt, T(['unit'])), | 140 B('builder-unit-tests', H(compile_opt), T(['unit'])), |
129 | 141 |
130 # Generic builder config (for substring match). | 142 # Generic builder config (for substring match). |
131 B('builder', std_build_opts), | 143 B('builder', H(std_build_opts)), |
132 ] | 144 ] |
133 | 145 |
134 bot_map = dict((config.bot_id, config) for config in bot_configs) | 146 bot_map = dict((config.bot_id, config) for config in bot_configs) |
135 | 147 |
136 # These bots have identical configuration to ones defined earlier. | 148 # These bots have identical configuration to ones defined earlier. |
137 copy_map = [ | 149 copy_map = [ |
138 ('lkgr-clobber', 'main-clobber'), | 150 ('lkgr-clobber', 'main-clobber'), |
139 ('try-builder-dbg', 'main-builder-dbg'), | 151 ('try-builder-dbg', 'main-builder-dbg'), |
140 ('try-builder-rel', 'main-builder-rel'), | 152 ('try-builder-rel', 'main-builder-rel'), |
141 ('try-clang-builder', 'main-clang-builder'), | 153 ('try-clang-builder', 'main-clang-builder'), |
(...skipping 11 matching lines...) Expand all Loading... |
153 # identical in configuration to their trunk building counterparts. | 165 # identical in configuration to their trunk building counterparts. |
154 test_obj = bot_map[to_id].test_obj | 166 test_obj = bot_map[to_id].test_obj |
155 if to_id.startswith('try') and test_obj: | 167 if to_id.startswith('try') and test_obj: |
156 extra_args = test_obj.extra_args | 168 extra_args = test_obj.extra_args |
157 if extra_args and flakiness_server in extra_args: | 169 if extra_args and flakiness_server in extra_args: |
158 extra_args.remove(flakiness_server) | 170 extra_args.remove(flakiness_server) |
159 return bot_map | 171 return bot_map |
160 | 172 |
161 | 173 |
162 def main(argv): | 174 def main(argv): |
163 parser = optparse.OptionParser() | 175 parser = bb_utils.GetParser() |
164 | |
165 def ConvertJson(option, _, value, parser): | |
166 setattr(parser.values, option.dest, json.loads(value)) | |
167 | |
168 parser.add_option('--build-properties', action='callback', | |
169 callback=ConvertJson, type='string', default={}, | |
170 help='build properties in JSON format') | |
171 parser.add_option('--factory-properties', action='callback', | |
172 callback=ConvertJson, type='string', default={}, | |
173 help='factory properties in JSON format') | |
174 parser.add_option('--bot-id', help='Specify bot id directly.') | 176 parser.add_option('--bot-id', help='Specify bot id directly.') |
175 parser.add_option('--TESTING', action='store_true', | 177 parser.add_option('--testing', action='store_true', |
176 help='For testing: print, but do not run commands') | 178 help='For testing: print, but do not run commands') |
177 options, args = parser.parse_args(argv[1:]) | 179 options, args = parser.parse_args(argv[1:]) |
178 if args: | 180 if args: |
179 parser.error('Unused args: %s' % args) | 181 parser.error('Unused args: %s' % args) |
180 | 182 |
181 bot_id = options.bot_id or options.factory_properties.get('android_bot_id') | 183 bot_id = options.bot_id or options.factory_properties.get('android_bot_id') |
182 if not bot_id: | 184 if not bot_id: |
183 parser.error('A bot id must be specified through option or factory_props.') | 185 parser.error('A bot id must be specified through option or factory_props.') |
184 | 186 |
185 # Get a BotConfig object looking first for an exact bot-id match. If no exact | 187 # Get a BotConfig object looking first for an exact bot-id match. If no exact |
186 # match, look for a bot-id which is a substring of the specified id. | 188 # match, look for a bot-id which is a substring of the specified id. |
187 # This allows similar bots to have unique IDs, but to share config. | 189 # This allows similar bots to have unique IDs, but to share config. |
188 # If multiple substring matches exist, pick the longest one. | 190 # If multiple substring matches exist, pick the longest one. |
189 bot_map = GetBotStepMap() | 191 bot_map = GetBotStepMap() |
190 bot_config = bot_map.get(bot_id) | 192 bot_config = bot_map.get(bot_id) |
191 if not bot_config: | 193 if not bot_config: |
192 substring_matches = filter(lambda x: x in bot_id, bot_map.iterkeys()) | 194 substring_matches = filter(lambda x: x in bot_id, bot_map.iterkeys()) |
193 if substring_matches: | 195 if substring_matches: |
194 max_id = max(substring_matches, key=len) | 196 max_id = max(substring_matches, key=len) |
195 print 'Using config from id="%s" (substring match).' % max_id | 197 print 'Using config from id="%s" (substring match).' % max_id |
196 bot_config = bot_map[max_id] | 198 bot_config = bot_map[max_id] |
197 if not bot_config: | 199 if not bot_config: |
198 print 'Error: config for id="%s" cannot be inferred.' % bot_id | 200 print 'Error: config for id="%s" cannot be inferred.' % bot_id |
199 return 1 | 201 return 1 |
200 | 202 |
201 print 'Using config:', bot_config | 203 print 'Using config:', bot_config |
202 | 204 |
203 command_objs = GetCommands(options, bot_config) | 205 commands = GetCommands(options, bot_config) |
204 for command_obj in command_objs: | 206 for command in commands: |
205 print 'Will run:', CommandToString(command_obj.command) | 207 print 'Will run: ', bb_utils.CommandToString(command) |
206 | 208 |
207 for command_obj in command_objs: | 209 env = GetEnvironment(bot_config.host_obj) |
208 if command_obj.step_name: | 210 print 'Environment changes:' |
209 buildbot_report.PrintNamedStep(command_obj.step_name) | 211 print DictDiff(dict(os.environ), env) |
210 command = command_obj.command | 212 |
211 print CommandToString(command) | 213 for command in commands: |
| 214 print bb_utils.CommandToString(command) |
212 sys.stdout.flush() | 215 sys.stdout.flush() |
213 env = None | 216 if options.testing: |
214 if options.TESTING: | 217 env['BUILDBOT_TESTING'] = '1' |
215 if not command_obj.testing_cmd: | 218 return_code = subprocess.call(command, cwd=bb_utils.CHROME_SRC, env=env) |
216 continue | |
217 return_code = subprocess.call( | |
218 command_obj.testing_cmd, | |
219 cwd=CHROME_SRC, | |
220 env=dict(os.environ, BUILDBOT_TESTING='1')) | |
221 else: | |
222 return_code = subprocess.call(command, cwd=CHROME_SRC, env=env) | |
223 if return_code != 0: | 219 if return_code != 0: |
224 return return_code | 220 return return_code |
225 | 221 |
226 | 222 |
227 if __name__ == '__main__': | 223 if __name__ == '__main__': |
228 sys.exit(main(sys.argv)) | 224 sys.exit(main(sys.argv)) |
OLD | NEW |