# -*- python -*-
# ex: set syntax=python:
c = BuildmasterConfig = {}
from buildbot.buildslave import BuildSlave
from buildbot.changes.pb import PBChangeSource
from buildbot.scheduler import AnyBranchScheduler, Triggerable
from buildbot.schedulers.filter import ChangeFilter
from buildbot.status import html
from buildbot.status.web.authz import Authz
from buildbot.process import buildstep, factory, properties
from buildbot.steps import master, shell, source, transfer, trigger
from buildbot.status.builder import SUCCESS, FAILURE, WARNINGS, SKIPPED
from twisted.internet import defer
import os
import re
import simplejson
import urllib
from webkitpy.common.config import build as wkbuild
from webkitpy.common.net.buildbot import BuildBot as wkbuildbot
WithProperties = properties.WithProperties
class ConfigureBuild(buildstep.BuildStep):
name = "configure build"
description = ["configuring build"]
descriptionDone = ["configured build"]
def __init__(self, platform, configuration, architecture, buildOnly, *args, **kwargs):
buildstep.BuildStep.__init__(self, *args, **kwargs)
self.platform = platform.split('-', 1)[0]
self.fullPlatform = platform
self.configuration = configuration
self.architecture = architecture
self.buildOnly = buildOnly
self.addFactoryArguments(platform=platform, configuration=configuration, architecture=architecture, buildOnly=buildOnly)
def start(self):
self.setProperty("platform", self.platform)
self.setProperty("fullPlatform", self.fullPlatform)
self.setProperty("configuration", self.configuration)
self.setProperty("architecture", self.architecture)
self.setProperty("buildOnly", self.buildOnly)
self.finished(SUCCESS)
return defer.succeed(None)
class CheckOutSource(source.SVN):
baseURL = "http://svn.webkit.org/repository/webkit/"
mode = "update"
def __init__(self, *args, **kwargs):
source.SVN.__init__(self, baseURL=self.baseURL, defaultBranch="trunk", mode=self.mode, *args, **kwargs)
class InstallWin32Dependencies(shell.Compile):
description = ["installing dependencies"]
descriptionDone = ["installed dependencies"]
command = ["perl", "./Tools/Scripts/update-webkit-auxiliary-libs"]
class KillOldProcesses(shell.Compile):
name = "kill old processes"
description = ["killing old processes"]
descriptionDone = ["killed old processes"]
command = ["python", "./Tools/BuildSlaveSupport/win/kill-old-processes"]
class InstallChromiumDependencies(shell.ShellCommand):
name = "gclient"
description = ["updating chromium dependencies"]
descriptionDone = ["updated chromium dependencies"]
command = ["perl", "./Tools/Scripts/update-webkit-chromium", "--force"]
haltOnFailure = True
class CleanupChromiumCrashLogs(shell.ShellCommand):
name = "cleanup crash logs"
description = ["removing crash logs"]
descriptionDone = ["removed crash logs"]
command = ["python", "./Tools/BuildSlaveSupport/chromium/remove-crash-logs"]
haltOnFailure = False
def appendCustomBuildFlags(step, platform):
if platform in ('chromium', 'efl', 'gtk', 'qt', 'wincairo', 'wince', 'wx'):
step.setCommand(step.command + ['--' + platform])
class CompileWebKit(shell.Compile):
command = ["perl", "./Tools/Scripts/build-webkit", WithProperties("--%(configuration)s")]
env = {'MFLAGS':''}
name = "compile-webkit"
description = ["compiling"]
descriptionDone = ["compiled"]
warningPattern = ".*arning: .*"
def start(self):
platform = self.getProperty('platform')
buildOnly = self.getProperty('buildOnly')
if platform == 'mac' and buildOnly:
self.setCommand(self.command + ['DEBUG_INFORMATION_FORMAT=dwarf-with-dsym'])
appendCustomBuildFlags(self, platform)
return shell.Compile.start(self)
class ArchiveBuiltProduct(shell.ShellCommand):
command = ["python", "./Tools/BuildSlaveSupport/built-product-archive",
WithProperties("--platform=%(platform)s"), WithProperties("--%(configuration)s"), "archive"]
name = "archive-built-product"
description = ["archiving built product"]
descriptionDone = ["archived built product"]
haltOnFailure = True
class ExtractBuiltProduct(shell.ShellCommand):
command = ["python", "./Tools/BuildSlaveSupport/built-product-archive",
WithProperties("--platform=%(platform)s"), WithProperties("--%(configuration)s"), "extract"]
name = "extract-built-product"
description = ["extracting built product"]
descriptionDone = ["extracted built product"]
haltOnFailure = True
class UploadBuiltProduct(transfer.FileUpload):
slavesrc = WithProperties("WebKitBuild/%(configuration)s.zip")
masterdest = WithProperties("archives/%(fullPlatform)s-%(architecture)s-%(configuration)s/%(got_revision)s.zip")
haltOnFailure = True
def __init__(self):
transfer.FileUpload.__init__(self, self.slavesrc, self.masterdest, mode=0644)
class DownloadBuiltProduct(transfer.FileDownload):
slavedest = WithProperties("WebKitBuild/%(configuration)s.zip")
mastersrc = WithProperties("archives/%(fullPlatform)s-%(architecture)s-%(configuration)s/%(got_revision)s.zip")
haltOnFailure = True
flunkOnFailure = True
def __init__(self):
transfer.FileDownload.__init__(self, self.mastersrc, self.slavedest)
class RunJavaScriptCoreTests(shell.Test):
name = "jscore-test"
description = ["jscore-tests running"]
descriptionDone = ["jscore-tests"]
command = ["perl", "./Tools/Scripts/run-javascriptcore-tests", WithProperties("--%(configuration)s")]
logfiles = {'actual.html (source)': 'Source/JavaScriptCore/tests/mozilla/actual.html'}
def __init__(self, skipBuild=False, *args, **kwargs):
self.skipBuild = skipBuild
shell.Test.__init__(self, *args, **kwargs)
self.addFactoryArguments(skipBuild=skipBuild)
def start(self):
appendCustomBuildFlags(self, self.getProperty('platform'))
if self.skipBuild:
self.setCommand(self.command + ['--skip-build'])
return shell.Test.start(self)
def commandComplete(self, cmd):
shell.Test.commandComplete(self, cmd)
logText = cmd.logs['stdio'].getText()
statusLines = [line for line in logText.splitlines() if line.find('regression') >= 0 and line.find(' found.') >= 0]
if statusLines and statusLines[0].split()[0] != '0':
self.regressionLine = statusLines[0]
else:
self.regressionLine = None
if 'actual.html (source)' in cmd.logs:
self.addHTMLLog('actual.html', cmd.logs['actual.html (source)'].getText())
def evaluateCommand(self, cmd):
if self.regressionLine:
return FAILURE
if cmd.rc != 0:
return FAILURE
return SUCCESS
def getText(self, cmd, results):
return self.getText2(cmd, results)
def getText2(self, cmd, results):
if results != SUCCESS and self.regressionLine:
return [self.name, self.regressionLine]
return [self.name]
class RunWebKitTests(shell.Test):
name = "layout-test"
description = ["layout-tests running"]
descriptionDone = ["layout-tests"]
command = ["perl", "./Tools/Scripts/run-webkit-tests", "--no-launch-safari", "--no-new-test-results",
"--no-sample-on-timeout", "--results-directory", "layout-test-results", "--use-remote-links-to-tests",
WithProperties("--%(configuration)s"), "--exit-after-n-crashes-or-timeouts", "20", "--exit-after-n-failures", "500"]
def __init__(self, skipBuild=False, *args, **kwargs):
self.skipBuild = skipBuild
shell.Test.__init__(self, *args, **kwargs)
self.addFactoryArguments(skipBuild=skipBuild)
def start(self):
platform = self.getProperty('platform')
appendCustomBuildFlags(self, platform)
if platform == "win":
rootArgument = ['--root=' + os.path.join("WebKitBuild", self.getProperty('configuration'), "bin")]
else:
rootArgument = ['--root=WebKitBuild/bin']
if self.skipBuild:
self.setCommand(self.command + rootArgument)
return shell.Test.start(self)
def commandComplete(self, cmd):
shell.Test.commandComplete(self, cmd)
logText = cmd.logs['stdio'].getText()
incorrectLayoutLines = []
for line in logText.splitlines():
if line.find('had incorrect layout') >= 0 or line.find('were new') >= 0 or line.find('was new') >= 0:
incorrectLayoutLines.append(line)
elif line.find('test case') >= 0 and (line.find(' crashed') >= 0 or line.find(' timed out') >= 0):
incorrectLayoutLines.append(line)
elif line.startswith("WARNING:") and line.find(' leak') >= 0:
incorrectLayoutLines.append(line.replace('WARNING: ', ''))
elif line.find('Exiting early') >= 0:
incorrectLayoutLines.append(line)
# FIXME: Detect and summarize leaks of RefCounted objects
self.incorrectLayoutLines = incorrectLayoutLines
def evaluateCommand(self, cmd):
if self.incorrectLayoutLines:
if len(self.incorrectLayoutLines) == 1:
line = self.incorrectLayoutLines[0]
if line.find('were new') >= 0 or line.find('was new') >= 0 or line.find(' leak') >= 0:
return WARNINGS
return FAILURE
if cmd.rc != 0:
return FAILURE
return SUCCESS
def getText(self, cmd, results):
return self.getText2(cmd, results)
def getText2(self, cmd, results):
if results != SUCCESS and self.incorrectLayoutLines:
return self.incorrectLayoutLines
return [self.name]
class NewRunWebKitTests(RunWebKitTests):
command = ["python", "./Tools/Scripts/new-run-webkit-tests", "--noshow-results",
"--verbose", "--results-directory", "layout-test-results",
"--builder-name", WithProperties("%(buildername)s"),
"--build-number", WithProperties("%(buildnumber)s"),
"--master-name", "webkit.org",
"--test-results-server", "test-results.appspot.com",
WithProperties("--%(configuration)s")]
class RunPythonTests(shell.Test):
name = "webkitpy-test"
description = ["python-tests running"]
descriptionDone = ["python-tests"]
command = ["python", "./Tools/Scripts/test-webkitpy"]
class RunPerlTests(shell.Test):
name = "webkitperl-test"
description = ["perl-tests running"]
descriptionDone = ["perl-tests"]
command = ["perl", "./Tools/Scripts/test-webkitperl"]
class RunGtkAPITests(shell.Test):
name = "API tests"
description = ["API tests running"]
descriptionDone = ["API tests"]
command = ["perl", "./Tools/Scripts/run-gtk-tests", WithProperties("--%(configuration)s")]
def commandComplete(self, cmd):
shell.Test.commandComplete(self, cmd)
logText = cmd.logs['stdio'].getText()
incorrectLines = []
for line in logText.splitlines():
if line.startswith('ERROR'):
incorrectLines.append(line)
self.incorrectLines = incorrectLines
def evaluateCommand(self, cmd):
if self.incorrectLines:
return FAILURE
if cmd.rc != 0:
return FAILURE
return SUCCESS
def getText(self, cmd, results):
return self.getText2(cmd, results)
def getText2(self, cmd, results):
if results != SUCCESS and self.incorrectLines:
return ["%d API tests failed" % len(self.incorrectLines)]
return [self.name]
class RunQtAPITests(shell.Test):
name = "API tests"
description = ["API tests running"]
descriptionDone = ["API tests"]
command = ["python", "./Tools/Scripts/run-qtwebkit-tests",
"--output-file=qt-unit-tests.html", "--do-not-open-results", "--timeout=120",
WithProperties("WebKitBuild/%(configuration_pretty)s/WebKit/qt/tests/")]
def start(self):
self.setProperty("configuration_pretty", self.getProperty("configuration").title())
return shell.Test.start(self)
def commandComplete(self, cmd):
shell.Test.commandComplete(self, cmd)
logText = cmd.logs['stdio'].getText()
foundItems = re.findall("TOTALS: (?P<passed>\d+) passed, (?P<failed>\d+) failed, (?P<skipped>\d+) skipped", logText)
self.incorrectTests = 0
self.statusLine = []
if foundItems:
self.incorrectTests = int(foundItems[0][1])
if self.incorrectTests > 0:
self.statusLine = [
"%s passed, %s failed, %s skipped" % (foundItems[0][0], foundItems[0][1], foundItems[0][2])
]
def evaluateCommand(self, cmd):
if self.incorrectTests:
return WARNINGS
if cmd.rc != 0:
return FAILURE
return SUCCESS
def getText(self, cmd, results):
return self.getText2(cmd, results)
def getText2(self, cmd, results):
if results != SUCCESS and self.incorrectTests:
return self.statusLine
return [self.name]
class RunWebKitLeakTests(RunWebKitTests):
warnOnWarnings = True
def start(self):
self.setCommand(self.command + ["--leaks"])
return RunWebKitTests.start(self)
class RunWebKit2Tests(RunWebKitTests):
def start(self):
self.setCommand(self.command + ["--webkit-test-runner"])
return RunWebKitTests.start(self)
class RunChromiumWebKitUnitTests(shell.Test):
name = "webkit-unit-tests"
description = ["webkit-unit-tests running"]
descriptionDone = ["webkit-unit-tests"]
command = ["perl", "./Tools/Scripts/run-chromium-webkit-unit-tests",
WithProperties("--%(configuration)s")]
class ArchiveTestResults(shell.ShellCommand):
command = ["python", "./Tools/BuildSlaveSupport/test-result-archive",
WithProperties("--platform=%(platform)s"), WithProperties("--%(configuration)s"), "archive"]
name = "archive-test-results"
description = ["archiving test results"]
descriptionDone = ["archived test results"]
haltOnFailure = True
class UploadTestResults(transfer.FileUpload):
slavesrc = "layout-test-results.zip"
masterdest = WithProperties("public_html/results/%(buildername)s/r%(got_revision)s (%(buildnumber)s).zip")
def __init__(self):
transfer.FileUpload.__init__(self, self.slavesrc, self.masterdest, mode=0644)
class ExtractTestResults(master.MasterShellCommand):
zipFile = WithProperties("public_html/results/%(buildername)s/r%(got_revision)s (%(buildnumber)s).zip")
resultDirectory = WithProperties("public_html/results/%(buildername)s/r%(got_revision)s (%(buildnumber)s)")
descriptionDone = ["uploaded results"]
def __init__(self):
master.MasterShellCommand.__init__(self, "")
def resultDirectoryURL(self):
return self.build.getProperties().render(self.resultDirectory).replace("public_html/", "/") + "/"
def start(self):
self.command = ["ditto", "-k", "-x", "-V", self.build.getProperties().render(self.zipFile), self.build.getProperties().render(self.resultDirectory)]
return master.MasterShellCommand.start(self)
def addCustomURLs(self):
url = self.resultDirectoryURL() + "results.html"
self.addURL("view results", url)
def finished(self, result):
self.addCustomURLs()
return master.MasterShellCommand.finished(self, result)
class ExtractTestResultsAndLeaks(ExtractTestResults):
def addCustomURLs(self):
ExtractTestResults.addCustomURLs(self)
url = "/LeaksViewer/?url=" + urllib.quote(self.resultDirectoryURL(), safe="")
self.addURL("view leaks", url)
class Factory(factory.BuildFactory):
def __init__(self, platform, configuration, architectures, buildOnly):
factory.BuildFactory.__init__(self)
self.addStep(ConfigureBuild, platform=platform, configuration=configuration, architecture=" ".join(architectures), buildOnly=buildOnly)
self.addStep(CheckOutSource)
if platform in ("win", "chromium-win"):
self.addStep(KillOldProcesses)
if platform == "win":
self.addStep(InstallWin32Dependencies)
if platform.startswith("chromium"):
self.addStep(InstallChromiumDependencies)
class BuildFactory(Factory):
def __init__(self, platform, configuration, architectures, triggers=None):
Factory.__init__(self, platform, configuration, architectures, True)
self.addStep(CompileWebKit)
if triggers:
self.addStep(ArchiveBuiltProduct)
self.addStep(UploadBuiltProduct)
self.addStep(trigger.Trigger, schedulerNames=triggers)
class TestFactory(Factory):
TestClass = RunWebKitTests
ExtractTestResultsClass = ExtractTestResults
def __init__(self, platform, configuration, architectures):
Factory.__init__(self, platform, configuration, architectures, False)
self.addStep(DownloadBuiltProduct)
self.addStep(ExtractBuiltProduct)
self.addStep(RunJavaScriptCoreTests, skipBuild=True)
self.addStep(self.TestClass, skipBuild=(platform == 'win'))
# Tiger's Python 2.3 is too old. WebKit Python requires 2.5+.
# Sadly we have no way to detect the version on the slave from here.
if platform != "mac-tiger":
self.addStep(RunPythonTests)
self.addStep(RunPerlTests)
self.addStep(ArchiveTestResults)
self.addStep(UploadTestResults)
self.addStep(self.ExtractTestResultsClass)
class BuildAndTestFactory(Factory):
TestClass = RunWebKitTests
ExtractTestResultsClass = ExtractTestResults
def __init__(self, platform, configuration, architectures):
Factory.__init__(self, platform, configuration, architectures, False)
if platform.startswith("chromium"):
self.addStep(CleanupChromiumCrashLogs)
self.addStep(CompileWebKit)
if not platform.startswith("chromium"):
self.addStep(RunJavaScriptCoreTests)
if platform.startswith("chromium"):
self.addStep(RunChromiumWebKitUnitTests)
self.addStep(self.TestClass)
# Tiger's Python 2.3 is too old. WebKit Python requires 2.5+.
# Sadly we have no way to detect the version on the slave from here.
# Chromium Win runs in non-Cygwin environment, which is not yet fit
# for running tests. This can be removed once bug 48166 is fixed.
if platform != "mac-tiger":
self.addStep(RunPythonTests)
# Chromium Win runs in non-Cygwin environment, which is not yet fit
# for running tests. This can be removed once bug 48166 is fixed.
if platform != "chromium-win":
self.addStep(RunPerlTests)
self.addStep(ArchiveTestResults)
self.addStep(UploadTestResults)
self.addStep(self.ExtractTestResultsClass)
if platform == "gtk":
self.addStep(RunGtkAPITests)
if platform == "qt":
self.addStep(RunQtAPITests)
class BuildAndTestLeaksFactory(BuildAndTestFactory):
TestClass = RunWebKitLeakTests
ExtractTestResultsClass = ExtractTestResultsAndLeaks
class NewBuildAndTestFactory(BuildAndTestFactory):
TestClass = NewRunWebKitTests
class TestWebKit2Factory(TestFactory):
TestClass = RunWebKit2Tests
class PlatformSpecificScheduler(AnyBranchScheduler):
def __init__(self, platform, branch, **kwargs):
self.platform = platform
filter = ChangeFilter(branch=[branch, None], filter_fn=self.filter)
AnyBranchScheduler.__init__(self, name=platform, change_filter=filter, **kwargs)
def filter(self, change):
return wkbuild.should_build(self.platform, change.files)
trunk_filter = ChangeFilter(branch=["trunk", None])
def loadBuilderConfig(c):
# FIXME: These file handles are leaked.
passwords = simplejson.load(open('passwords.json'))
config = simplejson.load(open('config.json'))
# use webkitpy's buildbot module to test for core builders
wkbb = wkbuildbot()
c['slaves'] = [BuildSlave(slave['name'], passwords[slave['name']], max_builds=1) for slave in config['slaves']]
c['schedulers'] = []
for scheduler in config['schedulers']:
if "change_filter" in scheduler:
scheduler["change_filter"] = globals()[scheduler["change_filter"]]
kls = globals()[scheduler.pop('type')]
c['schedulers'].append(kls(**scheduler))
c['builders'] = []
for builder in config['builders']:
for slaveName in builder['slavenames']:
for slave in config['slaves']:
if slave['name'] != slaveName or slave['platform'] == '*':
continue
if slave['platform'] != builder['platform']:
raise Exception, "Builder %r is for platform %r but has slave %r for platform %r!" % (builder['name'], builder['platform'], slave['name'], slave['platform'])
break
factory = globals()["%sFactory" % builder.pop('type')]
factoryArgs = []
for key in "platform", "configuration", "architectures", "triggers":
value = builder.pop(key, None)
if value:
factoryArgs.append(value)
builder["factory"] = factory(*factoryArgs)
builder["category"] = "noncore"
if wkbb._is_core_builder(builder['name']):
builder["category"] = "core"
c['builders'].append(builder)
loadBuilderConfig(c)
c['change_source'] = PBChangeSource()
# permissions for WebStatus
authz = Authz(
forceBuild=True,
forceAllBuilds=True,
pingBuilder=True,
gracefulShutdown=False,
stopBuild=True,
stopAllBuilds=True,
cancelPendingBuild=True,
stopChange=True,
cleanShutdown=False)
c['status'] = []
c['status'].append(html.WebStatus(http_port=8710,
revlink="http://trac.webkit.org/changeset/%s",
authz=authz))
c['slavePortnum'] = 17000
c['projectName'] = "WebKit"
c['projectURL'] = "http://webkit.org"
c['buildbotURL'] = "http://build.webkit.org/"
c['buildHorizon'] = 1000
c['logHorizon'] = 500
c['eventHorizon'] = 200
c['buildCacheSize'] = 60