| 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 |