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