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 in ("win", "chromium-win"):
424 self.addStep(KillOldProcesses)
425 if platform == "win":
426 self.addStep(InstallWin32Dependencies)
427 if platform.startswith("chromium"):
428 self.addStep(InstallChromiumDependencies)
430 class BuildFactory(Factory):
431 def __init__(self, platform, configuration, architectures, triggers=None):
432 Factory.__init__(self, platform, configuration, architectures, True)
433 self.addStep(CompileWebKit)
435 self.addStep(ArchiveBuiltProduct)
436 self.addStep(UploadBuiltProduct)
437 self.addStep(trigger.Trigger, schedulerNames=triggers)
439 class TestFactory(Factory):
440 TestClass = RunWebKitTests
441 def __init__(self, platform, configuration, architectures):
442 Factory.__init__(self, platform, configuration, architectures, False)
443 self.addStep(DownloadBuiltProduct)
444 self.addStep(ExtractBuiltProduct)
445 self.addStep(RunJavaScriptCoreTests, skipBuild=True)
446 self.addStep(self.TestClass, skipBuild=(platform == 'win'))
447 # Tiger's Python 2.3 is too old. WebKit Python requires 2.5+.
448 # Sadly we have no way to detect the version on the slave from here.
449 if platform != "mac-tiger":
450 self.addStep(RunPythonTests)
451 self.addStep(RunPerlTests)
452 self.addStep(ArchiveTestResults)
453 self.addStep(UploadTestResults)
454 self.addStep(ExtractTestResults)
456 class BuildAndTestFactory(Factory):
457 TestClass = RunWebKitTests
458 def __init__(self, platform, configuration, architectures):
459 Factory.__init__(self, platform, configuration, architectures, False)
460 if platform.startswith("chromium"):
461 self.addStep(CleanupChromiumCrashLogs)
462 self.addStep(CompileWebKit)
463 if not platform.startswith("chromium"):
464 self.addStep(RunJavaScriptCoreTests)
465 if platform.startswith("chromium"):
466 self.addStep(RunChromiumWebKitUnitTests)
467 self.addStep(self.TestClass)
468 # Tiger's Python 2.3 is too old. WebKit Python requires 2.5+.
469 # Sadly we have no way to detect the version on the slave from here.
470 # Chromium Win runs in non-Cygwin environment, which is not yet fit
471 # for running tests. This can be removed once bug 48166 is fixed.
472 if platform != "mac-tiger":
473 self.addStep(RunPythonTests)
474 # Chromium Win runs in non-Cygwin environment, which is not yet fit
475 # for running tests. This can be removed once bug 48166 is fixed.
476 if platform != "chromium-win":
477 self.addStep(RunPerlTests)
478 self.addStep(ArchiveTestResults)
479 self.addStep(UploadTestResults)
480 self.addStep(ExtractTestResults)
481 if platform == "gtk":
482 self.addStep(RunGtkAPITests)
484 self.addStep(RunQtAPITests)
486 class BuildAndTestLeaksFactory(BuildAndTestFactory):
487 TestClass = RunWebKitLeakTests
489 class NewBuildAndTestFactory(BuildAndTestFactory):
490 TestClass = NewRunWebKitTests
492 class TestWebKit2Factory(TestFactory):
493 TestClass = RunWebKit2Tests
495 class PlatformSpecificScheduler(AnyBranchScheduler):
496 def __init__(self, platform, branch, **kwargs):
497 self.platform = platform
498 filter = ChangeFilter(branch=[branch, None], filter_fn=self.filter)
499 AnyBranchScheduler.__init__(self, name=platform, change_filter=filter, **kwargs)
501 def filter(self, change):
502 return wkbuild.should_build(self.platform, change.files)
504 trunk_filter = ChangeFilter(branch=["trunk", None])
506 def loadBuilderConfig(c):
507 # FIXME: These file handles are leaked.
508 passwords = simplejson.load(open('passwords.json'))
509 config = simplejson.load(open('config.json'))
511 # use webkitpy's buildbot module to test for core builders
514 c['slaves'] = [BuildSlave(slave['name'], passwords[slave['name']], max_builds=1) for slave in config['slaves']]
517 for scheduler in config['schedulers']:
518 if "change_filter" in scheduler:
519 scheduler["change_filter"] = globals()[scheduler["change_filter"]]
520 kls = globals()[scheduler.pop('type')]
521 c['schedulers'].append(kls(**scheduler))
524 for builder in config['builders']:
525 for slaveName in builder['slavenames']:
526 for slave in config['slaves']:
527 if slave['name'] != slaveName or slave['platform'] == '*':
530 if slave['platform'] != builder['platform']:
531 raise Exception, "Builder %r is for platform %r but has slave %r for platform %r!" % (builder['name'], builder['platform'], slave['name'], slave['platform'])
535 factory = globals()["%sFactory" % builder.pop('type')]
537 for key in "platform", "configuration", "architectures", "triggers":
538 value = builder.pop(key, None)
540 factoryArgs.append(value)
542 builder["factory"] = factory(*factoryArgs)
544 builder["category"] = "noncore"
545 if wkbb._is_core_builder(builder['name']):
546 builder["category"] = "core"
548 c['builders'].append(builder)
552 c['change_source'] = PBChangeSource()
554 # permissions for WebStatus
559 gracefulShutdown=False,
562 cancelPendingBuild=True,
567 c['status'].append(html.WebStatus(http_port=8710,
568 revlink="http://trac.webkit.org/changeset/%s",
571 c['slavePortnum'] = 17000
572 c['projectName'] = "WebKit"
573 c['projectURL'] = "http://webkit.org"
574 c['buildbotURL'] = "http://build.webkit.org/"
576 c['buildHorizon'] = 1000
577 c['logHorizon'] = 500
578 c['eventHorizon'] = 200
579 c['buildCacheSize'] = 60