1# This file is part of Buildbot. Buildbot is free software: you can 2# redistribute it and/or modify it under the terms of the GNU General Public 3# License as published by the Free Software Foundation, version 2. 4# 5# This program is distributed in the hope that it will be useful, but WITHOUT 6# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 7# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 8# details. 9# 10# You should have received a copy of the GNU General Public License along with 11# this program; if not, write to the Free Software Foundation, Inc., 51 12# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 13# 14# Copyright Buildbot Team Members 15 16 17importtypes 18 19fromzope.interfaceimportimplements 20fromtwisted.pythonimportlog,components 21fromtwisted.python.failureimportFailure 22fromtwisted.internetimportreactor,defer,error 23 24frombuildbotimportinterfaces,locks 25frombuildbot.status.resultsimportSUCCESS,WARNINGS,FAILURE,EXCEPTION, \ 26RETRY,SKIPPED,worst_status 27frombuildbot.status.builderimportResults 28frombuildbot.status.progressimportBuildProgress 29frombuildbot.processimportmetrics,properties 30 31

33"""I represent a single build by a single slave. Specialized Builders can 34 use subclasses of Build to hold status information unique to those build 35 processes. 36 37 I control B{how} the build proceeds. The actual build is broken up into a 38 series of steps, saved in the .buildSteps[] array as a list of 39 L{buildbot.process.step.BuildStep} objects. Each step is a single remote 40 command, possibly a shell command. 41 42 During the build, I put status information into my C{BuildStatus} 43 gatherer. 44 45 After the build, I go away. 46 47 I can be used by a factory by setting buildClass on 48 L{buildbot.process.factory.BuildFactory} 49 50 @ivar requests: the list of L{BuildRequest}s that triggered me 51 @ivar build_status: the L{buildbot.status.build.BuildStatus} that 52 collects our status 53 """ 54 55implements(interfaces.IBuildControl) 56 57workdir="build" 58build_status=None 59reason="changes" 60finished=False 61results=None 62stopped=False 63set_runtime_properties=True 64

130"""Set a list of 'step factories', which are tuples of (class,131 kwargs), where 'class' is generally a subclass of step.BuildStep .132 These are used to create the Steps themselves when the Build starts133 (as opposed to when it is first created). By creating the steps134 later, their __init__ method will have access to things like135 build.allFiles() ."""136self.stepFactories=list(step_factories)

230d.addCallback(_release_slave,self.slavebuilder.slave,build_status)231232try:233self.setupBuild(expectations)# create .steps234except:235# the build hasn't started yet, so log the exception as a point236# event instead of flunking the build. 237# TODO: associate this failure with the build instead. 238# this involves doing239# self.build_status.buildStarted() from within the exception240# handler241log.msg("Build.setupBuild failed")242log.err(Failure())243self.builder.builder_status.addPointEvent(["setupBuild",244"exception"])245self.finished=True246self.results=FAILURE247self.deferred=None248d.callback(self)249returnd250251self.build_status.buildStarted(self)252self.acquireLocks().addCallback(self._startBuild_2)253returnd254

352"""This method is called to obtain the next BuildStep for this build.353 When it returns None (or raises a StopIteration exception), the build354 is complete."""355ifnotself.steps:356returnNone357ifnotself.remote:358returnNone359ifself.terminateorself.stopped:360# Run any remaining alwaysRun steps, and skip over the others361whileTrue:362s=self.steps.pop(0)363ifs.alwaysRun:364returns365ifnotself.steps:366returnNone367else:368returnself.steps.pop(0)

435# the slave went away. There are several possible reasons for this,436# and they aren't necessarily fatal. For now, kill the build, but437# TODO: see if we can resume the build when it reconnects.438log.msg("%s.lostRemote"%self)439self.remote=None440ifself.currentStep:441# this should cause the step to finish.442log.msg(" stopping currentStep",self.currentStep)443self.currentStep.interrupt(Failure(error.ConnectionLost()))

446# the idea here is to let the user cancel a build because, e.g.,447# they realized they committed a bug and they don't want to waste448# the time building something that they know will fail. Another449# reason might be to abandon a stuck build. We want to mark the450# build as failed quickly rather than waiting for the slave's451# timeout to kill it on its own.452453log.msg(" %s: stopping build: %s"%(self,reason))454ifself.finished:455return456# TODO: include 'reason' in this point event457self.builder.builder_status.addPointEvent(['interrupt'])458self.stopped=True459ifself.currentStep:460self.currentStep.interrupt(reason)461462self.result=EXCEPTION463464ifself._acquiringLock:465lock,access,d=self._acquiringLock466lock.stopWaitingUntilAvailable(self,access,d)467d.callback(None)