2 # ex: set syntax=python:
4 c = BuildmasterConfig = {}
6 from buildbot.buildslave import BuildSlave
7 from buildbot.changes.pb import PBChangeSource
8 from buildbot.scheduler import AnyBranchScheduler, Triggerable
9 from buildbot.schedulers.filter import ChangeFilter
10 from buildbot.status import html
11 from buildbot.status.web.authz import Authz
12 from buildbot.process import buildstep, factory, properties
13 from buildbot.steps import master, shell, source, transfer, trigger
14 from buildbot.status.builder import SUCCESS, FAILURE, WARNINGS, SKIPPED
16 from twisted.internet import defer
22 from webkitpy.common.config import build as wkbuild
23 from webkitpy.common.net.buildbot import BuildBot as wkbuildbot
25 WithProperties = properties.WithProperties
27 class ConfigureBuild(buildstep.BuildStep):
28 name = "configure build"
29 description = ["configuring build"]
30 descriptionDone = ["configured build"]
31 def __init__(self, platform, configuration, architecture, buildOnly, *args, **kwargs):
32 buildstep.BuildStep.__init__(self, *args, **kwargs)
33 self.platform = platform.split('-', 1)[0]
34 self.fullPlatform = platform
35 self.configuration = configuration
36 self.architecture = architecture
37 self.buildOnly = buildOnly
38 self.addFactoryArguments(platform=platform, configuration=configuration, architecture=architecture, buildOnly=buildOnly)
41 self.setProperty("platform", self.platform)
42 self.setProperty("fullPlatform", self.fullPlatform)
43 self.setProperty("configuration", self.configuration)
44 self.setProperty("architecture", self.architecture)
45 self.setProperty("buildOnly", self.buildOnly)
46 self.finished(SUCCESS)
47 return defer.succeed(None)
50 class CheckOutSource(source.SVN):
51 baseURL = "http://svn.webkit.org/repository/webkit/"
53 def __init__(self, *args, **kwargs):
54 source.SVN.__init__(self, baseURL=self.baseURL, defaultBranch="trunk", mode=self.mode, *args, **kwargs)
57 class InstallWin32Dependencies(shell.Compile):
58 description = ["installing dependencies"]
59 descriptionDone = ["installed dependencies"]
60 command = ["perl", "./Tools/Scripts/update-webkit-auxiliary-libs"]
62 class KillOldProcesses(shell.Compile):
63 name = "kill old processes"
64 description = ["killing old processes"]
65 descriptionDone = ["killed old processes"]
66 command = ["python", "./Tools/BuildSlaveSupport/win/kill-old-processes"]
68 class InstallChromiumDependencies(shell.ShellCommand):
70 description = ["updating chromium dependencies"]
71 descriptionDone = ["updated chromium dependencies"]
72 command = ["perl", "./Tools/Scripts/update-webkit-chromium", "--force"]
75 class CleanupChromiumCrashLogs(shell.ShellCommand):
76 name = "cleanup crash logs"
77 description = ["removing crash logs"]
78 descriptionDone = ["removed crash logs"]
79 command = ["python", "./Tools/BuildSlaveSupport/chromium/remove-crash-logs"]
83 def appendCustomBuildFlags(step, platform):
84 if platform in ('chromium', 'efl', 'gtk', 'qt', 'wincairo', 'wince', 'wx'):
85 step.setCommand(step.command + ['--' + platform])
88 class CompileWebKit(shell.Compile):
89 command = ["perl", "./Tools/Scripts/build-webkit", WithProperties("--%(configuration)s")]
91 name = "compile-webkit"
92 description = ["compiling"]
93 descriptionDone = ["compiled"]
94 warningPattern = ".*arning: .*"
97 platform = self.getProperty('platform')
98 buildOnly = self.getProperty('buildOnly')
99 if platform == 'mac' and buildOnly:
100 self.setCommand(self.command + ['DEBUG_INFORMATION_FORMAT=dwarf-with-dsym'])
102 appendCustomBuildFlags(self, platform)
103 return shell.Compile.start(self)
106 class ArchiveBuiltProduct(shell.ShellCommand):
107 command = ["python", "./Tools/BuildSlaveSupport/built-product-archive",
108 WithProperties("--platform=%(platform)s"), WithProperties("--%(configuration)s"), "archive"]
109 name = "archive-built-product"
110 description = ["archiving built product"]
111 descriptionDone = ["archived built product"]
115 class ExtractBuiltProduct(shell.ShellCommand):
116 command = ["python", "./Tools/BuildSlaveSupport/built-product-archive",
117 WithProperties("--platform=%(platform)s"), WithProperties("--%(configuration)s"), "extract"]
118 name = "extract-built-product"
119 description = ["extracting built product"]
120 descriptionDone = ["extracted built product"]
124 class UploadBuiltProduct(transfer.FileUpload):
125 slavesrc = WithProperties("WebKitBuild/%(configuration)s.zip")
126 masterdest = WithProperties("archives/%(fullPlatform)s-%(architecture)s-%(configuration)s/%(got_revision)s.zip")
130 transfer.FileUpload.__init__(self, self.slavesrc, self.masterdest, mode=0644)
133 class DownloadBuiltProduct(transfer.FileDownload):
134 slavedest = WithProperties("WebKitBuild/%(configuration)s.zip")
135 mastersrc = WithProperties("archives/%(fullPlatform)s-%(architecture)s-%(configuration)s/%(got_revision)s.zip")
137 flunkOnFailure = True
140 transfer.FileDownload.__init__(self, self.mastersrc, self.slavedest)
143 class RunJavaScriptCoreTests(shell.Test):
145 description = ["jscore-tests running"]
146 descriptionDone = ["jscore-tests"]
147 command = ["perl", "./Tools/Scripts/run-javascriptcore-tests", WithProperties("--%(configuration)s")]
148 logfiles = {'results': 'Source/JavaScriptCore/tests/mozilla/actual.html'}
150 def __init__(self, skipBuild=False, *args, **kwargs):
151 self.skipBuild = skipBuild
152 shell.Test.__init__(self, *args, **kwargs)
153 self.addFactoryArguments(skipBuild=skipBuild)
156 appendCustomBuildFlags(self, self.getProperty('platform'))
158 self.setCommand(self.command + ['--skip-build'])
159 return shell.Test.start(self)
161 def commandComplete(self, cmd):
162 shell.Test.commandComplete(self, cmd)
164 logText = cmd.logs['stdio'].getText()
165 statusLines = [line for line in logText.splitlines() if line.find('regression') >= 0 and line.find(' found.') >= 0]
166 if statusLines and statusLines[0].split()[0] != '0':
167 self.regressionLine = statusLines[0]
169 self.regressionLine = None
171 def evaluateCommand(self, cmd):
172 if self.regressionLine:
180 def getText(self, cmd, results):
181 return self.getText2(cmd, results)
183 def getText2(self, cmd, results):
184 if results != SUCCESS and self.regressionLine:
185 return [self.name, self.regressionLine]
190 class RunWebKitTests(shell.Test):
192 description = ["layout-tests running"]
193 descriptionDone = ["layout-tests"]
194 command = ["perl", "./Tools/Scripts/run-webkit-tests", "--no-launch-safari", "--no-new-test-results",
195 "--no-sample-on-timeout", "--results-directory", "layout-test-results", "--use-remote-links-to-tests",
196 WithProperties("--%(configuration)s"), "--exit-after-n-crashes-or-timeouts", "20", "--exit-after-n-failures", "500"]
198 def __init__(self, skipBuild=False, *args, **kwargs):
199 self.skipBuild = skipBuild
200 shell.Test.__init__(self, *args, **kwargs)
201 self.addFactoryArguments(skipBuild=skipBuild)
204 platform = self.getProperty('platform')
205 appendCustomBuildFlags(self, platform)
206 if platform == "win":
207 rootArgument = ['--root=' + os.path.join("WebKitBuild", self.getProperty('configuration'), "bin")]
209 rootArgument = ['--root=WebKitBuild/bin']
211 self.setCommand(self.command + rootArgument)
212 return shell.Test.start(self)
214 def commandComplete(self, cmd):
215 shell.Test.commandComplete(self, cmd)
217 logText = cmd.logs['stdio'].getText()
218 incorrectLayoutLines = []
219 for line in logText.splitlines():
220 if line.find('had incorrect layout') >= 0 or line.find('were new') >= 0 or line.find('was new') >= 0:
221 incorrectLayoutLines.append(line)
222 elif line.find('test case') >= 0 and (line.find(' crashed') >= 0 or line.find(' timed out') >= 0):
223 incorrectLayoutLines.append(line)
224 elif line.startswith("WARNING:") and line.find(' leak') >= 0:
225 incorrectLayoutLines.append(line.replace('WARNING: ', ''))
226 elif line.find('Exiting early') >= 0:
227 incorrectLayoutLines.append(line)
229 # FIXME: Detect and summarize leaks of RefCounted objects
231 self.incorrectLayoutLines = incorrectLayoutLines
233 def evaluateCommand(self, cmd):
234 if self.incorrectLayoutLines:
235 if len(self.incorrectLayoutLines) == 1:
236 line = self.incorrectLayoutLines[0]
237 if line.find('were new') >= 0 or line.find('was new') >= 0 or line.find(' leak') >= 0:
247 def getText(self, cmd, results):
248 return self.getText2(cmd, results)
250 def getText2(self, cmd, results):
251 if results != SUCCESS and self.incorrectLayoutLines:
252 return self.incorrectLayoutLines
257 class NewRunWebKitTests(RunWebKitTests):
258 command = ["python", "./Tools/Scripts/new-run-webkit-tests", "--noshow-results",
259 "--verbose", "--results-directory", "layout-test-results",
260 "--builder-name", WithProperties("%(buildername)s"),
261 "--build-number", WithProperties("%(buildnumber)s"),
262 "--master-name", "webkit.org",
263 "--test-results-server", "test-results.appspot.com",
264 WithProperties("--%(configuration)s")]
267 class RunPythonTests(shell.Test):
268 name = "webkitpy-test"
269 description = ["python-tests running"]
270 descriptionDone = ["python-tests"]
271 command = ["python", "./Tools/Scripts/test-webkitpy"]
274 class RunPerlTests(shell.Test):
275 name = "webkitperl-test"
276 description = ["perl-tests running"]
277 descriptionDone = ["perl-tests"]
278 command = ["perl", "./Tools/Scripts/test-webkitperl"]
281 class RunGtkAPITests(shell.Test):
283 description = ["API tests running"]
284 descriptionDone = ["API tests"]
285 command = ["perl", "./Tools/Scripts/run-gtk-tests", WithProperties("--%(configuration)s")]
287 def commandComplete(self, cmd):
288 shell.Test.commandComplete(self, cmd)
290 logText = cmd.logs['stdio'].getText()
292 for line in logText.splitlines():
293 if line.startswith('ERROR'):
294 incorrectLines.append(line)
296 self.incorrectLines = incorrectLines
298 def evaluateCommand(self, cmd):
299 if self.incorrectLines:
307 def getText(self, cmd, results):
308 return self.getText2(cmd, results)
310 def getText2(self, cmd, results):
311 if results != SUCCESS and self.incorrectLines:
312 return ["%d API tests failed" % len(self.incorrectLines)]
316 class RunQtAPITests(shell.Test):
318 description = ["API tests running"]
319 descriptionDone = ["API tests"]
320 command = ["python", "./Tools/Scripts/run-qtwebkit-tests",
321 "--output-file=qt-unit-tests.html", "--do-not-open-results", "--timeout=120",
322 WithProperties("WebKitBuild/%(configuration_pretty)s/WebKit/qt/tests/")]
325 self.setProperty("configuration_pretty", self.getProperty("configuration").title())
326 return shell.Test.start(self)
328 def commandComplete(self, cmd):
329 shell.Test.commandComplete(self, cmd)
331 logText = cmd.logs['stdio'].getText()
332 foundItems = re.findall("TOTALS: (?P<passed>\d+) passed, (?P<failed>\d+) failed, (?P<skipped>\d+) skipped", logText)
334 self.incorrectTests = 0
338 self.incorrectTests = int(foundItems[0][1])
339 if self.incorrectTests > 0:
341 "%s passed, %s failed, %s skipped" % (foundItems[0][0], foundItems[0][1], foundItems[0][2])
344 def evaluateCommand(self, cmd):
345 if self.incorrectTests:
353 def getText(self, cmd, results):
354 return self.getText2(cmd, results)
356 def getText2(self, cmd, results):
357 if results != SUCCESS and self.incorrectTests:
358 return self.statusLine
362 class RunWebKitLeakTests(RunWebKitTests):
364 self.setCommand(self.command + ["--leaks"])
365 return RunWebKitTests.start(self)
368 class RunWebKit2Tests(RunWebKitTests):
370 self.setCommand(self.command + ["--webkit-test-runner"])
371 return RunWebKitTests.start(self)
374 class RunChromiumWebKitUnitTests(shell.Test):
375 name = "webkit-unit-tests"
376 description = ["webkit-unit-tests running"]
377 descriptionDone = ["webkit-unit-tests"]
378 command = ["perl", "./Tools/Scripts/run-chromium-webkit-unit-tests",
379 WithProperties("--%(configuration)s")]
382 class ArchiveTestResults(shell.ShellCommand):
383 command = ["python", "./Tools/BuildSlaveSupport/test-result-archive",
384 WithProperties("--platform=%(platform)s"), WithProperties("--%(configuration)s"), "archive"]
385 name = "archive-test-results"
386 description = ["archiving test results"]
387 descriptionDone = ["archived test results"]
391 class UploadTestResults(transfer.FileUpload):
392 slavesrc = "layout-test-results.zip"
393 masterdest = WithProperties("public_html/results/%(buildername)s/r%(got_revision)s (%(buildnumber)s).zip")
396 transfer.FileUpload.__init__(self, self.slavesrc, self.masterdest, mode=0644)
399 class ExtractTestResults(master.MasterShellCommand):
400 zipFile = WithProperties("public_html/results/%(buildername)s/r%(got_revision)s (%(buildnumber)s).zip")
401 resultDirectory = WithProperties("public_html/results/%(buildername)s/r%(got_revision)s (%(buildnumber)s)")
404 master.MasterShellCommand.__init__(self, "")
407 self.command = ["ditto", "-k", "-x", "-V", self.build.getProperties().render(self.zipFile), self.build.getProperties().render(self.resultDirectory)]
408 return master.MasterShellCommand.start(self)
410 def finished(self, result):
411 url = self.build.getProperties().render(self.resultDirectory).replace("public_html/", "/")
412 self.addURL("view results", url)
413 result = master.MasterShellCommand.finished(self, result)
414 self.step_status.setText(["uploaded results"])
418 class Factory(factory.BuildFactory):
419 def __init__(self, platform, configuration, architectures, buildOnly):
420 factory.BuildFactory.__init__(self)
421 self.addStep(ConfigureBuild, platform=platform, configuration=configuration, architecture=" ".join(architectures), buildOnly=buildOnly)
422 self.addStep(CheckOutSource)
423 if platform == "win":
424 self.addStep(KillOldProcesses)
425 self.addStep(InstallWin32Dependencies)
426 if platform.startswith("chromium"):
427 self.addStep(InstallChromiumDependencies)
429 class BuildFactory(Factory):
430 def __init__(self, platform, configuration, architectures, triggers=None):
431 Factory.__init__(self, platform, configuration, architectures, True)
432 self.addStep(CompileWebKit)
434 self.addStep(ArchiveBuiltProduct)
435 self.addStep(UploadBuiltProduct)
436 self.addStep(trigger.Trigger, schedulerNames=triggers)
438 class TestFactory(Factory):
439 TestClass = RunWebKitTests
440 def __init__(self, platform, configuration, architectures):
441 Factory.__init__(self, platform, configuration, architectures, False)
442 self.addStep(DownloadBuiltProduct)
443 self.addStep(ExtractBuiltProduct)
444 self.addStep(RunJavaScriptCoreTests, skipBuild=True)
445 self.addStep(self.TestClass, skipBuild=(platform == 'win'))
446 # Tiger's Python 2.3 is too old. WebKit Python requires 2.5+.
447 # Sadly we have no way to detect the version on the slave from here.
448 if platform != "mac-tiger":
449 self.addStep(RunPythonTests)
450 self.addStep(RunPerlTests)
451 self.addStep(ArchiveTestResults)
452 self.addStep(UploadTestResults)
453 self.addStep(ExtractTestResults)
455 class BuildAndTestFactory(Factory):
456 TestClass = RunWebKitTests
457 def __init__(self, platform, configuration, architectures):
458 Factory.__init__(self, platform, configuration, architectures, False)
459 if platform.startswith("chromium"):
460 self.addStep(CleanupChromiumCrashLogs)
461 self.addStep(CompileWebKit)
462 if not platform.startswith("chromium"):
463 self.addStep(RunJavaScriptCoreTests)
464 if platform.startswith("chromium"):
465 self.addStep(RunChromiumWebKitUnitTests)
466 self.addStep(self.TestClass)
467 # Tiger's Python 2.3 is too old. WebKit Python requires 2.5+.
468 # Sadly we have no way to detect the version on the slave from here.
469 # Chromium Win runs in non-Cygwin environment, which is not yet fit
470 # for running tests. This can be removed once bug 48166 is fixed.
471 if platform != "mac-tiger":
472 self.addStep(RunPythonTests)
473 # Chromium Win runs in non-Cygwin environment, which is not yet fit
474 # for running tests. This can be removed once bug 48166 is fixed.
475 if platform != "chromium-win":
476 self.addStep(RunPerlTests)
477 self.addStep(ArchiveTestResults)
478 self.addStep(UploadTestResults)
479 self.addStep(ExtractTestResults)
480 if platform == "gtk":
481 self.addStep(RunGtkAPITests)
483 self.addStep(RunQtAPITests)
485 class BuildAndTestLeaksFactory(BuildAndTestFactory):
486 TestClass = RunWebKitLeakTests
488 class NewBuildAndTestFactory(BuildAndTestFactory):
489 TestClass = NewRunWebKitTests
491 class TestWebKit2Factory(TestFactory):
492 TestClass = RunWebKit2Tests
494 class PlatformSpecificScheduler(AnyBranchScheduler):
495 def __init__(self, platform, branch, **kwargs):
496 self.platform = platform
497 filter = ChangeFilter(branch=[branch, None], filter_fn=self.filter)
498 AnyBranchScheduler.__init__(self, name=platform, change_filter=filter, **kwargs)
500 def filter(self, change):
501 return wkbuild.should_build(self.platform, change.files)
503 trunk_filter = ChangeFilter(branch=["trunk", None])
505 def loadBuilderConfig(c):
506 # FIXME: These file handles are leaked.
507 passwords = simplejson.load(open('passwords.json'))
508 config = simplejson.load(open('config.json'))
510 # use webkitpy's buildbot module to test for core builders
513 c['slaves'] = [BuildSlave(slave['name'], passwords[slave['name']], max_builds=1) for slave in config['slaves']]
516 for scheduler in config['schedulers']:
517 if "change_filter" in scheduler:
518 scheduler["change_filter"] = globals()[scheduler["change_filter"]]
519 kls = globals()[scheduler.pop('type')]
520 c['schedulers'].append(kls(**scheduler))
523 for builder in config['builders']:
524 for slaveName in builder['slavenames']:
525 for slave in config['slaves']:
526 if slave['name'] != slaveName or slave['platform'] == '*':
529 if slave['platform'] != builder['platform']:
530 raise Exception, "Builder %r is for platform %r but has slave %r for platform %r!" % (builder['name'], builder['platform'], slave['name'], slave['platform'])
534 factory = globals()["%sFactory" % builder.pop('type')]
536 for key in "platform", "configuration", "architectures", "triggers":
537 value = builder.pop(key, None)
539 factoryArgs.append(value)
541 builder["factory"] = factory(*factoryArgs)
543 builder["category"] = "noncore"
544 if wkbb._is_core_builder(builder['name']):
545 builder["category"] = "core"
547 c['builders'].append(builder)
551 c['change_source'] = PBChangeSource()
553 # permissions for WebStatus
558 gracefulShutdown=False,
561 cancelPendingBuild=True,
566 c['status'].append(html.WebStatus(http_port=8710,
567 revlink="http://trac.webkit.org/changeset/%s",
570 c['slavePortnum'] = 17000
571 c['projectName'] = "WebKit"
572 c['projectURL'] = "http://webkit.org"
573 c['buildbotURL'] = "http://build.webkit.org/"
575 c['buildHorizon'] = 1000
576 c['logHorizon'] = 500
577 c['eventHorizon'] = 200
578 c['buildCacheSize'] = 60