| Index: third_party/buildbot_7_12/buildbot/status/progress.py
|
| diff --git a/third_party/buildbot_7_12/buildbot/status/progress.py b/third_party/buildbot_7_12/buildbot/status/progress.py
|
| deleted file mode 100644
|
| index dc4d3d572066bcd6cddfa88c2d88e7ee82c186f9..0000000000000000000000000000000000000000
|
| --- a/third_party/buildbot_7_12/buildbot/status/progress.py
|
| +++ /dev/null
|
| @@ -1,308 +0,0 @@
|
| -# -*- test-case-name: buildbot.test.test_status -*-
|
| -
|
| -from twisted.internet import reactor
|
| -from twisted.spread import pb
|
| -from twisted.python import log
|
| -from buildbot import util
|
| -
|
| -class StepProgress:
|
| - """I keep track of how much progress a single BuildStep has made.
|
| -
|
| - Progress is measured along various axes. Time consumed is one that is
|
| - available for all steps. Amount of command output is another, and may be
|
| - better quantified by scanning the output for markers to derive number of
|
| - files compiled, directories walked, tests run, etc.
|
| -
|
| - I am created when the build begins, and given to a BuildProgress object
|
| - so it can track the overall progress of the whole build.
|
| -
|
| - """
|
| -
|
| - startTime = None
|
| - stopTime = None
|
| - expectedTime = None
|
| - buildProgress = None
|
| - debug = False
|
| -
|
| - def __init__(self, name, metricNames):
|
| - self.name = name
|
| - self.progress = {}
|
| - self.expectations = {}
|
| - for m in metricNames:
|
| - self.progress[m] = None
|
| - self.expectations[m] = None
|
| -
|
| - def setBuildProgress(self, bp):
|
| - self.buildProgress = bp
|
| -
|
| - def setExpectations(self, metrics):
|
| - """The step can call this to explicitly set a target value for one
|
| - of its metrics. E.g., ShellCommands knows how many commands it will
|
| - execute, so it could set the 'commands' expectation."""
|
| - for metric, value in metrics.items():
|
| - self.expectations[metric] = value
|
| - self.buildProgress.newExpectations()
|
| -
|
| - def setExpectedTime(self, seconds):
|
| - self.expectedTime = seconds
|
| - self.buildProgress.newExpectations()
|
| -
|
| - def start(self):
|
| - if self.debug: print "StepProgress.start[%s]" % self.name
|
| - self.startTime = util.now()
|
| -
|
| - def setProgress(self, metric, value):
|
| - """The step calls this as progress is made along various axes."""
|
| - if self.debug:
|
| - print "setProgress[%s][%s] = %s" % (self.name, metric, value)
|
| - self.progress[metric] = value
|
| - if self.debug:
|
| - r = self.remaining()
|
| - print " step remaining:", r
|
| - self.buildProgress.newProgress()
|
| -
|
| - def finish(self):
|
| - """This stops the 'time' metric and marks the step as finished
|
| - overall. It should be called after the last .setProgress has been
|
| - done for each axis."""
|
| - if self.debug: print "StepProgress.finish[%s]" % self.name
|
| - self.stopTime = util.now()
|
| - self.buildProgress.stepFinished(self.name)
|
| -
|
| - def totalTime(self):
|
| - if self.startTime != None and self.stopTime != None:
|
| - return self.stopTime - self.startTime
|
| -
|
| - def remaining(self):
|
| - if self.startTime == None:
|
| - return self.expectedTime
|
| - if self.stopTime != None:
|
| - return 0 # already finished
|
| - # TODO: replace this with cleverness that graphs each metric vs.
|
| - # time, then finds the inverse function. Will probably need to save
|
| - # a timestamp with each setProgress update, when finished, go back
|
| - # and find the 2% transition points, then save those 50 values in a
|
| - # list. On the next build, do linear interpolation between the two
|
| - # closest samples to come up with a percentage represented by that
|
| - # metric.
|
| -
|
| - # TODO: If no other metrics are available, just go with elapsed
|
| - # time. Given the non-time-uniformity of text output from most
|
| - # steps, this would probably be better than the text-percentage
|
| - # scheme currently implemented.
|
| -
|
| - percentages = []
|
| - for metric, value in self.progress.items():
|
| - expectation = self.expectations[metric]
|
| - if value != None and expectation != None:
|
| - p = 1.0 * value / expectation
|
| - percentages.append(p)
|
| - if percentages:
|
| - avg = reduce(lambda x,y: x+y, percentages) / len(percentages)
|
| - if avg > 1.0:
|
| - # overdue
|
| - avg = 1.0
|
| - if avg < 0.0:
|
| - avg = 0.0
|
| - if percentages and self.expectedTime != None:
|
| - return self.expectedTime - (avg * self.expectedTime)
|
| - if self.expectedTime is not None:
|
| - # fall back to pure time
|
| - return self.expectedTime - (util.now() - self.startTime)
|
| - return None # no idea
|
| -
|
| -
|
| -class WatcherState:
|
| - def __init__(self, interval):
|
| - self.interval = interval
|
| - self.timer = None
|
| - self.needUpdate = 0
|
| -
|
| -class BuildProgress(pb.Referenceable):
|
| - """I keep track of overall build progress. I hold a list of StepProgress
|
| - objects.
|
| - """
|
| -
|
| - def __init__(self, stepProgresses):
|
| - self.steps = {}
|
| - for s in stepProgresses:
|
| - self.steps[s.name] = s
|
| - s.setBuildProgress(self)
|
| - self.finishedSteps = []
|
| - self.watchers = {}
|
| - self.debug = 0
|
| -
|
| - def setExpectationsFrom(self, exp):
|
| - """Set our expectations from the builder's Expectations object."""
|
| - for name, metrics in exp.steps.items():
|
| - s = self.steps[name]
|
| - s.setExpectedTime(exp.times[name])
|
| - s.setExpectations(exp.steps[name])
|
| -
|
| - def newExpectations(self):
|
| - """Call this when one of the steps has changed its expectations.
|
| - This should trigger us to update our ETA value and notify any
|
| - subscribers."""
|
| - pass # subscribers are not implemented: they just poll
|
| -
|
| - def stepFinished(self, stepname):
|
| - assert(stepname not in self.finishedSteps)
|
| - self.finishedSteps.append(stepname)
|
| - if len(self.finishedSteps) == len(self.steps.keys()):
|
| - self.sendLastUpdates()
|
| -
|
| - def newProgress(self):
|
| - r = self.remaining()
|
| - if self.debug:
|
| - print " remaining:", r
|
| - if r != None:
|
| - self.sendAllUpdates()
|
| -
|
| - def remaining(self):
|
| - # sum eta of all steps
|
| - sum = 0
|
| - for name, step in self.steps.items():
|
| - rem = step.remaining()
|
| - if rem == None:
|
| - return None # not sure
|
| - sum += rem
|
| - return sum
|
| - def eta(self):
|
| - left = self.remaining()
|
| - if left == None:
|
| - return None # not sure
|
| - done = util.now() + left
|
| - return done
|
| -
|
| -
|
| - def remote_subscribe(self, remote, interval=5):
|
| - # [interval, timer, needUpdate]
|
| - # don't send an update more than once per interval
|
| - self.watchers[remote] = WatcherState(interval)
|
| - remote.notifyOnDisconnect(self.removeWatcher)
|
| - self.updateWatcher(remote)
|
| - self.startTimer(remote)
|
| - log.msg("BuildProgress.remote_subscribe(%s)" % remote)
|
| - def remote_unsubscribe(self, remote):
|
| - # TODO: this doesn't work. I think 'remote' will always be different
|
| - # than the object that appeared in _subscribe.
|
| - log.msg("BuildProgress.remote_unsubscribe(%s)" % remote)
|
| - self.removeWatcher(remote)
|
| - #remote.dontNotifyOnDisconnect(self.removeWatcher)
|
| - def removeWatcher(self, remote):
|
| - #log.msg("removeWatcher(%s)" % remote)
|
| - try:
|
| - timer = self.watchers[remote].timer
|
| - if timer:
|
| - timer.cancel()
|
| - del self.watchers[remote]
|
| - except KeyError:
|
| - log.msg("Weird, removeWatcher on non-existent subscriber:",
|
| - remote)
|
| - def sendAllUpdates(self):
|
| - for r in self.watchers.keys():
|
| - self.updateWatcher(r)
|
| - def updateWatcher(self, remote):
|
| - # an update wants to go to this watcher. Send it if we can, otherwise
|
| - # queue it for later
|
| - w = self.watchers[remote]
|
| - if not w.timer:
|
| - # no timer, so send update now and start the timer
|
| - self.sendUpdate(remote)
|
| - self.startTimer(remote)
|
| - else:
|
| - # timer is running, just mark as needing an update
|
| - w.needUpdate = 1
|
| - def startTimer(self, remote):
|
| - w = self.watchers[remote]
|
| - timer = reactor.callLater(w.interval, self.watcherTimeout, remote)
|
| - w.timer = timer
|
| - def sendUpdate(self, remote, last=0):
|
| - self.watchers[remote].needUpdate = 0
|
| - #text = self.asText() # TODO: not text, duh
|
| - try:
|
| - remote.callRemote("progress", self.remaining())
|
| - if last:
|
| - remote.callRemote("finished", self)
|
| - except:
|
| - log.deferr()
|
| - self.removeWatcher(remote)
|
| -
|
| - def watcherTimeout(self, remote):
|
| - w = self.watchers.get(remote, None)
|
| - if not w:
|
| - return # went away
|
| - w.timer = None
|
| - if w.needUpdate:
|
| - self.sendUpdate(remote)
|
| - self.startTimer(remote)
|
| - def sendLastUpdates(self):
|
| - for remote in self.watchers.keys():
|
| - self.sendUpdate(remote, 1)
|
| - self.removeWatcher(remote)
|
| -
|
| -
|
| -class Expectations:
|
| - debug = False
|
| - # decay=1.0 ignores all but the last build
|
| - # 0.9 is short time constant. 0.1 is very long time constant
|
| - # TODO: let decay be specified per-metric
|
| - decay = 0.5
|
| -
|
| - def __init__(self, buildprogress):
|
| - """Create us from a successful build. We will expect each step to
|
| - take as long as it did in that build."""
|
| -
|
| - # .steps maps stepname to dict2
|
| - # dict2 maps metricname to final end-of-step value
|
| - self.steps = {}
|
| -
|
| - # .times maps stepname to per-step elapsed time
|
| - self.times = {}
|
| -
|
| - for name, step in buildprogress.steps.items():
|
| - self.steps[name] = {}
|
| - for metric, value in step.progress.items():
|
| - self.steps[name][metric] = value
|
| - self.times[name] = None
|
| - if step.startTime is not None and step.stopTime is not None:
|
| - self.times[name] = step.stopTime - step.startTime
|
| -
|
| - def wavg(self, old, current):
|
| - if old is None:
|
| - return current
|
| - if current is None:
|
| - return old
|
| - else:
|
| - return (current * self.decay) + (old * (1 - self.decay))
|
| -
|
| - def update(self, buildprogress):
|
| - for name, stepprogress in buildprogress.steps.items():
|
| - old = self.times[name]
|
| - current = stepprogress.totalTime()
|
| - if current == None:
|
| - log.msg("Expectations.update: current[%s] was None!" % name)
|
| - continue
|
| - new = self.wavg(old, current)
|
| - self.times[name] = new
|
| - if self.debug:
|
| - print "new expected time[%s] = %s, old %s, cur %s" % \
|
| - (name, new, old, current)
|
| -
|
| - for metric, current in stepprogress.progress.items():
|
| - old = self.steps[name][metric]
|
| - new = self.wavg(old, current)
|
| - if self.debug:
|
| - print "new expectation[%s][%s] = %s, old %s, cur %s" % \
|
| - (name, metric, new, old, current)
|
| - self.steps[name][metric] = new
|
| -
|
| - def expectedBuildTime(self):
|
| - if None in self.times.values():
|
| - return None
|
| - #return sum(self.times.values())
|
| - # python-2.2 doesn't have 'sum'. TODO: drop python-2.2 support
|
| - s = 0
|
| - for v in self.times.values():
|
| - s += v
|
| - return s
|
|
|