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

133"""Set a list of 'step factories', which are tuples of (class,134 kwargs), where 'class' is generally a subclass of step.BuildStep .135 These are used to create the Steps themselves when the Build starts136 (as opposed to when it is first created). By creating the steps137 later, their __init__ method will have access to things like138 build.allFiles() ."""139self.stepFactories=list(step_factories)

237d.addCallback(_release_slave,self.slavebuilder.slave,build_status)238239try:240self.setupBuild(expectations)# create .steps241except:242# the build hasn't started yet, so log the exception as a point243# event instead of flunking the build. 244# TODO: associate this failure with the build instead. 245# this involves doing246# self.build_status.buildStarted() from within the exception247# handler248log.msg("Build.setupBuild failed")249log.err(Failure())250self.builder.builder_status.addPointEvent(["setupBuild",251"exception"])252self.finished=True253self.results=FAILURE254self.deferred=None255d.callback(self)256returnd257258self.build_status.buildStarted(self)259self.acquireLocks().addCallback(self._startBuild_2)260returnd261

359"""This method is called to obtain the next BuildStep for this build.360 When it returns None (or raises a StopIteration exception), the build361 is complete."""362ifnotself.steps:363returnNone364ifnotself.remote:365returnNone366ifself.terminateorself.stopped:367# Run any remaining alwaysRun steps, and skip over the others368whileTrue:369s=self.steps.pop(0)370ifs.alwaysRun:371returns372ifnotself.steps:373returnNone374else:375returnself.steps.pop(0)

442# the slave went away. There are several possible reasons for this,443# and they aren't necessarily fatal. For now, kill the build, but444# TODO: see if we can resume the build when it reconnects.445log.msg("%s.lostRemote"%self)446self.remote=None447ifself.currentStep:448# this should cause the step to finish.449log.msg(" stopping currentStep",self.currentStep)450self.currentStep.interrupt(Failure(error.ConnectionLost()))451else:452self.result=RETRY453self.text=["lost","remote"]454self.stopped=True455ifself._acquiringLock:456lock,access,d=self._acquiringLock457lock.stopWaitingUntilAvailable(self,access,d)458d.callback(None)

461# the idea here is to let the user cancel a build because, e.g.,462# they realized they committed a bug and they don't want to waste463# the time building something that they know will fail. Another464# reason might be to abandon a stuck build. We want to mark the465# build as failed quickly rather than waiting for the slave's466# timeout to kill it on its own.467468log.msg(" %s: stopping build: %s"%(self,reason))469ifself.finished:470return471# TODO: include 'reason' in this point event472self.builder.builder_status.addPointEvent(['interrupt'])473self.stopped=True474ifself.currentStep:475self.currentStep.interrupt(reason)476477self.result=EXCEPTION478479ifself._acquiringLock:480lock,access,d=self._acquiringLock481lock.stopWaitingUntilAvailable(self,access,d)482d.callback(None)

499log.msg("%s.buildException"%self)500log.err(why)501# try to finish the build, but since we've already faced an exception,502# this may not work well.503try:504self.buildFinished(["build","exception"],EXCEPTION)505except:506log.err(Failure(),'while finishing a build with an exception')