Chromium Code Reviews| Index: tools/bots/bot.py |
| diff --git a/tools/bots/bot.py b/tools/bots/bot.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..bdd796e869821abd6dd7e11c4742b0b9ae55f548 |
| --- /dev/null |
| +++ b/tools/bots/bot.py |
| @@ -0,0 +1,224 @@ |
| +#!/usr/bin/python |
| + |
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +""" |
| +Shared code for use in the buildbot scripts. |
| +""" |
| + |
| +import optparse |
| +import os |
| +from os.path import abspath |
| +from os.path import dirname |
| +import subprocess |
| +import sys |
| + |
| +DART_PATH = dirname(dirname(dirname(abspath(__file__)))) |
| + |
| +NO_COLOR_ENV = dict(os.environ) |
| +NO_COLOR_ENV['TERM'] = 'nocolor' |
| + |
| +BUILDER_NAME = 'BUILDBOT_BUILDERNAME' |
| +BUILDER_CLOBBER = 'BUILDBOT_CLOBBER' |
| + |
| + |
| +class BuildInfo(object): |
| + """ |
| + Encapsulation of build information. |
| + |
| + - compiler: 'dart2js' or None when the builder has an incorrect name |
|
Emily Fortuna
2012/10/23 23:09:49
Some of these comments should be updated if this i
Bob Nystrom
2012/10/24 00:46:44
Good catch. Done.
|
| + - runtime: 'd8', 'ie', 'ff', 'safari', 'chrome', 'opera' |
| + - mode: 'debug' or 'release' |
| + - system: 'linux', 'mac', or 'win7' |
| + - checked: True if we should run in checked mode, otherwise False |
| + - host_checked: True if we should run in host checked mode, otherwise False |
| + - shard_index: The shard we are running, None when not specified. |
| + - total_shards: The total number of shards, None when not specified. |
| + - is_buildbot: True if we are on a buildbot (or emulating it). |
| + - test_set: Specification of a non standard test set, default None |
| + """ |
| + def __init__(self, compiler, runtime, mode, system, checked=False, |
| + host_checked=False, shard_index=None, total_shards=None, |
| + is_buildbot=False, test_set=None): |
| + self.compiler = compiler |
| + self.runtime = runtime |
| + self.mode = mode |
| + self.system = system |
| + self.checked = checked |
| + self.host_checked = host_checked |
| + self.shard_index = shard_index |
| + self.total_shards = total_shards |
| + self.is_buildbot = is_buildbot |
| + self.test_set = test_set |
| + |
| + def PrintBuildInfo(self): |
| + shard_description = "" |
| + if self.shard_index: |
| + shard_description = " shard %s of %s" % (self.shard_index, |
| + self.total_shards) |
| + print ("compiler: %s, runtime: %s mode: %s, system: %s," |
| + " checked: %s, host-checked: %s, test-set: %s%s" |
| + ) % (self.compiler, self.runtime, self.mode, self.system, |
| + self.checked, self.host_checked, self.test_set, |
| + shard_description) |
| + |
| + |
| +class BuildStep(object): |
| + """ |
| + A context manager for handling build steps. |
| + |
| + When the context manager is entered, it prints the "@@@BUILD_STEP __@@@" |
| + message. If it exits from an error being raised it displays the |
| + "@@@STEP_FAILURE@@@" message. |
| + """ |
| + def __init__(self, name): |
| + self.name = name |
| + |
| + def __enter__(self): |
| + print '@@@BUILD_STEP %s@@@' % self.name |
| + |
| + def __exit__(self, type, value, traceback): |
| + if value: |
| + print '@@@STEP_FAILURE@@@' |
| + |
| + |
| +def RunBot(parse_name, custom_steps): |
| + """ |
| + The main function for running a buildbot. |
| + |
| + A buildbot script should invoke this once. The parse_name function will be |
| + called with the name of the buildbot and should return an instance of |
| + BuildInfo. This function will then set up the bot, build the SDK etc. When |
| + that's done, it will call custom_steps, passing in the BuildInfo object. |
| + |
| + In that, you can perform any bot-specific build steps. |
| + |
| + This function will not return. It will call sys.exit() with an appropriate |
| + exit code. |
| + """ |
| + if len(sys.argv) == 0: |
| + print 'Script pathname not known, giving up.' |
| + sys.exit(1) |
| + |
| + name = GetBotName() |
| + build_info = parse_name(name) |
| + if not build_info: |
| + print 'Could not handle unfamiliar bot name "%s".' % name |
| + sys.exit(1) |
| + |
| + # Make sure we are in the dart directory |
| + os.chdir(DART_PATH) |
| + |
| + try: |
| + Clobber(build_info.mode) |
| + BuildSDK(build_info.mode, build_info.system) |
| + |
| + custom_steps(build_info) |
| + except OSError as e: |
| + sys.exit(e.errno) |
| + |
| + sys.exit(0) |
| + |
| + |
| +def GetBotName(): |
| + """ |
| + Gets the name of the current buildbot. |
| + """ |
| + # For testing the bot locally, allow the user to pass in a buildbot name. |
| + parser = optparse.OptionParser() |
| + parser.add_option('-n', '--name', dest='name', help='The name of the build' |
| + 'bot you would like to emulate (ex: vm-mac-debug)', default=None) |
| + args, _ = parser.parse_args() |
| + |
| + if args.name: |
| + return args.name |
| + |
| + name = os.environ.get(BUILDER_NAME) |
| + if not name: |
| + print 'Use -n $BUILDBOT_NAME for the bot you would like to emulate.' |
| + sys.exit(1) |
| + |
| + return name |
| + |
| + |
| +def Clobber(mode): |
| + """ |
| + Clobbers the builder before we do the build, if appropriate. |
| + |
| + - mode: either 'debug' or 'release' |
| + """ |
| + if os.environ.get(BUILDER_CLOBBER) != "1": |
| + return |
| + |
| + with BuildStep('Clobber'): |
| + cmd = [sys.executable, |
| + './tools/clean_output_directory.py', |
| + '--mode=' + mode] |
| + print 'Clobbering %s' % (' '.join(cmd)) |
| + RunProcess(cmd) |
| + |
| + |
| +def BuildSDK(mode, system): |
| + """ |
| + Builds the SDK. |
| + |
| + - mode: either 'debug' or 'release' |
| + - system: either 'linux', 'mac', or 'win7' |
| + """ |
| + with BuildStep('Build SDK'): |
| + args = [sys.executable, './tools/build.py', '--mode=' + mode, 'create_sdk'] |
| + print 'Building SDK: %s' % (' '.join(args)) |
| + RunProcess(args) |
| + |
| + |
| +def RunTest(name, build_info, targets, flags=None): |
| + """ |
| + Runs test.py with the given settings. |
| + """ |
| + if not flags: |
| + flags = [] |
| + |
| + step_name = GetStepName(name, flags) |
| + with BuildStep(step_name): |
| + sys.stdout.flush() |
| + |
| + cmd = [ |
| + sys.executable, os.path.join(os.curdir, 'tools', 'test.py'), |
| + '--step_name=' + step_name, |
| + '--mode=' + build_info.mode, |
| + '--compiler=' + build_info.compiler, |
| + '--runtime=' + build_info.runtime, |
| + '--progress=buildbot', |
| + '-v', '--time', '--use-sdk', '--report' |
| + ] |
| + |
| + if build_info.checked: |
| + cmd.append('--checked') |
| + |
| + cmd.extend(flags) |
| + cmd.extend(targets) |
| + |
| + print 'Running: %s' % (' '.join(cmd)) |
| + RunProcess(cmd) |
| + |
| + |
| +def RunProcess(command): |
| + """ |
| + Runs command. |
| + |
| + If a non-zero exit code is returned, raises an OSError with errno as the exit |
| + code. |
| + """ |
| + exit_code = subprocess.call(command, env=NO_COLOR_ENV) |
| + if exit_code != 0: |
| + raise OSError(exit_code) |
| + |
| + |
| +def GetStepName(name, flags): |
| + """ |
| + Filters out flags with '=' as this breaks the /stats feature of the buildbot. |
| + """ |
| + flags = [x for x in flags if not '=' in x] |
| + return ('%s tests %s' % (name, ' '.join(flags))).strip() |