| Index: third_party/buildbot_7_12/buildbot/process/base.py
|
| diff --git a/third_party/buildbot_7_12/buildbot/process/base.py b/third_party/buildbot_7_12/buildbot/process/base.py
|
| deleted file mode 100644
|
| index 18748508513738bf4f9d7c7d774a1a192268d84b..0000000000000000000000000000000000000000
|
| --- a/third_party/buildbot_7_12/buildbot/process/base.py
|
| +++ /dev/null
|
| @@ -1,622 +0,0 @@
|
| -# -*- test-case-name: buildbot.test.test_step -*-
|
| -
|
| -import types
|
| -
|
| -from zope.interface import implements
|
| -from twisted.python import log
|
| -from twisted.python.failure import Failure
|
| -from twisted.internet import reactor, defer, error
|
| -
|
| -from buildbot import interfaces, locks
|
| -from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION
|
| -from buildbot.status.builder import Results, BuildRequestStatus
|
| -from buildbot.status.progress import BuildProgress
|
| -from buildbot.process.properties import Properties
|
| -
|
| -class BuildRequest:
|
| - """I represent a request to a specific Builder to run a single build.
|
| -
|
| - I have a SourceStamp which specifies what sources I will build. This may
|
| - specify a specific revision of the source tree (so source.branch,
|
| - source.revision, and source.patch are used). The .patch attribute is
|
| - either None or a tuple of (patchlevel, diff), consisting of a number to
|
| - use in 'patch -pN', and a unified-format context diff.
|
| -
|
| - Alternatively, the SourceStamp may specify a set of Changes to be built,
|
| - contained in source.changes. In this case, I may be mergeable with other
|
| - BuildRequests on the same branch.
|
| -
|
| - I may be part of a BuildSet, in which case I will report status results
|
| - to it.
|
| -
|
| - I am paired with a BuildRequestStatus object, to which I feed status
|
| - information.
|
| -
|
| - @type source: a L{buildbot.sourcestamp.SourceStamp} instance.
|
| - @ivar source: the source code that this BuildRequest use
|
| -
|
| - @type reason: string
|
| - @ivar reason: the reason this Build is being requested. Schedulers
|
| - provide this, but for forced builds the user requesting the
|
| - build will provide a string.
|
| -
|
| - @type properties: Properties object
|
| - @ivar properties: properties that should be applied to this build
|
| - 'owner' property is used by Build objects to collect
|
| - the list returned by getInterestedUsers
|
| -
|
| - @ivar status: the IBuildStatus object which tracks our status
|
| -
|
| - @ivar submittedAt: a timestamp (seconds since epoch) when this request
|
| - was submitted to the Builder. This is used by the CVS
|
| - step to compute a checkout timestamp, as well as the
|
| - master to prioritize build requests from oldest to
|
| - newest.
|
| - """
|
| -
|
| - source = None
|
| - builder = None
|
| - startCount = 0 # how many times we have tried to start this build
|
| - submittedAt = None
|
| -
|
| - implements(interfaces.IBuildRequestControl)
|
| -
|
| - def __init__(self, reason, source, builderName, properties=None):
|
| - assert interfaces.ISourceStamp(source, None)
|
| - self.reason = reason
|
| - self.source = source
|
| -
|
| - self.properties = Properties()
|
| - if properties:
|
| - self.properties.updateFromProperties(properties)
|
| -
|
| - self.start_watchers = []
|
| - self.finish_watchers = []
|
| - self.status = BuildRequestStatus(source, builderName)
|
| -
|
| - def canBeMergedWith(self, other):
|
| - return self.source.canBeMergedWith(other.source)
|
| -
|
| - def mergeWith(self, others):
|
| - return self.source.mergeWith([o.source for o in others])
|
| -
|
| - def mergeReasons(self, others):
|
| - """Return a reason for the merged build request."""
|
| - reasons = []
|
| - for req in [self] + others:
|
| - if req.reason and req.reason not in reasons:
|
| - reasons.append(req.reason)
|
| - return ", ".join(reasons)
|
| -
|
| - def waitUntilFinished(self):
|
| - """Get a Deferred that will fire (with a
|
| - L{buildbot.interfaces.IBuildStatus} instance when the build
|
| - finishes."""
|
| - d = defer.Deferred()
|
| - self.finish_watchers.append(d)
|
| - return d
|
| -
|
| - # these are called by the Builder
|
| -
|
| - def requestSubmitted(self, builder):
|
| - # the request has been placed on the queue
|
| - self.builder = builder
|
| -
|
| - def buildStarted(self, build, buildstatus):
|
| - """This is called by the Builder when a Build has been started in the
|
| - hopes of satifying this BuildRequest. It may be called multiple
|
| - times, since interrupted builds and lost buildslaves may force
|
| - multiple Builds to be run until the fate of the BuildRequest is known
|
| - for certain."""
|
| - for o in self.start_watchers[:]:
|
| - # these observers get the IBuildControl
|
| - o(build)
|
| - # while these get the IBuildStatus
|
| - self.status.buildStarted(buildstatus)
|
| -
|
| - def finished(self, buildstatus):
|
| - """This is called by the Builder when the BuildRequest has been
|
| - retired. This happens when its Build has either succeeded (yay!) or
|
| - failed (boo!). TODO: If it is halted due to an exception (oops!), or
|
| - some other retryable error, C{finished} will not be called yet."""
|
| -
|
| - for w in self.finish_watchers:
|
| - w.callback(buildstatus)
|
| - self.finish_watchers = []
|
| -
|
| - # IBuildRequestControl
|
| -
|
| - def subscribe(self, observer):
|
| - self.start_watchers.append(observer)
|
| - def unsubscribe(self, observer):
|
| - self.start_watchers.remove(observer)
|
| -
|
| - def cancel(self):
|
| - """Cancel this request. This can only be successful if the Build has
|
| - not yet been started.
|
| -
|
| - @return: a boolean indicating if the cancel was successful."""
|
| - if self.builder:
|
| - return self.builder.cancelBuildRequest(self)
|
| - return False
|
| -
|
| - def setSubmitTime(self, t):
|
| - self.submittedAt = t
|
| - self.status.setSubmitTime(t)
|
| -
|
| - def getSubmitTime(self):
|
| - return self.submittedAt
|
| -
|
| -
|
| -class Build:
|
| - """I represent a single build by a single slave. Specialized Builders can
|
| - use subclasses of Build to hold status information unique to those build
|
| - processes.
|
| -
|
| - I control B{how} the build proceeds. The actual build is broken up into a
|
| - series of steps, saved in the .buildSteps[] array as a list of
|
| - L{buildbot.process.step.BuildStep} objects. Each step is a single remote
|
| - command, possibly a shell command.
|
| -
|
| - During the build, I put status information into my C{BuildStatus}
|
| - gatherer.
|
| -
|
| - After the build, I go away.
|
| -
|
| - I can be used by a factory by setting buildClass on
|
| - L{buildbot.process.factory.BuildFactory}
|
| -
|
| - @ivar requests: the list of L{BuildRequest}s that triggered me
|
| - @ivar build_status: the L{buildbot.status.builder.BuildStatus} that
|
| - collects our status
|
| - """
|
| -
|
| - implements(interfaces.IBuildControl)
|
| -
|
| - workdir = "build"
|
| - build_status = None
|
| - reason = "changes"
|
| - finished = False
|
| - results = None
|
| -
|
| - def __init__(self, requests):
|
| - self.requests = requests
|
| - for req in self.requests:
|
| - req.startCount += 1
|
| - self.locks = []
|
| - # build a source stamp
|
| - self.source = requests[0].mergeWith(requests[1:])
|
| - self.reason = requests[0].mergeReasons(requests[1:])
|
| -
|
| - self.progress = None
|
| - self.currentStep = None
|
| - self.slaveEnvironment = {}
|
| -
|
| - self.terminate = False
|
| -
|
| - def setBuilder(self, builder):
|
| - """
|
| - Set the given builder as our builder.
|
| -
|
| - @type builder: L{buildbot.process.builder.Builder}
|
| - """
|
| - self.builder = builder
|
| -
|
| - def setLocks(self, locks):
|
| - self.locks = locks
|
| -
|
| - def setSlaveEnvironment(self, env):
|
| - self.slaveEnvironment = env
|
| -
|
| - def getSourceStamp(self):
|
| - return self.source
|
| -
|
| - def setProperty(self, propname, value, source):
|
| - """Set a property on this build. This may only be called after the
|
| - build has started, so that it has a BuildStatus object where the
|
| - properties can live."""
|
| - self.build_status.setProperty(propname, value, source)
|
| -
|
| - def getProperties(self):
|
| - return self.build_status.getProperties()
|
| -
|
| - def getProperty(self, propname):
|
| - return self.build_status.getProperty(propname)
|
| -
|
| - def allChanges(self):
|
| - return self.source.changes
|
| -
|
| - def allFiles(self):
|
| - # return a list of all source files that were changed
|
| - files = []
|
| - havedirs = 0
|
| - for c in self.allChanges():
|
| - for f in c.files:
|
| - files.append(f)
|
| - if c.isdir:
|
| - havedirs = 1
|
| - return files
|
| -
|
| - def __repr__(self):
|
| - return "<Build %s>" % (self.builder.name,)
|
| -
|
| - def blamelist(self):
|
| - blamelist = []
|
| - for c in self.allChanges():
|
| - if c.who not in blamelist:
|
| - blamelist.append(c.who)
|
| - blamelist.sort()
|
| - return blamelist
|
| -
|
| - def changesText(self):
|
| - changetext = ""
|
| - for c in self.allChanges():
|
| - changetext += "-" * 60 + "\n\n" + c.asText() + "\n"
|
| - # consider sorting these by number
|
| - return changetext
|
| -
|
| - def setStepFactories(self, step_factories):
|
| - """Set a list of 'step factories', which are tuples of (class,
|
| - kwargs), where 'class' is generally a subclass of step.BuildStep .
|
| - These are used to create the Steps themselves when the Build starts
|
| - (as opposed to when it is first created). By creating the steps
|
| - later, their __init__ method will have access to things like
|
| - build.allFiles() ."""
|
| - self.stepFactories = list(step_factories)
|
| -
|
| -
|
| -
|
| - useProgress = True
|
| -
|
| - def getSlaveCommandVersion(self, command, oldversion=None):
|
| - return self.slavebuilder.getSlaveCommandVersion(command, oldversion)
|
| - def getSlaveName(self):
|
| - return self.slavebuilder.slave.slavename
|
| -
|
| - def setupProperties(self):
|
| - props = self.getProperties()
|
| -
|
| - # start with global properties from the configuration
|
| - buildmaster = self.builder.botmaster.parent
|
| - props.updateFromProperties(buildmaster.properties)
|
| -
|
| - # get any properties from requests (this is the path through
|
| - # which schedulers will send us properties)
|
| - for rq in self.requests:
|
| - props.updateFromProperties(rq.properties)
|
| -
|
| - # and finally, from the SourceStamp, which has properties via Change
|
| - for change in self.source.changes:
|
| - props.updateFromProperties(change.properties)
|
| -
|
| - # now set some properties of our own, corresponding to the
|
| - # build itself
|
| - props.setProperty("buildername", self.builder.name, "Build")
|
| - props.setProperty("buildnumber", self.build_status.number, "Build")
|
| - props.setProperty("branch", self.source.branch, "Build")
|
| - props.setProperty("revision", self.source.revision, "Build")
|
| -
|
| - def setupSlaveBuilder(self, slavebuilder):
|
| - self.slavebuilder = slavebuilder
|
| -
|
| - # navigate our way back to the L{buildbot.buildslave.BuildSlave}
|
| - # object that came from the config, and get its properties
|
| - buildslave_properties = slavebuilder.slave.properties
|
| - self.getProperties().updateFromProperties(buildslave_properties)
|
| -
|
| - self.slavename = slavebuilder.slave.slavename
|
| - self.build_status.setSlavename(self.slavename)
|
| -
|
| - def startBuild(self, build_status, expectations, slavebuilder):
|
| - """This method sets up the build, then starts it by invoking the
|
| - first Step. It returns a Deferred which will fire when the build
|
| - finishes. This Deferred is guaranteed to never errback."""
|
| -
|
| - # we are taking responsibility for watching the connection to the
|
| - # remote. This responsibility was held by the Builder until our
|
| - # startBuild was called, and will not return to them until we fire
|
| - # the Deferred returned by this method.
|
| -
|
| - log.msg("%s.startBuild" % self)
|
| - self.build_status = build_status
|
| - # now that we have a build_status, we can set properties
|
| - self.setupProperties()
|
| - self.setupSlaveBuilder(slavebuilder)
|
| - slavebuilder.slave.updateSlaveStatus(buildStarted=build_status)
|
| -
|
| - # convert all locks into their real forms
|
| - lock_list = []
|
| - for access in self.locks:
|
| - if not isinstance(access, locks.LockAccess):
|
| - # Buildbot 0.7.7 compability: user did not specify access
|
| - access = access.defaultAccess()
|
| - lock = self.builder.botmaster.getLockByID(access.lockid)
|
| - lock_list.append((lock, access))
|
| - self.locks = lock_list
|
| - # then narrow SlaveLocks down to the right slave
|
| - self.locks = [(l.getLock(self.slavebuilder), la)
|
| - for l, la in self.locks]
|
| - self.remote = slavebuilder.remote
|
| - self.remote.notifyOnDisconnect(self.lostRemote)
|
| - d = self.deferred = defer.Deferred()
|
| - def _release_slave(res, slave, bs):
|
| - self.slavebuilder.buildFinished()
|
| - slave.updateSlaveStatus(buildFinished=bs)
|
| - return res
|
| - d.addCallback(_release_slave, self.slavebuilder.slave, build_status)
|
| -
|
| - try:
|
| - self.setupBuild(expectations) # create .steps
|
| - except:
|
| - # the build hasn't started yet, so log the exception as a point
|
| - # event instead of flunking the build. TODO: associate this
|
| - # failure with the build instead. this involves doing
|
| - # self.build_status.buildStarted() from within the exception
|
| - # handler
|
| - log.msg("Build.setupBuild failed")
|
| - log.err(Failure())
|
| - self.builder.builder_status.addPointEvent(["setupBuild",
|
| - "exception"])
|
| - self.finished = True
|
| - self.results = FAILURE
|
| - self.deferred = None
|
| - d.callback(self)
|
| - return d
|
| -
|
| - self.acquireLocks().addCallback(self._startBuild_2)
|
| - return d
|
| -
|
| - def acquireLocks(self, res=None):
|
| - log.msg("acquireLocks(step %s, locks %s)" % (self, self.locks))
|
| - if not self.locks:
|
| - return defer.succeed(None)
|
| - for lock, access in self.locks:
|
| - if not lock.isAvailable(access):
|
| - log.msg("Build %s waiting for lock %s" % (self, lock))
|
| - d = lock.waitUntilMaybeAvailable(self, access)
|
| - d.addCallback(self.acquireLocks)
|
| - return d
|
| - # all locks are available, claim them all
|
| - for lock, access in self.locks:
|
| - lock.claim(self, access)
|
| - return defer.succeed(None)
|
| -
|
| - def _startBuild_2(self, res):
|
| - self.build_status.buildStarted(self)
|
| - self.startNextStep()
|
| -
|
| - def setupBuild(self, expectations):
|
| - # create the actual BuildSteps. If there are any name collisions, we
|
| - # add a count to the loser until it is unique.
|
| - self.steps = []
|
| - self.stepStatuses = {}
|
| - stepnames = {}
|
| - sps = []
|
| -
|
| - for factory, args in self.stepFactories:
|
| - args = args.copy()
|
| - try:
|
| - step = factory(**args)
|
| - except:
|
| - log.msg("error while creating step, factory=%s, args=%s"
|
| - % (factory, args))
|
| - raise
|
| - step.setBuild(self)
|
| - step.setBuildSlave(self.slavebuilder.slave)
|
| - step.setDefaultWorkdir(self.workdir)
|
| - name = step.name
|
| - if stepnames.has_key(name):
|
| - count = stepnames[name]
|
| - count += 1
|
| - stepnames[name] = count
|
| - name = step.name + "_%d" % count
|
| - else:
|
| - stepnames[name] = 0
|
| - step.name = name
|
| - self.steps.append(step)
|
| -
|
| - # tell the BuildStatus about the step. This will create a
|
| - # BuildStepStatus and bind it to the Step.
|
| - step_status = self.build_status.addStepWithName(name)
|
| - step.setStepStatus(step_status)
|
| -
|
| - sp = None
|
| - if self.useProgress:
|
| - # XXX: maybe bail if step.progressMetrics is empty? or skip
|
| - # progress for that one step (i.e. "it is fast"), or have a
|
| - # separate "variable" flag that makes us bail on progress
|
| - # tracking
|
| - sp = step.setupProgress()
|
| - if sp:
|
| - sps.append(sp)
|
| -
|
| - # Create a buildbot.status.progress.BuildProgress object. This is
|
| - # called once at startup to figure out how to build the long-term
|
| - # Expectations object, and again at the start of each build to get a
|
| - # fresh BuildProgress object to track progress for that individual
|
| - # build. TODO: revisit at-startup call
|
| -
|
| - if self.useProgress:
|
| - self.progress = BuildProgress(sps)
|
| - if self.progress and expectations:
|
| - self.progress.setExpectationsFrom(expectations)
|
| -
|
| - # we are now ready to set up our BuildStatus.
|
| - self.build_status.setSourceStamp(self.source)
|
| - self.build_status.setRequests([req.status for req in self.requests])
|
| - self.build_status.setReason(self.reason)
|
| - self.build_status.setBlamelist(self.blamelist())
|
| - self.build_status.setProgress(self.progress)
|
| -
|
| - # gather owners from build requests
|
| - owners = [r.properties['owner'] for r in self.requests
|
| - if r.properties.has_key('owner')]
|
| - if owners: self.setProperty('owners', owners, self.reason)
|
| -
|
| - self.results = [] # list of FAILURE, SUCCESS, WARNINGS, SKIPPED
|
| - self.result = SUCCESS # overall result, may downgrade after each step
|
| - self.text = [] # list of text string lists (text2)
|
| -
|
| - def getNextStep(self):
|
| - """This method is called to obtain the next BuildStep for this build.
|
| - When it returns None (or raises a StopIteration exception), the build
|
| - is complete."""
|
| - if not self.steps:
|
| - return None
|
| - if self.terminate:
|
| - while True:
|
| - s = self.steps.pop(0)
|
| - if s.alwaysRun:
|
| - return s
|
| - if not self.steps:
|
| - return None
|
| - else:
|
| - return self.steps.pop(0)
|
| -
|
| - def startNextStep(self):
|
| - try:
|
| - s = self.getNextStep()
|
| - except StopIteration:
|
| - s = None
|
| - if not s:
|
| - return self.allStepsDone()
|
| - self.currentStep = s
|
| - d = defer.maybeDeferred(s.startStep, self.remote)
|
| - d.addCallback(self._stepDone, s)
|
| - d.addErrback(self.buildException)
|
| -
|
| - def _stepDone(self, results, step):
|
| - self.currentStep = None
|
| - if self.finished:
|
| - return # build was interrupted, don't keep building
|
| - terminate = self.stepDone(results, step) # interpret/merge results
|
| - if terminate:
|
| - self.terminate = True
|
| - return self.startNextStep()
|
| -
|
| - def stepDone(self, result, step):
|
| - """This method is called when the BuildStep completes. It is passed a
|
| - status object from the BuildStep and is responsible for merging the
|
| - Step's results into those of the overall Build."""
|
| -
|
| - terminate = False
|
| - text = None
|
| - if type(result) == types.TupleType:
|
| - result, text = result
|
| - assert type(result) == type(SUCCESS)
|
| - log.msg(" step '%s' complete: %s" % (step.name, Results[result]))
|
| - self.results.append(result)
|
| - if text:
|
| - self.text.extend(text)
|
| - if not self.remote:
|
| - terminate = True
|
| - if result == FAILURE:
|
| - if step.warnOnFailure:
|
| - if self.result != FAILURE:
|
| - self.result = WARNINGS
|
| - if step.flunkOnFailure:
|
| - self.result = FAILURE
|
| - if step.haltOnFailure:
|
| - terminate = True
|
| - elif result == WARNINGS:
|
| - if step.warnOnWarnings:
|
| - if self.result != FAILURE:
|
| - self.result = WARNINGS
|
| - if step.flunkOnWarnings:
|
| - self.result = FAILURE
|
| - elif result == EXCEPTION:
|
| - self.result = EXCEPTION
|
| - terminate = True
|
| - return terminate
|
| -
|
| - def lostRemote(self, remote=None):
|
| - # the slave went away. There are several possible reasons for this,
|
| - # and they aren't necessarily fatal. For now, kill the build, but
|
| - # TODO: see if we can resume the build when it reconnects.
|
| - log.msg("%s.lostRemote" % self)
|
| - self.remote = None
|
| - if self.currentStep:
|
| - # this should cause the step to finish.
|
| - log.msg(" stopping currentStep", self.currentStep)
|
| - self.currentStep.interrupt(Failure(error.ConnectionLost()))
|
| -
|
| - def stopBuild(self, reason="<no reason given>"):
|
| - # the idea here is to let the user cancel a build because, e.g.,
|
| - # they realized they committed a bug and they don't want to waste
|
| - # the time building something that they know will fail. Another
|
| - # reason might be to abandon a stuck build. We want to mark the
|
| - # build as failed quickly rather than waiting for the slave's
|
| - # timeout to kill it on its own.
|
| -
|
| - log.msg(" %s: stopping build: %s" % (self, reason))
|
| - if self.finished:
|
| - return
|
| - # TODO: include 'reason' in this point event
|
| - self.builder.builder_status.addPointEvent(['interrupt'])
|
| - self.currentStep.interrupt(reason)
|
| - if 0:
|
| - # TODO: maybe let its deferred do buildFinished
|
| - if self.currentStep and self.currentStep.progress:
|
| - # XXX: really .fail or something
|
| - self.currentStep.progress.finish()
|
| - text = ["stopped", reason]
|
| - self.buildFinished(text, FAILURE)
|
| -
|
| - def allStepsDone(self):
|
| - if self.result == FAILURE:
|
| - text = ["failed"]
|
| - elif self.result == WARNINGS:
|
| - text = ["warnings"]
|
| - elif self.result == EXCEPTION:
|
| - text = ["exception"]
|
| - else:
|
| - text = ["build", "successful"]
|
| - text.extend(self.text)
|
| - return self.buildFinished(text, self.result)
|
| -
|
| - def buildException(self, why):
|
| - log.msg("%s.buildException" % self)
|
| - log.err(why)
|
| - self.buildFinished(["build", "exception"], FAILURE)
|
| -
|
| - def buildFinished(self, text, results):
|
| - """This method must be called when the last Step has completed. It
|
| - marks the Build as complete and returns the Builder to the 'idle'
|
| - state.
|
| -
|
| - It takes two arguments which describe the overall build status:
|
| - text, results. 'results' is one of SUCCESS, WARNINGS, or FAILURE.
|
| -
|
| - If 'results' is SUCCESS or WARNINGS, we will permit any dependant
|
| - builds to start. If it is 'FAILURE', those builds will be
|
| - abandoned."""
|
| -
|
| - self.finished = True
|
| - if self.remote:
|
| - self.remote.dontNotifyOnDisconnect(self.lostRemote)
|
| - self.results = results
|
| -
|
| - log.msg(" %s: build finished" % self)
|
| - self.build_status.setText(text)
|
| - self.build_status.setResults(results)
|
| - self.build_status.buildFinished()
|
| - if self.progress and results == SUCCESS:
|
| - # XXX: also test a 'timing consistent' flag?
|
| - log.msg(" setting expectations for next time")
|
| - self.builder.setExpectations(self.progress)
|
| - reactor.callLater(0, self.releaseLocks)
|
| - self.deferred.callback(self)
|
| - self.deferred = None
|
| -
|
| - def releaseLocks(self):
|
| - log.msg("releaseLocks(%s): %s" % (self, self.locks))
|
| - for lock, access in self.locks:
|
| - lock.release(self, access)
|
| -
|
| - # IBuildControl
|
| -
|
| - def getStatus(self):
|
| - return self.build_status
|
| -
|
| - # stopBuild is defined earlier
|
| -
|
|
|