| OLD | NEW |
| (Empty) |
| 1 # -*- python -*- | |
| 2 # ex: set syntax=python: | |
| 3 | |
| 4 # These modules come from scripts/master, which must be in the PYTHONPATH. | |
| 5 # TODO(maruel): Upstream these to make it webkit.org-friendly. | |
| 6 from builders_pools import BuildersPools | |
| 7 from try_job_http import TryJobHTTP | |
| 8 from try_mail_notifier import TryMailNotifier | |
| 9 #from try_job_svn import TryJobSubversion | |
| 10 | |
| 11 | |
| 12 c = BuildmasterConfig = {} | |
| 13 | |
| 14 from buildbot.buildslave import BuildSlave | |
| 15 from buildbot.changes.pb import PBChangeSource | |
| 16 from buildbot.scheduler import AnyBranchScheduler, Triggerable | |
| 17 from buildbot.status import html | |
| 18 from buildbot.process import buildstep, factory, properties | |
| 19 from buildbot.steps import master, shell, source, transfer, trigger | |
| 20 from buildbot.status.builder import SUCCESS, FAILURE, WARNINGS, SKIPPED | |
| 21 | |
| 22 from twisted.internet import defer | |
| 23 | |
| 24 import re | |
| 25 import simplejson | |
| 26 | |
| 27 WithProperties = properties.WithProperties | |
| 28 | |
| 29 class ConfigureBuild(buildstep.BuildStep): | |
| 30 name = "configure build" | |
| 31 description = ["configuring build"] | |
| 32 descriptionDone = ["configured build"] | |
| 33 def __init__(self, platform, configuration, architecture, buildOnly, *args,
**kwargs): | |
| 34 buildstep.BuildStep.__init__(self, *args, **kwargs) | |
| 35 self.platform = platform.split('-', 1)[0] | |
| 36 self.fullPlatform = platform | |
| 37 self.configuration = configuration | |
| 38 self.architecture = architecture | |
| 39 self.buildOnly = buildOnly | |
| 40 self.addFactoryArguments(platform=platform, configuration=configuration,
architecture=architecture, buildOnly=buildOnly) | |
| 41 | |
| 42 def start(self): | |
| 43 self.setProperty("platform", self.platform) | |
| 44 self.setProperty("fullPlatform", self.fullPlatform) | |
| 45 self.setProperty("configuration", self.configuration) | |
| 46 self.setProperty("architecture", self.architecture) | |
| 47 self.setProperty("buildOnly", self.buildOnly) | |
| 48 self.finished(SUCCESS) | |
| 49 return defer.succeed(None) | |
| 50 | |
| 51 | |
| 52 class CheckOutSource(source.SVN): | |
| 53 baseURL = "http://svn.webkit.org/repository/webkit/" | |
| 54 mode = "update" | |
| 55 def __init__(self, *args, **kwargs): | |
| 56 source.SVN.__init__(self, baseURL=self.baseURL, defaultBranch="trunk", m
ode=self.mode, *args, **kwargs) | |
| 57 | |
| 58 | |
| 59 class InstallWin32Dependencies(shell.Compile): | |
| 60 description = ["installing dependencies"] | |
| 61 descriptionDone = ["installed dependencies"] | |
| 62 command = ["perl", "./WebKitTools/Scripts/update-webkit-auxiliary-libs"] | |
| 63 | |
| 64 | |
| 65 class InstallChromiumDependencies(shell.ShellCommand): | |
| 66 name = "gclient" | |
| 67 description = ["updating chromium dependencies"] | |
| 68 descriptionDone = ["updated chromium dependencies"] | |
| 69 command = ["perl", "./WebKitTools/Scripts/update-webkit-chromium"] | |
| 70 haltOnFailure = True | |
| 71 | |
| 72 | |
| 73 def appendCustomBuildFlags(step, platform): | |
| 74 if platform in ('gtk', 'wx', 'qt', 'chromium'): | |
| 75 step.setCommand(step.command + ['--' + platform]) | |
| 76 | |
| 77 | |
| 78 class CompileWebKit(shell.Compile): | |
| 79 command = ["perl", "./WebKitTools/Scripts/build-webkit", WithProperties("--%
(configuration)s")] | |
| 80 env = {'MFLAGS':''} | |
| 81 name = "compile-webkit" | |
| 82 description = ["compiling"] | |
| 83 descriptionDone = ["compiled"] | |
| 84 warningPattern = ".*arning: .*" | |
| 85 | |
| 86 def start(self): | |
| 87 platform = self.getProperty('platform') | |
| 88 buildOnly = self.getProperty('buildOnly') | |
| 89 if platform == 'mac' and buildOnly: | |
| 90 self.setCommand(self.command + ['DEBUG_INFORMATION_FORMAT=dwarf-with
-dsym']) | |
| 91 | |
| 92 appendCustomBuildFlags(self, platform) | |
| 93 return shell.Compile.start(self) | |
| 94 | |
| 95 | |
| 96 class ArchiveBuiltProduct(shell.ShellCommand): | |
| 97 command = ["python", "./WebKitTools/BuildSlaveSupport/built-product-archive"
, | |
| 98 WithProperties("--platform=%(platform)s"), WithProperties("--%(co
nfiguration)s"), "archive"] | |
| 99 name = "archive-built-product" | |
| 100 description = ["archiving built product"] | |
| 101 descriptionDone = ["archived built product"] | |
| 102 haltOnFailure = True | |
| 103 | |
| 104 | |
| 105 class ExtractBuiltProduct(shell.ShellCommand): | |
| 106 command = ["python", "./WebKitTools/BuildSlaveSupport/built-product-archive"
, | |
| 107 WithProperties("--platform=%(platform)s"), WithProperties("--%(co
nfiguration)s"), "extract"] | |
| 108 name = "extract-built-product" | |
| 109 description = ["extracting built product"] | |
| 110 descriptionDone = ["extracted built product"] | |
| 111 haltOnFailure = True | |
| 112 | |
| 113 | |
| 114 class UploadBuiltProduct(transfer.FileUpload): | |
| 115 slavesrc = WithProperties("WebKitBuild/%(configuration)s.zip") | |
| 116 masterdest = WithProperties("archives/%(fullPlatform)s-%(architecture)s-%(co
nfiguration)s/%(got_revision)s.zip") | |
| 117 haltOnFailure = True | |
| 118 | |
| 119 def __init__(self): | |
| 120 transfer.FileUpload.__init__(self, self.slavesrc, self.masterdest) | |
| 121 | |
| 122 | |
| 123 class DownloadBuiltProduct(transfer.FileDownload): | |
| 124 slavedest = WithProperties("WebKitBuild/%(configuration)s.zip") | |
| 125 mastersrc = WithProperties("archives/%(fullPlatform)s-%(architecture)s-%(con
figuration)s/%(got_revision)s.zip") | |
| 126 haltOnFailure = True | |
| 127 | |
| 128 def __init__(self): | |
| 129 transfer.FileDownload.__init__(self, self.mastersrc, self.slavedest) | |
| 130 | |
| 131 | |
| 132 class RunJavaScriptCoreTests(shell.Test): | |
| 133 name = "jscore-test" | |
| 134 description = ["jscore-tests running"] | |
| 135 descriptionDone = ["jscore-tests"] | |
| 136 command = ["perl", "./WebKitTools/Scripts/run-javascriptcore-tests", WithPro
perties("--%(configuration)s")] | |
| 137 logfiles = {'results': 'JavaScriptCore/tests/mozilla/actual.html'} | |
| 138 | |
| 139 def __init__(self, skipBuild=False, *args, **kwargs): | |
| 140 self.skipBuild = skipBuild | |
| 141 shell.Test.__init__(self, *args, **kwargs) | |
| 142 self.addFactoryArguments(skipBuild=skipBuild) | |
| 143 | |
| 144 def start(self): | |
| 145 appendCustomBuildFlags(self, self.getProperty('platform')) | |
| 146 if self.skipBuild: | |
| 147 self.setCommand(self.command + ['--skip-build']) | |
| 148 return shell.Test.start(self) | |
| 149 | |
| 150 def commandComplete(self, cmd): | |
| 151 shell.Test.commandComplete(self, cmd) | |
| 152 | |
| 153 logText = cmd.logs['stdio'].getText() | |
| 154 statusLines = [line for line in logText.splitlines() if line.find('regre
ssion') >= 0 and line.find(' found.') >= 0] | |
| 155 if statusLines and statusLines[0].split()[0] != '0': | |
| 156 self.regressionLine = statusLines[0] | |
| 157 else: | |
| 158 self.regressionLine = None | |
| 159 | |
| 160 def evaluateCommand(self, cmd): | |
| 161 if self.regressionLine: | |
| 162 return FAILURE | |
| 163 | |
| 164 if cmd.rc != 0: | |
| 165 return FAILURE | |
| 166 | |
| 167 return SUCCESS | |
| 168 | |
| 169 def getText(self, cmd, results): | |
| 170 return self.getText2(cmd, results) | |
| 171 | |
| 172 def getText2(self, cmd, results): | |
| 173 if results != SUCCESS and self.regressionLine: | |
| 174 return [self.name, self.regressionLine] | |
| 175 | |
| 176 return [self.name] | |
| 177 | |
| 178 | |
| 179 class RunWebKitTests(shell.Test): | |
| 180 name = "layout-test" | |
| 181 description = ["layout-tests running"] | |
| 182 descriptionDone = ["layout-tests"] | |
| 183 command = ["perl", "./WebKitTools/Scripts/run-webkit-tests", "--no-launch-sa
fari", "--no-new-test-results", | |
| 184 "--no-sample-on-timeout", "--results-directory", "layout-test-res
ults", "--use-remote-links-to-tests", | |
| 185 WithProperties("--%(configuration)s"), "--exit-after-n-failures",
"20"] | |
| 186 | |
| 187 def __init__(self, skipBuild=False, *args, **kwargs): | |
| 188 self.skipBuild = skipBuild | |
| 189 shell.Test.__init__(self, *args, **kwargs) | |
| 190 self.addFactoryArguments(skipBuild=skipBuild) | |
| 191 | |
| 192 def start(self): | |
| 193 appendCustomBuildFlags(self, self.getProperty('platform')) | |
| 194 if self.skipBuild: | |
| 195 self.setCommand(self.command + ['--root=WebKitBuild/bin']) | |
| 196 return shell.Test.start(self) | |
| 197 | |
| 198 def commandComplete(self, cmd): | |
| 199 shell.Test.commandComplete(self, cmd) | |
| 200 | |
| 201 logText = cmd.logs['stdio'].getText() | |
| 202 incorrectLayoutLines = [] | |
| 203 for line in logText.splitlines(): | |
| 204 if line.find('had incorrect layout') >= 0 or line.find('were new') >
= 0 or line.find('was new') >= 0: | |
| 205 incorrectLayoutLines.append(line) | |
| 206 elif line.find('test case') >= 0 and (line.find(' crashed') >= 0 or
line.find(' timed out') >= 0): | |
| 207 incorrectLayoutLines.append(line) | |
| 208 elif line.startswith("WARNING:") and line.find(' leak') >= 0: | |
| 209 incorrectLayoutLines.append(line.replace('WARNING: ', '')) | |
| 210 elif line.find('Exiting early') >= 0: | |
| 211 incorrectLayoutLines.append(line) | |
| 212 | |
| 213 # FIXME: Detect and summarize leaks of RefCounted objects | |
| 214 | |
| 215 self.incorrectLayoutLines = incorrectLayoutLines | |
| 216 | |
| 217 def evaluateCommand(self, cmd): | |
| 218 if self.incorrectLayoutLines: | |
| 219 if len(self.incorrectLayoutLines) == 1: | |
| 220 line = self.incorrectLayoutLines[0] | |
| 221 if line.find('were new') >= 0 or line.find('was new') >= 0 or li
ne.find(' leak') >= 0: | |
| 222 return WARNINGS | |
| 223 | |
| 224 return FAILURE | |
| 225 | |
| 226 if cmd.rc != 0: | |
| 227 return FAILURE | |
| 228 | |
| 229 return SUCCESS | |
| 230 | |
| 231 def getText(self, cmd, results): | |
| 232 return self.getText2(cmd, results) | |
| 233 | |
| 234 def getText2(self, cmd, results): | |
| 235 if results != SUCCESS and self.incorrectLayoutLines: | |
| 236 return self.incorrectLayoutLines | |
| 237 | |
| 238 return [self.name] | |
| 239 | |
| 240 | |
| 241 class RunWebKitLeakTests(RunWebKitTests): | |
| 242 def start(self): | |
| 243 self.setCommand(self.command + ["--leaks"]) | |
| 244 return RunWebKitTests.start(self) | |
| 245 | |
| 246 | |
| 247 class ArchiveTestResults(shell.ShellCommand): | |
| 248 command = ["python", "./WebKitTools/BuildSlaveSupport/test-result-archive", | |
| 249 WithProperties("--platform=%(platform)s"), WithProperties("--%(co
nfiguration)s"), "archive"] | |
| 250 name = "archive-test-results" | |
| 251 description = ["archiving test results"] | |
| 252 descriptionDone = ["archived test results"] | |
| 253 haltOnFailure = True | |
| 254 | |
| 255 | |
| 256 class UploadTestResults(transfer.FileUpload): | |
| 257 slavesrc = "layout-test-results.zip" | |
| 258 masterdest = WithProperties("public_html/results/%(buildername)s/r%(got_revi
sion)s (%(buildnumber)s).zip") | |
| 259 | |
| 260 def __init__(self): | |
| 261 transfer.FileUpload.__init__(self, self.slavesrc, self.masterdest) | |
| 262 | |
| 263 | |
| 264 class ExtractTestResults(master.MasterShellCommand): | |
| 265 zipFile = WithProperties("public_html/results/%(buildername)s/r%(got_revisio
n)s (%(buildnumber)s).zip") | |
| 266 resultDirectory = WithProperties("public_html/results/%(buildername)s/r%(got
_revision)s (%(buildnumber)s)") | |
| 267 | |
| 268 def __init__(self): | |
| 269 master.MasterShellCommand.__init__(self, "") | |
| 270 | |
| 271 def start(self): | |
| 272 self.command = ["ditto", "-k", "-x", "-V", self.build.getProperties().re
nder(self.zipFile), self.build.getProperties().render(self.resultDirectory)] | |
| 273 return master.MasterShellCommand.start(self) | |
| 274 | |
| 275 def finished(self, result): | |
| 276 url = self.build.getProperties().render(self.resultDirectory).replace("p
ublic_html/", "") | |
| 277 self.addURL("view results", url) | |
| 278 result = master.MasterShellCommand.finished(self, result) | |
| 279 self.step_status.setText(["uploaded results"]) | |
| 280 return result | |
| 281 | |
| 282 | |
| 283 class Factory(factory.BuildFactory): | |
| 284 def __init__(self, platform, configuration, architectures, buildOnly): | |
| 285 factory.BuildFactory.__init__(self) | |
| 286 self.addStep(ConfigureBuild, platform=platform, configuration=configurat
ion, architecture=" ".join(architectures), buildOnly=buildOnly) | |
| 287 self.addStep(CheckOutSource) | |
| 288 if platform == "win": | |
| 289 self.addStep(InstallWin32Dependencies) | |
| 290 if platform == "chromium": | |
| 291 self.addStep(InstallChromiumDependencies) | |
| 292 | |
| 293 class BuildFactory(Factory): | |
| 294 def __init__(self, platform, configuration, architectures, triggers=None): | |
| 295 Factory.__init__(self, platform, configuration, architectures, True) | |
| 296 self.addStep(CompileWebKit) | |
| 297 if triggers: | |
| 298 self.addStep(ArchiveBuiltProduct) | |
| 299 self.addStep(UploadBuiltProduct) | |
| 300 self.addStep(trigger.Trigger, schedulerNames=triggers) | |
| 301 | |
| 302 class TestFactory(Factory): | |
| 303 def __init__(self, platform, configuration, architectures): | |
| 304 Factory.__init__(self, platform, configuration, architectures, False) | |
| 305 self.addStep(DownloadBuiltProduct) | |
| 306 self.addStep(ExtractBuiltProduct) | |
| 307 self.addStep(RunJavaScriptCoreTests, skipBuild=True) | |
| 308 self.addStep(RunWebKitTests, skipBuild=(platform == 'win')) | |
| 309 self.addStep(ArchiveTestResults) | |
| 310 self.addStep(UploadTestResults) | |
| 311 self.addStep(ExtractTestResults) | |
| 312 | |
| 313 class BuildAndTestFactory(Factory): | |
| 314 TestClass = RunWebKitTests | |
| 315 def __init__(self, platform, configuration, architectures): | |
| 316 Factory.__init__(self, platform, configuration, architectures, False) | |
| 317 self.addStep(CompileWebKit) | |
| 318 self.addStep(RunJavaScriptCoreTests) | |
| 319 self.addStep(self.TestClass) | |
| 320 self.addStep(ArchiveTestResults) | |
| 321 self.addStep(UploadTestResults) | |
| 322 self.addStep(ExtractTestResults) | |
| 323 | |
| 324 class BuildAndTestLeaksFactory(BuildAndTestFactory): | |
| 325 TestClass = RunWebKitLeakTests | |
| 326 | |
| 327 | |
| 328 class TryFactory(Factory): | |
| 329 """Similar to BuildAndTestFactory but doesn't archive the results.""" | |
| 330 TestClass = RunWebKitTests | |
| 331 def __init__(self, platform, configuration, architectures): | |
| 332 Factory.__init__(self, platform, configuration, architectures, False) | |
| 333 self.addStep(CompileWebKit) | |
| 334 self.addStep(RunJavaScriptCoreTests) | |
| 335 self.addStep(self.TestClass) | |
| 336 | |
| 337 | |
| 338 class TryBuildFactory(BuildFactory): | |
| 339 """Same as BuildFactory.""" | |
| 340 pass | |
| 341 | |
| 342 | |
| 343 class TryLeaksFactory(TryFactory): | |
| 344 TestClass = RunWebKitLeakTests | |
| 345 | |
| 346 | |
| 347 def convertToString(json_data): | |
| 348 """Converts unicode strings to ascii strings in dictionaries and lists. | |
| 349 | |
| 350 Some simplejson version will always return unicode strings. Python | |
| 351 doesn't accept unicode strings as dictionary key for kwargs so convert | |
| 352 them.""" | |
| 353 if isinstance(json_data, dict): | |
| 354 retval = {} | |
| 355 for (k,v) in json_data.iteritems(): | |
| 356 retval[str(k)] = convertToString(v) | |
| 357 return retval | |
| 358 elif isinstance(json_data, list): | |
| 359 retval = [] | |
| 360 for i in json_data: | |
| 361 retval.append(convertToString(i)) | |
| 362 return retval | |
| 363 elif isinstance(json_data, unicode): | |
| 364 return str(json_data) | |
| 365 else: | |
| 366 return json_data | |
| 367 | |
| 368 | |
| 369 def loadBuilderConfig(c, config_file, passwords_file): | |
| 370 passwords = convertToString(simplejson.load(open(passwords_file))) | |
| 371 | |
| 372 config = convertToString(simplejson.load(open(config_file))) | |
| 373 | |
| 374 c['slaves'] = [BuildSlave(slave['name'], passwords[slave['name']], max_build
s=1) for slave in config['slaves']] | |
| 375 | |
| 376 c['schedulers'] = [] | |
| 377 for scheduler in config['schedulers']: | |
| 378 kls = globals()[scheduler.pop('type')] | |
| 379 c['schedulers'].append(kls(**scheduler)) | |
| 380 | |
| 381 c['builders'] = [] | |
| 382 for builder in config['builders']: | |
| 383 for slaveName in builder['slavenames']: | |
| 384 for slave in config['slaves']: | |
| 385 if slave['name'] != slaveName or slave['platform'] == '*': | |
| 386 continue | |
| 387 | |
| 388 if slave['platform'] != builder['platform']: | |
| 389 raise Exception, "Builder %r is for platform %r but has slav
e %r for platform %r!" % (builder['name'], builder['platform'], slave['name'], s
lave['platform']) | |
| 390 | |
| 391 break | |
| 392 | |
| 393 factory = globals()["%sFactory" % builder.pop('type')] | |
| 394 factoryArgs = [] | |
| 395 for key in "platform", "configuration", "architectures", "triggers": | |
| 396 value = builder.pop(key, None) | |
| 397 if value: | |
| 398 factoryArgs.append(value) | |
| 399 | |
| 400 builder["factory"] = factory(*factoryArgs) | |
| 401 | |
| 402 c['builders'].append(builder) | |
| 403 | |
| 404 | |
| 405 loadBuilderConfig(c, 'config.json', 'passwords.json') | |
| 406 | |
| 407 c['change_source'] = PBChangeSource() | |
| 408 pools = BuildersPools('webkit') | |
| 409 #pools['webkit'].append("Try Apple Leopard Intel Release") | |
| 410 pools['webkit'].append("Try Apple Leopard Intel Debug") | |
| 411 #pools['webkit'].append("Try Apple SnowLeopard Intel Release") | |
| 412 #pools['webkit'].append("Try Apple SnowLeopard Intel Leaks") | |
| 413 #pools['webkit'].append("Try Apple Windows Release") | |
| 414 pools['webkit'].append("Try Apple Windows Debug") | |
| 415 #pools['webkit'].append("Try GTK Linux Release") | |
| 416 #pools['webkit'].append("Try Qt Linux Release") | |
| 417 pools['webkit'].append("Try Chromium Win Release") | |
| 418 pools['webkit'].append("Try Chromium Mac Release") | |
| 419 pools['webkit'].append("Try Chromium Linux Release") | |
| 420 c['schedulers'].append(TryJobHTTP( | |
| 421 name='try_job_http', | |
| 422 port=8017, | |
| 423 pools=pools)) | |
| 424 | |
| 425 c['status'] = [] | |
| 426 c['status'].append(html.WebStatus(http_port=8010, allowForce=True)) | |
| 427 c['status'].append(html.WebStatus(http_port=8044, allowForce=False)) | |
| 428 | |
| 429 def GetSmtp(): | |
| 430 # TODO(maruel): Remove me. | |
| 431 import chromium_config_private | |
| 432 return chromium_config_private.Master.smtp | |
| 433 | |
| 434 c['status'].append(TryMailNotifier( | |
| 435 fromaddr='tryserver@chromium.org', | |
| 436 subject="try %(result)s for %(reason)s on %(builder)s @ r%(revision)s", | |
| 437 mode='all', | |
| 438 relayhost=GetSmtp(), | |
| 439 lookup='')) | |
| 440 | |
| 441 | |
| 442 c['slavePortnum'] = 8031 | |
| 443 c['projectName'] = "WebKit" | |
| 444 c['projectURL'] = "http://webkit.org" | |
| 445 c['buildbotURL'] = "http://build.chromium.org/buildbot/webkit-try/" | |
| 446 | |
| 447 # Keep last try jobs, the default is too low. Must keep at least a few days | |
| 448 # worth of try jobs. | |
| 449 c['buildHorizon'] = 3000 | |
| 450 c['logHorizon'] = 3000 | |
| 451 # Must be at least 2x the number of slaves. | |
| 452 c['eventHorizon'] = 200 | |
| 453 # Must be at least 2x the number of on-going builds. | |
| 454 c['buildCacheSize'] = 200 | |
| 455 | |
| OLD | NEW |