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 os | 10 import os |
11 import pipes | 11 import pipes |
12 import re | 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 BotConfig = collections.namedtuple( | 18 _BotConfig = collections.namedtuple( |
19 'BotConfig', ['bot_id', 'host_obj', 'test_obj']) | 19 'BotConfig', ['bot_id', 'host_obj', 'test_obj']) |
20 | 20 |
21 HostConfig = collections.namedtuple( | 21 HostConfig = collections.namedtuple( |
22 'HostConfig', | 22 'HostConfig', |
23 ['host_steps', 'extra_args', 'extra_gyp_defines', 'target_arch']) | 23 ['script', 'host_steps', 'extra_args', 'extra_gyp_defines', 'target_arch']) |
24 | 24 |
25 TestConfig = collections.namedtuple('Tests', ['tests', 'extra_args']) | 25 TestConfig = collections.namedtuple('Tests', ['script', 'tests', 'extra_args']) |
| 26 |
| 27 |
| 28 def BotConfig(bot_id, host_object, test_object=None): |
| 29 return _BotConfig(bot_id, host_object, test_object) |
26 | 30 |
27 | 31 |
28 def DictDiff(d1, d2): | 32 def DictDiff(d1, d2): |
29 diff = [] | 33 diff = [] |
30 for key in sorted(set(d1.keys() + d2.keys())): | 34 for key in sorted(set(d1.keys() + d2.keys())): |
31 if key in d1 and d1[key] != d2.get(key): | 35 if key in d1 and d1[key] != d2.get(key): |
32 diff.append('- %s=%s' % (key, pipes.quote(d1[key]))) | 36 diff.append('- %s=%s' % (key, pipes.quote(d1[key]))) |
33 if key in d2 and d2[key] != d1.get(key): | 37 if key in d2 and d2[key] != d1.get(key): |
34 diff.append('+ %s=%s' % (key, pipes.quote(d2[key]))) | 38 diff.append('+ %s=%s' % (key, pipes.quote(d2[key]))) |
35 return '\n'.join(diff) | 39 return '\n'.join(diff) |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 | 71 |
68 # Bots checkout chrome in /b/build/slave/<name>/build/src | 72 # Bots checkout chrome in /b/build/slave/<name>/build/src |
69 build_internal_android = os.path.abspath(os.path.join( | 73 build_internal_android = os.path.abspath(os.path.join( |
70 bb_utils.CHROME_SRC, '..', '..', '..', '..', '..', 'build_internal', | 74 bb_utils.CHROME_SRC, '..', '..', '..', '..', '..', 'build_internal', |
71 'scripts', 'slave', 'android')) | 75 'scripts', 'slave', 'android')) |
72 if os.path.exists(build_internal_android): | 76 if os.path.exists(build_internal_android): |
73 env['PATH'] = os.pathsep.join([build_internal_android, env['PATH']]) | 77 env['PATH'] = os.pathsep.join([build_internal_android, env['PATH']]) |
74 return env | 78 return env |
75 | 79 |
76 | 80 |
77 def GetCommands(options, bot_config): | 81 def GetCommands(options, bot_config, host_step_script, device_step_script): |
78 """Get a formatted list of commands. | 82 """Get a formatted list of commands. |
79 | 83 |
80 Args: | 84 Args: |
81 options: Options object. | 85 options: Options object. |
82 bot_config: A BotConfig named tuple. | 86 bot_config: A BotConfig named tuple. |
| 87 host_step_script: Host step script. |
| 88 device_step_script: Device step script. |
83 Returns: | 89 Returns: |
84 list of Command objects. | 90 list of Command objects. |
85 """ | 91 """ |
86 property_args = bb_utils.EncodeProperties(options) | 92 property_args = bb_utils.EncodeProperties(options) |
87 commands = [['build/android/buildbot/bb_host_steps.py'] + | 93 commands = [[host_step_script, |
88 ['--steps=%s' % ','.join(bot_config.host_obj.host_steps)] + | 94 '--steps=%s' % ','.join(bot_config.host_obj.host_steps)] + |
89 property_args + (bot_config.host_obj.extra_args or [])] | 95 property_args + (bot_config.host_obj.extra_args or [])] |
90 | 96 |
91 test_obj = bot_config.test_obj | 97 test_obj = bot_config.test_obj |
92 if test_obj: | 98 if test_obj: |
93 run_test_cmd = [ | 99 run_test_cmd = [device_step_script, '--reboot'] + property_args |
94 'build/android/buildbot/bb_device_steps.py', '--reboot'] + property_args | |
95 for test in test_obj.tests: | 100 for test in test_obj.tests: |
96 run_test_cmd.extend(['-f', test]) | 101 run_test_cmd.extend(['-f', test]) |
97 if test_obj.extra_args: | 102 if test_obj.extra_args: |
98 run_test_cmd.extend(test_obj.extra_args) | 103 run_test_cmd.extend(test_obj.extra_args) |
99 commands.append(run_test_cmd) | 104 commands.append(run_test_cmd) |
100 return commands | 105 return commands |
101 | 106 |
102 | 107 |
103 def GetBotStepMap(): | 108 def GetBotStepMap(): |
104 compile_step = ['compile'] | 109 compile_step = ['compile'] |
105 std_host_tests = ['check_webview_licenses', 'findbugs'] | 110 std_host_tests = ['check_webview_licenses', 'findbugs'] |
106 std_build_steps = ['compile', 'zip_build'] | 111 std_build_steps = ['compile', 'zip_build'] |
107 std_test_steps = ['extract_build'] | 112 std_test_steps = ['extract_build'] |
108 std_tests = ['ui', 'unit'] | 113 std_tests = ['ui', 'unit'] |
109 flakiness_server = '--upload-to-flakiness-server' | 114 flakiness_server = '--upload-to-flakiness-server' |
110 | 115 |
111 def B(bot_id, host_object, test_object=None): | 116 B = BotConfig |
112 return BotConfig(bot_id, host_object, test_object) | 117 H = (lambda steps, extra_args=None, extra_gyp=None, target_arch=None : |
113 | 118 HostConfig('build/android/buildbot/bb_host_steps.py', steps, extra_args, |
114 def T(tests, extra_args=None): | 119 extra_gyp, target_arch)) |
115 return TestConfig(tests, extra_args) | 120 T = (lambda tests, extra_args=None : |
116 | 121 TestConfig('build/android/buildbot/bb_device_steps.py', tests, |
117 def H(host_steps, extra_args=None, extra_gyp=None, target_arch=None): | 122 extra_args)) |
118 return HostConfig(host_steps, extra_args, extra_gyp, target_arch) | |
119 | 123 |
120 bot_configs = [ | 124 bot_configs = [ |
121 # Main builders | 125 # Main builders |
122 B('main-builder-dbg', H(std_build_steps + std_host_tests)), | 126 B('main-builder-dbg', H(std_build_steps + std_host_tests)), |
123 B('main-builder-rel', H(std_build_steps)), | 127 B('main-builder-rel', H(std_build_steps)), |
124 B('main-clang-builder', | 128 B('main-clang-builder', |
125 H(compile_step, extra_gyp='clang=1 component=shared_library')), | 129 H(compile_step, extra_gyp='clang=1 component=shared_library')), |
126 B('main-clobber', H(compile_step)), | 130 B('main-clobber', H(compile_step)), |
127 B('main-tests', H(std_test_steps), T(std_tests, [flakiness_server])), | 131 B('main-tests', H(std_test_steps), T(std_tests, [flakiness_server])), |
128 | 132 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
174 # Trybots do not upload to flakiness dashboard. They should be otherwise | 178 # Trybots do not upload to flakiness dashboard. They should be otherwise |
175 # identical in configuration to their trunk building counterparts. | 179 # identical in configuration to their trunk building counterparts. |
176 test_obj = bot_map[to_id].test_obj | 180 test_obj = bot_map[to_id].test_obj |
177 if to_id.startswith('try') and test_obj: | 181 if to_id.startswith('try') and test_obj: |
178 extra_args = test_obj.extra_args | 182 extra_args = test_obj.extra_args |
179 if extra_args and flakiness_server in extra_args: | 183 if extra_args and flakiness_server in extra_args: |
180 extra_args.remove(flakiness_server) | 184 extra_args.remove(flakiness_server) |
181 return bot_map | 185 return bot_map |
182 | 186 |
183 | 187 |
184 def main(argv): | 188 # Return an object from the map, looking first for an exact id match. |
| 189 # If this fails, look for an id which is a substring of the specified id. |
| 190 # Choose the longest of all substring matches. |
| 191 # pylint: disable=W0622 |
| 192 def GetBestMatch(id_map, id): |
| 193 config = id_map.get(id) |
| 194 if not config: |
| 195 substring_matches = filter(lambda x: x in id, id_map.iterkeys()) |
| 196 if substring_matches: |
| 197 max_id = max(substring_matches, key=len) |
| 198 print 'Using config from id="%s" (substring match).' % max_id |
| 199 config = id_map[max_id] |
| 200 return config |
| 201 |
| 202 |
| 203 def GetRunBotOptParser(): |
185 parser = bb_utils.GetParser() | 204 parser = bb_utils.GetParser() |
186 parser.add_option('--bot-id', help='Specify bot id directly.') | 205 parser.add_option('--bot-id', help='Specify bot id directly.') |
187 parser.add_option('--testing', action='store_true', | 206 parser.add_option('--testing', action='store_true', |
188 help='For testing: print, but do not run commands') | 207 help='For testing: print, but do not run commands') |
| 208 |
| 209 return parser |
| 210 |
| 211 |
| 212 def main(argv): |
| 213 parser = GetRunBotOptParser() |
189 options, args = parser.parse_args(argv[1:]) | 214 options, args = parser.parse_args(argv[1:]) |
190 if args: | 215 if args: |
191 parser.error('Unused args: %s' % args) | 216 parser.error('Unused args: %s' % args) |
192 | 217 |
193 bot_id = options.bot_id or options.factory_properties.get('android_bot_id') | 218 bot_id = options.bot_id or options.factory_properties.get('android_bot_id') |
194 if not bot_id: | 219 if not bot_id: |
195 parser.error('A bot id must be specified through option or factory_props.') | 220 parser.error('A bot id must be specified through option or factory_props.') |
196 | 221 |
197 # Get a BotConfig object looking first for an exact bot-id match. If no exact | 222 bot_config = GetBestMatch(GetBotStepMap(), bot_id) |
198 # match, look for a bot-id which is a substring of the specified id. | |
199 # This allows similar bots to have unique IDs, but to share config. | |
200 # If multiple substring matches exist, pick the longest one. | |
201 bot_map = GetBotStepMap() | |
202 bot_config = bot_map.get(bot_id) | |
203 if not bot_config: | |
204 substring_matches = filter(lambda x: x in bot_id, bot_map.iterkeys()) | |
205 if substring_matches: | |
206 max_id = max(substring_matches, key=len) | |
207 print 'Using config from id="%s" (substring match).' % max_id | |
208 bot_config = bot_map[max_id] | |
209 if not bot_config: | 223 if not bot_config: |
210 print 'Error: config for id="%s" cannot be inferred.' % bot_id | 224 print 'Error: config for id="%s" cannot be inferred.' % bot_id |
211 return 1 | 225 return 1 |
212 | 226 |
213 print 'Using config:', bot_config | 227 print 'Using config:', bot_config |
214 | 228 |
215 commands = GetCommands(options, bot_config) | 229 commands = GetCommands( |
| 230 options, bot_config, |
| 231 'build/android/buildbot/bb_host_steps.py', |
| 232 'build/android/buildbot/bb_device_steps.py') |
216 for command in commands: | 233 for command in commands: |
217 print 'Will run: ', bb_utils.CommandToString(command) | 234 print 'Will run: ', bb_utils.CommandToString(command) |
218 print | 235 print |
219 | 236 |
220 env = GetEnvironment(bot_config.host_obj, options.testing) | 237 env = GetEnvironment(bot_config.host_obj, options.testing) |
221 print 'Environment changes:' | 238 print 'Environment changes:' |
222 print DictDiff(dict(os.environ), env) | 239 print DictDiff(dict(os.environ), env) |
223 | 240 |
224 for command in commands: | 241 for command in commands: |
225 print bb_utils.CommandToString(command) | 242 print bb_utils.CommandToString(command) |
226 sys.stdout.flush() | 243 sys.stdout.flush() |
227 if options.testing: | 244 if options.testing: |
228 env['BUILDBOT_TESTING'] = '1' | 245 env['BUILDBOT_TESTING'] = '1' |
229 return_code = subprocess.call(command, cwd=bb_utils.CHROME_SRC, env=env) | 246 return_code = subprocess.call(command, cwd=bb_utils.CHROME_SRC, env=env) |
230 if return_code != 0: | 247 if return_code != 0: |
231 return return_code | 248 return return_code |
232 | 249 |
233 | 250 |
234 if __name__ == '__main__': | 251 if __name__ == '__main__': |
235 sys.exit(main(sys.argv)) | 252 sys.exit(main(sys.argv)) |
OLD | NEW |