Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2014 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 """Chromium buildbot steps | 7 """Dartium buildbot steps |
| 8 | 8 |
| 9 Run the Dart layout tests. | 9 Archive dartium, content_shell, and chromedriver to the cloud storage bucket |
| 10 gs://dart-archive, and run tests, including the Dart layout tests. | |
| 10 """ | 11 """ |
| 11 | 12 |
| 13 import imp | |
| 12 import os | 14 import os |
| 13 import platform | 15 import platform |
| 14 import re | 16 import re |
| 15 import shutil | 17 import shutil |
| 16 import socket | |
| 17 import subprocess | 18 import subprocess |
| 18 import sys | 19 import sys |
| 19 import imp | |
| 20 | 20 |
| 21 BUILDER_NAME = 'BUILDBOT_BUILDERNAME' | 21 import upload_steps |
| 22 REVISION = 'BUILDBOT_REVISION' | |
| 23 BUILDER_PATTERN = (r'^dartium-(mac|lucid64|lucid32|win)' | |
| 24 r'-(full|inc|debug)(-ninja)?(-(be|dev|stable|integration))?$' ) | |
| 25 | |
| 26 if platform.system() == 'Windows': | |
| 27 GSUTIL = 'e:/b/build/scripts/slave/gsutil.bat' | |
| 28 else: | |
| 29 GSUTIL = '/b/build/scripts/slave/gsutil' | |
| 30 ACL = 'public-read' | |
| 31 GS_SITE = 'gs://' | |
| 32 GS_URL = 'https://sandbox.google.com/storage/' | |
| 33 GS_DIR = 'dartium-archive' | |
| 34 LATEST = 'latest' | |
| 35 CONTINUOUS = 'continuous' | |
| 36 | |
| 37 REVISION_FILE = 'chrome/browser/ui/webui/dartvm_revision.h' | |
| 38 | |
| 39 # Add dartium tools and build/util to python path. | |
| 40 SRC_PATH = os.path.dirname(os.path.dirname( | |
| 41 os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) | |
| 42 DART_PATH = os.path.join(SRC_PATH, 'dart') | |
| 43 TOOLS_PATH = os.path.join(DART_PATH, 'tools', 'dartium') | |
| 44 BUILD_UTIL_PATH = os.path.join(SRC_PATH, 'build', 'util') | |
| 45 # We limit testing on drt since it takes a long time to run | |
| 46 DRT_FILTER = 'html' | |
| 47 | |
| 48 | |
| 49 sys.path.extend([TOOLS_PATH, BUILD_UTIL_PATH]) | |
| 50 import archive | |
| 51 import utils | 22 import utils |
| 52 | 23 |
| 53 bot_utils = imp.load_source('bot_utils', | 24 SRC_PATH = os.path.dirname( |
|
ricow1
2014/05/14 17:28:18
I think it makes sense to have the path at where w
Bill Hesse
2014/05/15 10:46:37
Done. Moved to utils and comment added.
| |
| 54 os.path.join(DART_PATH, 'tools', 'bots', 'bot_utils.py')) | 25 os.path.dirname( |
| 26 os.path.dirname( | |
| 27 os.path.dirname(os.path.abspath(__file__))))) | |
| 28 DART_PATH = os.path.join(SRC_PATH, 'dart') | |
| 55 | 29 |
| 56 def DartArchiveFile(local_path, remote_path, create_md5sum=False): | 30 # We limit testing on drt since it takes a long time to run. |
| 57 # Copy it to the new unified gs://dart-archive bucket | 31 DRT_FILTER = 'html' |
|
ricow1
2014/05/14 17:28:18
I have been meaning to update that to the browser
| |
| 58 # TODO(kustermann/ricow): Remove all the old archiving code, once everything | |
| 59 # points to the new location | |
| 60 gsutil = bot_utils.GSUtil() | |
| 61 gsutil.upload(local_path, remote_path, public=True) | |
| 62 if create_md5sum: | |
| 63 # 'local_path' may have a different filename than 'remote_path'. So we need | |
| 64 # to make sure the *.md5sum file contains the correct name. | |
| 65 assert '/' in remote_path and not remote_path.endswith('/') | |
| 66 mangled_filename = remote_path[remote_path.rfind('/') + 1:] | |
| 67 local_md5sum = bot_utils.CreateChecksumFile(local_path, mangled_filename) | |
| 68 gsutil.upload(local_md5sum, remote_path + '.md5sum', public=True) | |
| 69 | |
| 70 def UploadDartiumVariant(revision, name, channel, arch, mode, zip_file): | |
| 71 name = name.replace('drt', 'content_shell') | |
| 72 system = sys.platform | |
| 73 | |
| 74 namer = bot_utils.GCSNamer(channel, bot_utils.ReleaseType.RAW) | |
| 75 remote_path = namer.dartium_variant_zipfilepath(revision, name, system, arch, | |
| 76 mode) | |
| 77 DartArchiveFile(zip_file, remote_path, create_md5sum=True) | |
| 78 return remote_path | |
| 79 | |
| 80 def ExecuteCommand(cmd): | |
| 81 """Execute a command in a subprocess. | |
| 82 """ | |
| 83 print 'Executing: ' + ' '.join(cmd) | |
| 84 try: | |
| 85 pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| 86 (output, error) = pipe.communicate() | |
| 87 if pipe.returncode != 0: | |
| 88 print 'Execution failed: ' + str(error) | |
| 89 return (pipe.returncode, output) | |
| 90 except: | |
| 91 import traceback | |
| 92 print 'Execution raised exception:', traceback.format_exc() | |
| 93 return (-1, '') | |
| 94 | |
| 95 | |
| 96 # TODO: Instead of returning a tuple we should make a class with these fields. | |
| 97 def GetBuildInfo(): | |
| 98 """Returns a tuple (name, dart_revision, version, mode, arch, channel, | |
| 99 is_full) where: | |
| 100 - name: A name for the build - the buildbot host if a buildbot. | |
| 101 - dart_revision: The dart revision. | |
| 102 - version: A version string corresponding to this build. | |
| 103 - mode: 'Debug' or 'Release' | |
| 104 - arch: target architecture | |
| 105 - channel: the channel this build is happening on | |
| 106 - is_full: True if this is a full build. | |
| 107 """ | |
| 108 os.chdir(SRC_PATH) | |
| 109 | |
| 110 name = None | |
| 111 version = None | |
| 112 mode = 'Release' | |
| 113 | |
| 114 # Populate via builder environment variables. | |
| 115 name = os.environ[BUILDER_NAME] | |
| 116 | |
| 117 # We need to chdir() to src/dart in order to get the correct revision number. | |
| 118 with utils.ChangedWorkingDirectory(DART_PATH): | |
| 119 dart_tools_utils = imp.load_source('dart_tools_utils', | |
| 120 os.path.join('tools', 'utils.py')) | |
| 121 dart_revision = dart_tools_utils.GetSVNRevision() | |
| 122 | |
| 123 version = dart_revision + '.0' | |
| 124 is_incremental = '-inc' in name | |
| 125 is_win_ninja = 'win-inc-ninja' in name | |
| 126 is_full = False | |
| 127 | |
| 128 pattern = re.match(BUILDER_PATTERN, name) | |
| 129 assert pattern | |
| 130 arch = 'x64' if pattern.group(1) == 'lucid64' else 'ia32' | |
| 131 if pattern.group(2) == 'debug': | |
| 132 mode = 'Debug' | |
| 133 is_full = pattern.group(2) == 'full' | |
| 134 channel = pattern.group(5) | |
| 135 if not channel: | |
| 136 channel = 'be' | |
| 137 | |
| 138 # Fall back if not on builder. | |
| 139 if not name: | |
| 140 name = socket.gethostname().split('.')[0] | |
| 141 | |
| 142 return (name, dart_revision, version, mode, arch, channel, is_full, | |
| 143 is_incremental, is_win_ninja) | |
| 144 | |
| 145 | 32 |
| 146 def RunDartTests(mode, component, suite, arch, checked, test_filter=None, | 33 def RunDartTests(mode, component, suite, arch, checked, test_filter=None, |
| 147 is_win_ninja=False): | 34 is_win_ninja=False): |
| 148 """Runs the Dart WebKit Layout tests. | 35 """Runs the Dart WebKit Layout tests. |
| 149 """ | 36 """ |
| 150 cmd = [sys.executable] | 37 cmd = [sys.executable] |
| 151 script = os.path.join(TOOLS_PATH, 'test.py') | 38 script = os.path.join(DART_PATH, 'tools', 'dartium', 'test.py') |
| 152 cmd.append(script) | 39 cmd.append(script) |
| 153 cmd.append('--buildbot') | 40 cmd.append('--buildbot') |
| 154 cmd.append('--mode=' + mode) | 41 cmd.append('--mode=' + mode) |
| 155 cmd.append('--component=' + component) | 42 cmd.append('--component=' + component) |
| 156 cmd.append('--suite=' + suite) | 43 cmd.append('--suite=' + suite) |
| 157 cmd.append('--arch=' + arch) | 44 cmd.append('--arch=' + arch) |
| 158 cmd.append('--' + checked) | 45 cmd.append('--' + checked) |
| 159 cmd.append('--no-show-results') | 46 cmd.append('--no-show-results') |
| 160 | 47 |
| 161 if is_win_ninja: | 48 if is_win_ninja: |
| 162 cmd.append('--win-ninja-build') | 49 cmd.append('--win-ninja-build') |
| 163 | 50 |
| 164 if test_filter: | 51 if test_filter: |
| 165 cmd.append('--test-filter=' + test_filter) | 52 cmd.append('--test-filter=' + test_filter) |
| 166 | 53 |
| 167 status = subprocess.call(cmd) | 54 status = subprocess.call(cmd) |
| 168 if status != 0: | 55 if status != 0: |
| 169 print '@@@STEP_FAILURE@@@' | 56 print '@@@STEP_FAILURE@@@' |
| 170 return status | 57 return status |
| 171 | 58 |
| 172 | 59 |
| 173 def UploadDartTestsResults(layout_test_results_dir, name, version, | 60 def Test(info, component, suite, checked, test_filter=None): |
| 174 component, checked): | 61 """Test a particular component (e.g., dartium or content_shell(drt)). |
| 175 """Uploads test results to google storage. | |
| 176 """ | 62 """ |
| 177 print ('@@@BUILD_STEP archive %s_layout_%s_tests results@@@' % | 63 print '@@@BUILD_STEP %s_%s_%s_tests@@@' % (component, suite, checked) |
| 178 (component, checked)) | 64 sys.stdout.flush() |
| 179 dir_name = os.path.dirname(layout_test_results_dir) | 65 layout_test_results_dir = os.path.join(SRC_PATH, 'webkit', info.mode, |
| 180 base_name = os.path.basename(layout_test_results_dir) | 66 'layout-test-results') |
| 181 cwd = os.getcwd() | 67 shutil.rmtree(layout_test_results_dir, ignore_errors=True) |
| 182 os.chdir(dir_name) | 68 status = RunDartTests(info.mode, component, suite, info.arch, checked, |
| 183 | 69 test_filter=test_filter, is_win_ninja=info.is_win_ninja) |
| 184 archive_name = 'layout_test_results.zip' | 70 # Archive test failures |
| 185 archive.ZipDir(archive_name, base_name) | 71 if suite == 'layout' and status != 0: |
| 186 | 72 upload_steps.UploadDartTestsResults(layout_test_results_dir, |
| 187 target = '/'.join([GS_DIR, 'layout-test-results', name, component + '-' + | 73 info.name, |
| 188 checked + '-' + version + '.zip']) | 74 info.version, |
| 189 status = UploadArchive(os.path.abspath(archive_name), GS_SITE + target) | 75 component, checked) |
| 190 os.remove(archive_name) | |
| 191 if status == 0: | |
| 192 print ('@@@STEP_LINK@download@' + GS_URL + target + '@@@') | |
| 193 else: | |
| 194 print '@@@STEP_FAILURE@@@' | |
| 195 os.chdir(cwd) | |
| 196 | |
| 197 | |
| 198 def ListArchives(pattern): | |
| 199 """List the contents in Google storage matching the file pattern. | |
| 200 """ | |
| 201 cmd = [GSUTIL, 'ls', pattern] | |
| 202 (status, output) = ExecuteCommand(cmd) | |
| 203 if status != 0: | |
| 204 return [] | |
| 205 return output.split(os.linesep) | |
| 206 | |
| 207 | |
| 208 def RemoveArchives(archives): | |
| 209 """Remove the list of archives in Google storage. | |
| 210 """ | |
| 211 for archive in archives: | |
| 212 if archive.find(GS_SITE) == 0: | |
| 213 cmd = [GSUTIL, 'rm', archive.rstrip()] | |
| 214 (status, _) = ExecuteCommand(cmd) | |
| 215 if status != 0: | |
| 216 return status | |
| 217 return 0 | |
| 218 | |
| 219 | |
| 220 def UploadArchive(source, target): | |
| 221 """Upload an archive zip file to Google storage. | |
| 222 """ | |
| 223 | |
| 224 # Upload file. | |
| 225 cmd = [GSUTIL, 'cp', source, target] | |
| 226 (status, output) = ExecuteCommand(cmd) | |
| 227 if status != 0: | |
| 228 return status | |
| 229 print 'Uploaded: ' + output | |
| 230 | |
| 231 # Set ACL. | |
| 232 if ACL is not None: | |
| 233 cmd = [GSUTIL, 'setacl', ACL, target] | |
| 234 (status, output) = ExecuteCommand(cmd) | |
| 235 return status | 76 return status |
| 236 | 77 |
| 237 | 78 |
| 238 def main(): | 79 def main(): |
| 239 (dartium_bucket, dart_revision, version, mode, arch, channel, | 80 # We need to chdir() to src/dart in order to get the correct revision number. |
| 240 is_full, is_incremental, is_win_ninja) = GetBuildInfo() | 81 with utils.ChangedWorkingDirectory(DART_PATH): |
| 241 drt_bucket = dartium_bucket.replace('dartium', 'drt') | 82 dart_tools_utils = imp.load_source('dart_tools_utils', |
| 242 chromedriver_bucket = dartium_bucket.replace('dartium', 'chromedriver') | 83 os.path.join('tools', 'utils.py')) |
| 84 dart_revision = dart_tools_utils.GetSVNRevision() | |
| 243 | 85 |
| 244 def archiveAndUpload(archive_latest=False): | 86 version = dart_revision + '.0' |
| 245 print '@@@BUILD_STEP dartium_generate_archive@@@' | 87 info = upload_steps.BuildInfo(version, dart_revision) |
|
ricow1
2014/05/14 17:28:18
does that function belong somewhere else, the info
Bill Hesse
2014/05/15 10:46:37
I did have the class here, and in multivm-upload,
| |
| 246 cwd = os.getcwd() | |
| 247 dartium_archive = dartium_bucket + '-' + version | |
| 248 drt_archive = drt_bucket + '-' + version | |
| 249 chromedriver_archive = chromedriver_bucket + '-' + version | |
| 250 dartium_zip, drt_zip, chromedriver_zip = \ | |
| 251 archive.Archive(SRC_PATH, mode, dartium_archive, | |
| 252 drt_archive, chromedriver_archive, | |
| 253 is_win_ninja=is_win_ninja) | |
| 254 status = upload('dartium', dartium_bucket, os.path.abspath(dartium_zip), | |
| 255 archive_latest=archive_latest) | |
| 256 if status == 0: | |
| 257 status = upload('drt', drt_bucket, os.path.abspath(drt_zip), | |
| 258 archive_latest=archive_latest) | |
| 259 if status == 0: | |
| 260 status = upload('chromedriver', chromedriver_bucket, | |
| 261 os.path.abspath(chromedriver_zip), | |
| 262 archive_latest=archive_latest) | |
| 263 os.chdir(cwd) | |
| 264 if status != 0: | |
| 265 print '@@@STEP_FAILURE@@@' | |
| 266 return status | |
| 267 | |
| 268 def upload(module, bucket, zip_file, archive_latest=False): | |
| 269 status = 0 | |
| 270 | |
| 271 # We archive to the new location on all builders except for -inc builders. | |
| 272 if not is_incremental: | |
| 273 print '@@@BUILD_STEP %s_upload_archive_new @@@' % module | |
| 274 # We archive the full builds to gs://dart-archive/ | |
| 275 revision = 'latest' if archive_latest else dart_revision | |
| 276 remote_path = UploadDartiumVariant(revision, module, channel, arch, | |
| 277 mode.lower(), zip_file) | |
| 278 print '@@@STEP_LINK@download@' + remote_path + '@@@' | |
| 279 | |
| 280 # We archive to the old locations only for bleeding_edge builders | |
| 281 if channel == 'be': | |
| 282 _, filename = os.path.split(zip_file) | |
| 283 if not archive_latest: | |
| 284 target = '/'.join([GS_DIR, bucket, filename]) | |
| 285 print '@@@BUILD_STEP %s_upload_archive@@@' % module | |
| 286 status = UploadArchive(zip_file, GS_SITE + target) | |
| 287 print '@@@STEP_LINK@download@' + GS_URL + target + '@@@' | |
| 288 else: | |
| 289 print '@@@BUILD_STEP %s_upload_latest@@@' % module | |
| 290 # Clear latest for this build type. | |
| 291 old = '/'.join([GS_DIR, LATEST, bucket + '-*']) | |
| 292 old_archives = ListArchives(GS_SITE + old) | |
| 293 | |
| 294 # Upload the new latest and remove unnecessary old ones. | |
| 295 target = GS_SITE + '/'.join([GS_DIR, LATEST, filename]) | |
| 296 status = UploadArchive(zip_file, target) | |
| 297 if status == 0: | |
| 298 RemoveArchives( | |
| 299 [iarch for iarch in old_archives if iarch != target]) | |
| 300 else: | |
| 301 print 'Upload failed' | |
| 302 | |
| 303 # Upload unversioned name to continuous site for incremental | |
| 304 # builds. | |
| 305 if '-inc' in bucket: | |
| 306 continuous_name = bucket[:bucket.find('-inc')] | |
| 307 target = GS_SITE + '/'.join([GS_DIR, CONTINUOUS, | |
| 308 continuous_name + '.zip']) | |
| 309 status = UploadArchive(zip_file, target) | |
| 310 | |
| 311 print ('@@@BUILD_STEP %s_upload_archive is over (status = %s)@@@' % | |
| 312 (module, status)) | |
| 313 | |
| 314 return status | |
| 315 | |
| 316 def test(component, suite, checked, test_filter=None): | |
| 317 """Test a particular component (e.g., dartium or frog). | |
| 318 """ | |
| 319 print '@@@BUILD_STEP %s_%s_%s_tests@@@' % (component, suite, checked) | |
| 320 sys.stdout.flush() | |
| 321 layout_test_results_dir = os.path.join(SRC_PATH, 'webkit', mode, | |
| 322 'layout-test-results') | |
| 323 shutil.rmtree(layout_test_results_dir, ignore_errors=True) | |
| 324 status = RunDartTests(mode, component, suite, arch, checked, | |
| 325 test_filter=test_filter, is_win_ninja=is_win_ninja) | |
| 326 | |
| 327 if suite == 'layout' and status != 0: | |
| 328 UploadDartTestsResults(layout_test_results_dir, dartium_bucket, version, | |
| 329 component, checked) | |
| 330 return status | |
| 331 | 88 |
| 332 result = 0 | 89 result = 0 |
| 333 | 90 |
| 334 # Archive to the revision bucket unless integration build | 91 # Archive to the revision bucket unless integration build |
| 335 if channel != 'integration': | 92 if info.channel != 'integration': |
| 336 result = archiveAndUpload(archive_latest=False) | 93 result = upload_steps.ArchiveAndUpload(info, archive_latest=False) |
| 337 | |
| 338 # On dev/stable we archive to the latest bucket as well | 94 # On dev/stable we archive to the latest bucket as well |
| 339 if channel != 'be': | 95 if info.channel != 'be': |
| 340 result = archiveAndUpload(archive_latest=True) or result | 96 result = (upload_steps.ArchiveAndUpload(info, archive_latest=True) |
| 97 or result) | |
| 341 | 98 |
| 342 # Run layout tests | 99 # Run layout tests |
| 343 if mode == 'Release' or platform.system() != 'Darwin': | 100 if info.mode == 'Release' or platform.system() != 'Darwin': |
| 344 result = test('drt', 'layout', 'unchecked') or result | 101 result = Test(info, 'drt', 'layout', 'unchecked') or result |
| 345 result = test('drt', 'layout', 'checked') or result | 102 result = Test(info, 'drt', 'layout', 'checked') or result |
| 346 | 103 |
| 347 # Run dartium tests | 104 # Run dartium tests |
| 348 result = test('dartium', 'core', 'unchecked') or result | 105 result = Test(info, 'dartium', 'core', 'unchecked') or result |
| 349 result = test('dartium', 'core', 'checked') or result | 106 result = Test(info, 'dartium', 'core', 'checked') or result |
| 350 | 107 |
| 351 # Run ContentShell tests | 108 # Run ContentShell tests |
| 352 # NOTE: We don't run ContentShell tests on dartium-*-inc builders to keep | 109 # NOTE: We don't run ContentShell tests on dartium-*-inc builders to keep |
| 353 # cycle times down. | 110 # cycle times down. |
| 354 if not is_incremental: | 111 if not info.is_incremental: |
| 355 # If we run all checked tests on dartium, we restrict the number of | 112 # If we run all checked tests on dartium, we restrict the number of |
| 356 # unchecked tests on drt to DRT_FILTER | 113 # unchecked tests on drt to DRT_FILTER |
| 357 result = test('drt', 'core', 'unchecked', test_filter=DRT_FILTER) or result | 114 result = Test(info, 'drt', 'core', 'unchecked', |
| 358 result = test('drt', 'core', 'checked') or result | 115 test_filter=DRT_FILTER) or result |
| 116 result = Test(info, 'drt', 'core', 'checked') or result | |
| 359 | 117 |
| 360 # On the 'be' channel, we only archive to the latest bucket if all tests ran | 118 # On the 'be' channel, we only archive to the latest bucket if all tests were |
| 361 # successfull. | 119 # successful. |
| 362 if result == 0 and channel == 'be': | 120 if result == 0 and info.channel == 'be': |
| 363 result = archiveAndUpload(archive_latest=True) or result | 121 result = upload_steps.ArchiveAndUpload(info, archive_latest=True) or result |
| 364 | 122 |
| 365 if __name__ == '__main__': | 123 if __name__ == '__main__': |
| 366 sys.exit(main()) | 124 sys.exit(main()) |
| OLD | NEW |