| OLD | NEW |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import json | 5 import json |
| 6 | 6 |
| 7 from recipe_engine.config import List | 7 from recipe_engine.config import List |
| 8 from recipe_engine.config import Single | 8 from recipe_engine.config import Single |
| 9 from recipe_engine.recipe_api import Property | 9 from recipe_engine.recipe_api import Property |
| 10 | 10 |
| 11 | 11 |
| 12 DEPS = [ | 12 DEPS = [ |
| 13 'chromium', | 13 'chromium', |
| 14 'chromium_tests', | 14 'chromium_tests', |
| 15 'findit', | 15 'findit', |
| 16 'depot_tools/gclient', | 16 'depot_tools/gclient', |
| 17 'recipe_engine/json', | 17 'recipe_engine/json', |
| 18 'recipe_engine/path', | 18 'recipe_engine/path', |
| 19 'recipe_engine/platform', |
| 19 'recipe_engine/properties', | 20 'recipe_engine/properties', |
| 20 'recipe_engine/python', | 21 'recipe_engine/python', |
| 21 'recipe_engine/step', | 22 'recipe_engine/step', |
| 22 ] | 23 ] |
| 23 | 24 |
| 24 | 25 |
| 25 PROPERTIES = { | 26 PROPERTIES = { |
| 26 'target_mastername': Property( | 27 'target_mastername': Property( |
| 27 kind=str, help='The target master to match compile config to.'), | 28 kind=str, help='The target master to match compile config to.'), |
| 28 'target_buildername': Property( | 29 'target_buildername': Property( |
| 29 kind=str, help='The target builder to match compile config to.'), | 30 kind=str, help='The target builder to match compile config to.'), |
| 30 'good_revision': Property( | 31 'good_revision': Property( |
| 31 kind=str, help='The last known good chromium revision.'), | 32 kind=str, help='The last known good chromium revision.'), |
| 32 'bad_revision': Property( | 33 'bad_revision': Property( |
| 33 kind=str, help='The first known bad chromium revision.'), | 34 kind=str, help='The first known bad chromium revision.'), |
| 34 'requested_compile_targets': Property( | 35 'requested_compile_targets': Property( |
| 35 kind=List(basestring), default=None, param_name='compile_targets', | 36 kind=List(basestring), default=None, param_name='compile_targets', |
| 36 help='The failed compile targets, eg: browser_tests'), | 37 help='The failed compile targets, eg: browser_tests, ' |
| 38 'obj/path/to/source.o, gen/path/to/generated.cc, etc.'), |
| 37 'use_analyze': Property( | 39 'use_analyze': Property( |
| 38 kind=Single(bool, empty_val=False, required=False), default=True, | 40 kind=Single(bool, empty_val=False, required=False), default=True, |
| 39 help='Use analyze to filter out affected targets.'), | 41 help='Use analyze to filter out affected targets.'), |
| 40 } | 42 } |
| 41 | 43 |
| 42 | 44 |
| 43 class CompileResult(object): | 45 class CompileResult(object): |
| 44 SKIPPED = 'skipped' # No compile is needed. | 46 SKIPPED = 'skipped' # No compile is needed. |
| 45 PASSED = 'passed' # Compile passed. | 47 PASSED = 'passed' # Compile passed. |
| 46 FAILED = 'failed' # Compile failed. | 48 FAILED = 'failed' # Compile failed. |
| 47 | 49 |
| 48 | 50 |
| 49 def _run_compile_at_revision(api, target_mastername, target_buildername, | 51 def _run_compile_at_revision(api, target_mastername, target_buildername, |
| 50 revision, compile_targets, use_analyze): | 52 revision, compile_targets, use_analyze): |
| 51 with api.step.nest('test %s' % str(revision)): | 53 with api.step.nest('test %s' % str(revision)): |
| 52 # Checkout code at the given revision to recompile. | 54 # Checkout code at the given revision to recompile. |
| 53 bot_config = api.chromium_tests.create_bot_config_object( | 55 bot_config = api.chromium_tests.create_bot_config_object( |
| 54 target_mastername, target_buildername) | 56 target_mastername, target_buildername) |
| 55 bot_update_step, bot_db = api.chromium_tests.prepare_checkout( | 57 bot_update_step, bot_db = api.chromium_tests.prepare_checkout( |
| 56 bot_config, root_solution_revision=revision) | 58 bot_config, root_solution_revision=revision) |
| 57 | 59 |
| 58 # TODO(http://crbug.com/560991): if compile targets are provided, check | |
| 59 # whether they exist and then use analyze to compile the impacted ones by | |
| 60 # the given revision. | |
| 61 compile_targets = sorted(set(compile_targets or [])) | 60 compile_targets = sorted(set(compile_targets or [])) |
| 62 if not compile_targets: | 61 if not compile_targets: |
| 62 # If compile targets are not specified, retrieve them from the build spec. |
| 63 _, tests_including_triggered = api.chromium_tests.get_tests( | 63 _, tests_including_triggered = api.chromium_tests.get_tests( |
| 64 bot_config, bot_db) | 64 bot_config, bot_db) |
| 65 compile_targets = api.chromium_tests.get_compile_targets( | 65 compile_targets = api.chromium_tests.get_compile_targets( |
| 66 bot_config, bot_db, tests_including_triggered) | 66 bot_config, bot_db, tests_including_triggered) |
| 67 | 67 |
| 68 # Use dependency "analyze" to filter out those that are not impacted by |
| 69 # the given revision. This is to reduce the number of targets to be |
| 70 # compiled. |
| 68 if use_analyze: | 71 if use_analyze: |
| 69 changed_files = api.findit.files_changed_by_revision(revision) | 72 changed_files = api.findit.files_changed_by_revision(revision) |
| 70 | 73 |
| 71 _, compile_targets = api.chromium_tests.analyze( | 74 _, compile_targets = api.chromium_tests.analyze( |
| 72 changed_files, | 75 changed_files, |
| 73 test_targets=[], | 76 test_targets=[], |
| 74 additional_compile_targets=compile_targets, | 77 additional_compile_targets=compile_targets, |
| 75 config_file_name='trybot_analyze_config.json', | 78 config_file_name='trybot_analyze_config.json', |
| 76 mb_mastername=target_mastername, | 79 mb_mastername=target_mastername, |
| 77 mb_buildername=target_buildername, | 80 mb_buildername=target_buildername, |
| 78 additional_names=None) | 81 additional_names=None) |
| 82 else: |
| 83 # Use ninja to filter out none-existing targets. |
| 84 compile_targets = api.findit.existing_targets( |
| 85 compile_targets, target_mastername, target_buildername) |
| 79 | 86 |
| 80 if not compile_targets: | 87 if not compile_targets: |
| 81 # No compile target is impacted by the given revision. | 88 # No compile target exists, or is impacted by the given revision. |
| 82 return CompileResult.SKIPPED | 89 return CompileResult.SKIPPED |
| 83 | 90 |
| 84 try: | 91 try: |
| 85 api.chromium_tests.compile_specific_targets( | 92 api.chromium_tests.compile_specific_targets( |
| 86 bot_config, | 93 bot_config, |
| 87 bot_update_step, | 94 bot_update_step, |
| 88 bot_db, | 95 bot_db, |
| 89 compile_targets, | 96 compile_targets, |
| 90 tests_including_triggered=[], | 97 tests_including_triggered=[], |
| 91 mb_mastername=target_mastername, | 98 mb_mastername=target_mastername, |
| 92 mb_buildername=target_buildername, | 99 mb_buildername=target_buildername, |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 124 try: | 131 try: |
| 125 for current_revision in revisions_to_check: | 132 for current_revision in revisions_to_check: |
| 126 last_revision = None | 133 last_revision = None |
| 127 compile_result = _run_compile_at_revision( | 134 compile_result = _run_compile_at_revision( |
| 128 api, target_mastername, target_buildername, | 135 api, target_mastername, target_buildername, |
| 129 current_revision, requested_compile_targets, use_analyze) | 136 current_revision, requested_compile_targets, use_analyze) |
| 130 | 137 |
| 131 compile_results[current_revision] = compile_result | 138 compile_results[current_revision] = compile_result |
| 132 last_revision = current_revision | 139 last_revision = current_revision |
| 133 if compile_result == CompileResult.FAILED: | 140 if compile_result == CompileResult.FAILED: |
| 134 # TODO(http://crbug.com/560991): if compile targets are specified, | |
| 135 # compile may fail because those targets are added in a later revision. | |
| 136 break # Found the culprit, no need to check later revisions. | 141 break # Found the culprit, no need to check later revisions. |
| 137 finally: | 142 finally: |
| 138 # Report the result. | 143 # Report the result. |
| 139 # TODO(http://crbug.com/563807): use api.python.succeeding_step instead. | 144 step_result = api.python.succeeding_step( |
| 140 step_result = api.python.inline( | 145 'report', [json.dumps(report, indent=2)], as_log='report') |
| 141 'report', 'import sys; sys.exit(0)', add_python_log=False) | |
| 142 | 146 |
| 143 if (not requested_compile_targets and | 147 if (last_revision and |
| 144 compile_results and | |
| 145 last_revision and | |
| 146 compile_results.get(last_revision) == CompileResult.FAILED): | 148 compile_results.get(last_revision) == CompileResult.FAILED): |
| 147 step_result.presentation.step_text = '<br/>Culprit: %s' % last_revision | 149 step_result.presentation.step_text = '<br/>Culprit: %s' % last_revision |
| 148 | 150 |
| 149 step_result.presentation.logs.setdefault('report', []).append( | |
| 150 json.dumps(report, indent=2)) | |
| 151 | |
| 152 # Set the report as a build property too, so that it will be reported back | 151 # Set the report as a build property too, so that it will be reported back |
| 153 # to Buildbucket and Findit will pull from there instead of buildbot master. | 152 # to Buildbucket and Findit will pull from there instead of buildbot master. |
| 154 step_result.presentation.properties['report'] = report | 153 step_result.presentation.properties['report'] = report |
| 155 | 154 |
| 156 return report | 155 return report |
| 157 | 156 |
| 158 | 157 |
| 159 def GenTests(api): | 158 def GenTests(api): |
| 160 def props(compile_targets=None, use_analyze=False): | 159 def props(compile_targets=None, use_analyze=False): |
| 161 properties = { | 160 properties = { |
| 162 'mastername': 'tryserver.chromium.linux', | 161 'mastername': 'tryserver.chromium.linux', |
| 163 'buildername': 'linux_variable', | 162 'buildername': 'linux_variable', |
| 164 'slavename': 'build1-a1', | 163 'slavename': 'build1-a1', |
| 165 'buildnumber': '1', | 164 'buildnumber': '1', |
| 166 'target_mastername': 'chromium.linux', | 165 'target_mastername': 'chromium.linux', |
| 167 'target_buildername': 'Linux Builder', | 166 'target_buildername': 'Linux Builder', |
| 168 'good_revision': 'r0', | 167 'good_revision': 'r0', |
| 169 'bad_revision': 'r1', | 168 'bad_revision': 'r1', |
| 170 'use_analyze': use_analyze, | 169 'use_analyze': use_analyze, |
| 171 } | 170 } |
| 172 if compile_targets: | 171 if compile_targets: |
| 173 properties['compile_targets'] = compile_targets | 172 properties['compile_targets'] = compile_targets |
| 174 return api.properties(**properties) | 173 return api.properties(**properties) + api.platform.name('linux') |
| 175 | 174 |
| 176 yield ( | 175 yield ( |
| 177 api.test('compile_specified_targets') + | 176 api.test('compile_specified_targets') + |
| 178 props(compile_targets=['target_name']) | 177 props(compile_targets=['target_name']) + |
| 178 api.override_step_data('test r1.check_targets', |
| 179 api.json.output({ |
| 180 'found': ['target_name'], |
| 181 'not_found': [], |
| 182 })) |
| 179 ) | 183 ) |
| 180 | 184 |
| 181 yield ( | 185 yield ( |
| 186 api.test('compile_none_existing_targets') + |
| 187 props(compile_targets=['gen/a/b/source.cc']) + |
| 188 api.override_step_data('test r1.check_targets', |
| 189 api.json.output({ |
| 190 'found': [], |
| 191 'not_found': ['gen/a/b/source.cc'], |
| 192 })) |
| 193 ) |
| 194 |
| 195 |
| 196 yield ( |
| 182 api.test('compile_default_targets') + | 197 api.test('compile_default_targets') + |
| 183 props() + | 198 props() + |
| 184 api.override_step_data('test r1.read test spec', | 199 api.override_step_data('test r1.read test spec', |
| 185 api.json.output({ | 200 api.json.output({ |
| 186 'Linux Builder': { | 201 'Linux Builder': { |
| 187 'additional_compile_targets': [ | 202 'additional_compile_targets': [ |
| 188 'base_unittests', | 203 'base_unittests', |
| 189 ], | 204 ], |
| 190 } | 205 } |
| 191 })) | 206 })) |
| 192 ) | 207 ) |
| 193 | 208 |
| 194 yield ( | 209 yield ( |
| 195 api.test('compile_succeeded') + | 210 api.test('compile_succeeded') + |
| 196 props() + | 211 props() + |
| 197 api.override_step_data('test r1.compile', retcode=1) | 212 api.override_step_data('test r1.compile', retcode=1) |
| 198 ) | 213 ) |
| 199 | 214 |
| 200 yield ( | 215 yield ( |
| 201 api.test('compile_failed') + | 216 api.test('compile_failed') + |
| 202 props() + | 217 props() + |
| 203 api.override_step_data('test r1.compile', retcode=1) | 218 api.override_step_data('test r1.compile', retcode=1) |
| 204 ) | 219 ) |
| 205 | 220 |
| 206 yield ( | 221 yield ( |
| 207 api.test('failed_compile_upon_infra_failure') + | 222 api.test('failed_compile_upon_infra_failure') + |
| 208 props(compile_targets=['target_name']) + | 223 props(compile_targets=['target_name']) + |
| 224 api.override_step_data('test r1.check_targets', |
| 225 api.json.output({ |
| 226 'found': ['target_name'], |
| 227 'not_found': [], |
| 228 })) + |
| 209 api.override_step_data( | 229 api.override_step_data( |
| 210 'test r1.compile', | 230 'test r1.compile', |
| 211 api.json.output({ | 231 api.json.output({ |
| 212 'notice': [ | 232 'notice': [ |
| 213 { | 233 { |
| 214 'infra_status': { | 234 'infra_status': { |
| 215 'ping_status_code': 408, | 235 'ping_status_code': 408, |
| 216 }, | 236 }, |
| 217 }, | 237 }, |
| 218 ], | 238 ], |
| (...skipping 28 matching lines...) Expand all Loading... |
| 247 })) + | 267 })) + |
| 248 api.override_step_data( | 268 api.override_step_data( |
| 249 'test r1.analyze', | 269 'test r1.analyze', |
| 250 api.json.output({ | 270 api.json.output({ |
| 251 'status': 'Found dependency', | 271 'status': 'Found dependency', |
| 252 'compile_targets': ['a', 'a_run'], | 272 'compile_targets': ['a', 'a_run'], |
| 253 'test_targets': ['a', 'a_run'], | 273 'test_targets': ['a', 'a_run'], |
| 254 }) | 274 }) |
| 255 ) | 275 ) |
| 256 ) | 276 ) |
| OLD | NEW |